@waniwani/sdk 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp/index.d.ts +6 -0
- package/dist/mcp/index.js +3 -3
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/react.d.ts +6 -0
- package/dist/mcp/react.js +7 -7
- package/dist/mcp/react.js.map +1 -1
- package/dist/mcp-apps-client-Z62VBB36.js +3 -0
- package/dist/mcp-apps-client-Z62VBB36.js.map +1 -0
- package/dist/openai-client-MVV4KJUO.js +3 -0
- package/dist/openai-client-MVV4KJUO.js.map +1 -0
- package/package.json +3 -3
- package/dist/mcp-apps-client-GILGJJZP.js +0 -3
- package/dist/mcp-apps-client-GILGJJZP.js.map +0 -1
- package/dist/openai-client-3M6MDERR.js +0 -3
- package/dist/openai-client-3M6MDERR.js.map +0 -1
package/dist/mcp/index.d.ts
CHANGED
|
@@ -156,6 +156,12 @@ interface UnifiedWidgetClient {
|
|
|
156
156
|
* On MCP Apps, this establishes the postMessage connection.
|
|
157
157
|
*/
|
|
158
158
|
connect(): Promise<void>;
|
|
159
|
+
/**
|
|
160
|
+
* Close the connection to the host and clean up resources.
|
|
161
|
+
* On OpenAI, this is a no-op.
|
|
162
|
+
* On MCP Apps, this closes the transport and removes event listeners.
|
|
163
|
+
*/
|
|
164
|
+
close(): Promise<void>;
|
|
159
165
|
/**
|
|
160
166
|
* Get the tool output (structured content returned by the tool handler).
|
|
161
167
|
* This is the main data source for widget rendering.
|
package/dist/mcp/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function M(){return typeof window<"u"&&"openai"in window?"openai":"mcp-apps"}function J(){return M()==="openai"}function z(){return M()==="mcp-apps"}var y="__start__",R="__end__",I=Symbol.for("waniwani.flow.interrupt"),F=Symbol.for("waniwani.flow.widget");function O(e){return{__type:I,...e}}function H(e,t){return{__type:F,resource:e,...t}}function _(e){return typeof e=="object"&&e!==null&&"__type"in e&&e.__type===I}function $(e){return typeof e=="object"&&e!==null&&"__type"in e&&e.__type===F}import{z as w}from"zod";var
|
|
2
|
-
`)}async function
|
|
3
|
-
${g}`,r;for(let n of s.values())if(n.resource){r=n.resource;break}let d=r?
|
|
1
|
+
function M(){return typeof window<"u"&&"openai"in window?"openai":"mcp-apps"}function J(){return M()==="openai"}function z(){return M()==="mcp-apps"}var y="__start__",R="__end__",I=Symbol.for("waniwani.flow.interrupt"),F=Symbol.for("waniwani.flow.widget");function O(e){return{__type:I,...e}}function H(e,t){return{__type:F,resource:e,...t}}function _(e){return typeof e=="object"&&e!==null&&"__type"in e&&e.__type===I}function $(e){return typeof e=="object"&&e!==null&&"__type"in e&&e.__type===F}import{z as w}from"zod";var v="text/html+skybridge",E="text/html;profile=mcp-app",A=async(e,t)=>{let o=e.endsWith("/")?e.slice(0,-1):e;return await(await fetch(`${o}${t}`)).text()};function W(e){return{"openai/widgetDescription":e.description,"openai/widgetPrefersBorder":e.prefersBorder,"openai/widgetDomain":e.widgetDomain,...e.widgetCSP&&{"openai/widgetCSP":e.widgetCSP}}}function D(e){let t=e.widgetCSP?{connectDomains:e.widgetCSP.connect_domains,resourceDomains:e.widgetCSP.resource_domains,frameDomains:e.widgetCSP.frame_domains,redirectDomains:e.widgetCSP.redirect_domains}:void 0;return{ui:{...t&&{csp:t},...e.prefersBorder!==void 0&&{prefersBorder:e.prefersBorder}}}}function C(e){return{"openai/outputTemplate":e.openaiTemplateUri,"openai/toolInvocation/invoking":e.invoking,"openai/toolInvocation/invoked":e.invoked,"openai/widgetAccessible":!0,"openai/resultCanProduceWidget":!0,ui:{resourceUri:e.mcpTemplateUri,...e.autoHeight&&{autoHeight:!0}}}}function B(e){let t=e.description??"",o=e._zod?.def;if(o?.type==="enum"&&o.entries){let s=Object.keys(o.entries).map(c=>`"${c}"`).join(" | ");return t?`${s} \u2014 ${t}`:s}return t}function L(e){let t=["","## FLOW EXECUTION PROTOCOL","","This tool implements a multi-step conversational flow. Follow this protocol exactly:","",'1. Call with `action: "start"` to begin. If the user\'s message already'," contains answers to likely questions, extract them into `initialState`"," as `{ field: value }` pairs. The engine will auto-skip questions whose"," fields are already filled."," Only extract values the user explicitly stated \u2014 do NOT guess or invent values."];if(e.fields){let o=Object.entries(e.fields).map(([s,c])=>{let g=B(c);return g?`\`${s}\` (${g})`:`\`${s}\``}).join(", ");t.push(` Known fields: ${o}.`)}return t.push("2. The response JSON `status` field tells you what to do next:",' - `"interrupt"`: Ask the user the `question`. If a `context` field is present,'," use it as hidden instructions to enrich your response (do NOT show it verbatim)."," Then call again with:",' `action: "continue"`, `step` = the returned `step`, `state` = the returned `state`,'," `answer` = the user's answer.",' - `"widget"`: A widget UI is being shown. The user will interact with the widget.'," When the user makes a choice and you need to continue the flow, call again with:",' `action: "widget_result"`, `step` = the returned `step`, `state` = the returned `state`,'," `answer` = the user's selection.",' - `"complete"`: The flow is done. Present the result to the user.',' - `"error"`: Something went wrong. Show the `error` message.',"","3. ALWAYS pass back the `state` object exactly as received.","4. Do NOT invent state values. Only use `initialState` for information the user explicitly provided."),t.join(`
|
|
2
|
+
`)}async function N(e,t){return e.type==="direct"?e.to:e.condition(t)}async function b(e,t,o,s,c,g,h){let r=e,d={...t},x=50,n=0;for(;n++<x;){if(r===R)return{text:JSON.stringify({status:"complete",state:d}),data:{status:"complete",state:d}};let l=o.get(r);if(!l)return{text:JSON.stringify({status:"error",error:`Unknown node: "${r}"`}),data:{status:"error"}};try{let a=await l(d,h);if(_(a)){let i=d[a.field];if(i!=null&&i!==""){let u=c.get(r);if(!u)return{text:JSON.stringify({status:"error",error:`No outgoing edge from node "${r}"`}),data:{status:"error"}};r=await N(u,d);continue}return{text:JSON.stringify({status:"interrupt",step:r,question:a.question,field:a.field,suggestions:a.suggestions,...a.context?{context:a.context}:{},state:d}),data:{status:"interrupt",step:r,state:d}}}if($(a)){let i=s.get(r)?.field;if(i){let m=d[i];if(m!=null&&m!==""){let f=c.get(r);if(!f)return{text:JSON.stringify({status:"error",error:`No outgoing edge from node "${r}"`}),data:{status:"error"}};r=await N(f,d);continue}}let u=a.resource;return{text:JSON.stringify({status:"widget",step:r,widgetId:u.id,description:a.description,state:d}),data:{...a.data,__flow:{flowId:g,step:r,state:d}},widgetMeta:C({openaiTemplateUri:u.openaiUri,mcpTemplateUri:u.mcpUri,invoking:"Loading...",invoked:"Loaded",autoHeight:u.autoHeight})}}d={...d,...a};let p=c.get(r);if(!p)return{text:JSON.stringify({status:"error",error:`No outgoing edge from node "${r}"`}),data:{status:"error"}};r=await N(p,d)}catch(a){let p=a instanceof Error?a.message:String(a);return{text:JSON.stringify({status:"error",step:r,error:p,state:d}),data:{status:"error",error:p}}}}return{text:JSON.stringify({status:"error",error:"Flow exceeded maximum iterations (possible infinite loop)"}),data:{status:"error"}}}var Y={action:w.enum(["start","continue","widget_result"]).describe('"start" to begin the flow, "continue" after the user answers a question, "widget_result" when a widget returns data'),step:w.string().optional().describe("Current step name (from the previous response)"),state:w.record(w.string(),w.unknown()).optional().describe("Flow state \u2014 pass back exactly as received"),answer:w.string().optional().describe("The user's answer (for interrupt steps)"),widgetResult:w.record(w.string(),w.unknown()).optional().describe("Data returned by a widget callback"),initialState:w.record(w.string(),w.unknown()).optional().describe(`Pre-filled answers extracted from the user's message (only for action: "start")`)};function U(e){let{config:t,nodes:o,nodeConfigs:s,edges:c}=e,g=L(t),h=`${t.description}
|
|
3
|
+
${g}`,r;for(let n of s.values())if(n.resource){r=n.resource;break}let d=r?C({openaiTemplateUri:r.openaiUri,mcpTemplateUri:r.mcpUri,invoking:"Loading...",invoked:"Loaded",autoHeight:r.autoHeight}):void 0;async function x(n,l){let a=n.state??{};if(n.action==="start"){let p=c.get(y);if(!p)return{text:JSON.stringify({status:"error",error:"No start edge"}),data:{status:"error"}};let i=n.initialState?{...a,...n.initialState}:a,u=await N(p,i);return b(u,i,o,s,c,t.id,l)}if(n.action==="continue"){if(!n.step)return{text:JSON.stringify({status:"error",error:'Missing "step" for continue action'}),data:{status:"error"}};let p={...a};if(n.answer){let m=o.get(n.step);if(m)try{let f=await m(p,l);_(f)&&f.field&&(p={...p,[f.field]:n.answer})}catch{}}let i=c.get(n.step);if(!i)return{text:JSON.stringify({status:"error",error:`No edge from step "${n.step}"`}),data:{status:"error"}};let u=await N(i,p);return b(u,p,o,s,c,t.id,l)}if(n.action==="widget_result"){if(!n.step)return{text:JSON.stringify({status:"error",error:'Missing "step" for widget_result action'}),data:{status:"error"}};let p=n.widgetResult??{};if(n.answer!==void 0&&Object.keys(p).length===0){let f=s.get(n.step)?.field;f&&(p={[f]:n.answer})}let i={...a,...p},u=c.get(n.step);if(!u)return{text:JSON.stringify({status:"error",error:`No edge from step "${n.step}"`}),data:{status:"error"}};let m=await N(u,i);return b(m,i,o,s,c,t.id,l)}return{text:JSON.stringify({status:"error",error:`Unknown action: "${n.action}"`}),data:{status:"error"}}}return{id:t.id,title:t.title,description:h,async register(n){n.registerTool(t.id,{title:t.title,description:h,inputSchema:Y,annotations:t.annotations,...d&&{_meta:d}},(async(l,a)=>{let i=a._meta??{},u=await x(l,i);return u.widgetMeta?{content:[{type:"text",text:u.text}],structuredContent:u.data,_meta:{...u.widgetMeta,...i}}:{content:[{type:"text",text:u.text}]}}))}}}var k=class{nodes=new Map;nodeConfigs=new Map;edges=new Map;config;constructor(t){this.config=t}addNode(t,o,s){if(t===y||t===R)throw new Error(`"${t}" is a reserved name and cannot be used as a node name`);if(this.nodes.has(t))throw new Error(`Node "${t}" already exists`);let c,g={};if(typeof o=="function")c=o;else{if(!s)throw new Error(`addNode("${t}", config, handler) requires a handler as the third argument`);c=s,g=o}return this.nodes.set(t,c),this.nodeConfigs.set(t,g),this}addEdge(t,o){if(this.edges.has(t))throw new Error(`Node "${t}" already has an outgoing edge. Use addConditionalEdge for branching.`);return this.edges.set(t,{type:"direct",to:o}),this}addConditionalEdge(t,o){if(this.edges.has(t))throw new Error(`Node "${t}" already has an outgoing edge.`);return this.edges.set(t,{type:"conditional",condition:o}),this}compile(){return this.validate(),U({config:this.config,nodes:new Map(this.nodes),nodeConfigs:new Map(this.nodeConfigs),edges:new Map(this.edges)})}validate(){if(!this.edges.has(y))throw new Error('Flow must have an entry point. Add an edge from START: .addEdge(START, "first_node")');let t=this.edges.get(y);if(t?.type==="direct"&&t.to!==R&&!this.nodes.has(t.to))throw new Error(`START edge references non-existent node: "${t.to}"`);for(let[o,s]of this.edges){if(o!==y&&!this.nodes.has(o))throw new Error(`Edge from non-existent node: "${o}"`);if(s.type==="direct"&&s.to!==R&&!this.nodes.has(s.to))throw new Error(`Edge from "${o}" references non-existent node: "${s.to}"`)}for(let[o]of this.nodes)if(!this.edges.has(o))throw new Error(`Node "${o}" has no outgoing edge. Add one with .addEdge("${o}", ...) or .addConditionalEdge("${o}", ...)`)}};function q(e){return new k(e)}function j(e){let{id:t,title:o,description:s,baseUrl:c,htmlPath:g,widgetDomain:h,prefersBorder:r=!0,autoHeight:d,widgetCSP:x}=e,n=`ui://widgets/apps-sdk/${t}.html`,l=`ui://widgets/ext-apps/${t}.html`,a=null,p=()=>(a||(a=A(c,g)),a),i=s;async function u(m){let f=await p();m.registerResource(`${t}-openai-widget`,n,{title:o,description:i,mimeType:v,_meta:{"openai/widgetDescription":i,"openai/widgetPrefersBorder":r}},async P=>({contents:[{uri:P.href,mimeType:v,text:f,_meta:W({description:i,prefersBorder:r,widgetDomain:h,widgetCSP:x})}]})),m.registerResource(`${t}-mcp-widget`,l,{title:o,description:i,mimeType:E,_meta:{ui:{prefersBorder:r}}},async P=>({contents:[{uri:P.href,mimeType:E,text:f,_meta:D({description:i,prefersBorder:r,widgetCSP:x})}]}))}return{id:t,title:o,description:s,openaiUri:n,mcpUri:l,autoHeight:d,register:u}}function G(e,t){let{resource:o,description:s,inputSchema:c,annotations:g}=e,h=e.id??o?.id,r=e.title??o?.title;if(!h)throw new Error("createTool: `id` is required when no resource is provided");if(!r)throw new Error("createTool: `title` is required when no resource is provided");let d=o?C({openaiTemplateUri:o.openaiUri,mcpTemplateUri:o.mcpUri,invoking:e.invoking??"Loading...",invoked:e.invoked??"Loaded",autoHeight:o.autoHeight}):void 0;return{id:h,title:r,description:s,async register(x){x.registerTool(h,{title:r,description:s,inputSchema:c,annotations:g,...d&&{_meta:d}},(async(n,l)=>{let p=l._meta??{},i=await t(n,{extra:{_meta:p}});return o&&i.data?{content:[{type:"text",text:i.text}],structuredContent:i.data,_meta:{...d,...p}}:{content:[{type:"text",text:i.text}]}}))}}}async function Z(e,t){await Promise.all(t.map(o=>o.register(e)))}export{R as END,y as START,k as StateGraph,q as createFlow,j as createResource,G as createTool,M as detectPlatform,O as interrupt,z as isMCPApps,J as isOpenAI,Z as registerTools,H as showWidget};
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/mcp/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/mcp/react/widgets/platform.ts","../../src/mcp/server/flows/@types.ts","../../src/mcp/server/flows/compile.ts","../../src/mcp/server/resources/meta.ts","../../src/mcp/server/flows/state-graph.ts","../../src/mcp/server/flows/create-flow.ts","../../src/mcp/server/resources/create-resource.ts","../../src/mcp/server/tools/create-tool.ts"],"sourcesContent":["/**\n * Widget platform types\n */\nexport type WidgetPlatform = \"openai\" | \"mcp-apps\";\n\n/**\n * Detects which platform the widget is running on.\n *\n * OpenAI injects a global `window.openai` object.\n * MCP Apps runs in a sandboxed iframe and uses postMessage.\n */\nexport function detectPlatform(): WidgetPlatform {\n\tif (typeof window !== \"undefined\" && \"openai\" in window) {\n\t\treturn \"openai\";\n\t}\n\treturn \"mcp-apps\";\n}\n\n/**\n * Check if running on OpenAI platform\n */\nexport function isOpenAI(): boolean {\n\treturn detectPlatform() === \"openai\";\n}\n\n/**\n * Check if running on MCP Apps platform\n */\nexport function isMCPApps(): boolean {\n\treturn detectPlatform() === \"mcp-apps\";\n}\n","import type { z } from \"zod\";\nimport type { McpServer, RegisteredResource } from \"../resources/types\";\n\nexport type { McpServer };\n\n// ============================================================================\n// Sentinel constants\n// ============================================================================\n\nexport const START = \"__start__\" as const;\nexport const END = \"__end__\" as const;\n\n// ============================================================================\n// Signal types — returned by node handlers to control flow behavior\n// ============================================================================\n\nconst INTERRUPT = Symbol.for(\"waniwani.flow.interrupt\");\nconst WIDGET = Symbol.for(\"waniwani.flow.widget\");\n\nexport type InterruptSignal = {\n\treadonly __type: typeof INTERRUPT;\n\t/** Question to ask the user */\n\tquestion: string;\n\t/** State key where the answer will be stored */\n\tfield: string;\n\t/** Optional suggestions to present as options */\n\tsuggestions?: string[];\n\t/** Hidden context/instructions for the assistant (not shown to user directly) */\n\tcontext?: string;\n};\n\nexport type WidgetSignal = {\n\treadonly __type: typeof WIDGET;\n\t/** The resource to display */\n\tresource: RegisteredResource;\n\t/** Data to pass to the widget as structuredContent */\n\tdata: Record<string, unknown>;\n\t/** Description of what the widget does (for the AI's context) */\n\tdescription?: string;\n};\n\n/**\n * Create an interrupt signal — pauses the flow and asks the user a text question.\n */\nexport function interrupt(config: {\n\tquestion: string;\n\tfield: string;\n\tsuggestions?: string[];\n\tcontext?: string;\n}): InterruptSignal {\n\treturn { __type: INTERRUPT, ...config };\n}\n\n/**\n * Create a widget signal — pauses the flow and renders a widget UI.\n */\nexport function showWidget(\n\tresource: RegisteredResource,\n\tconfig: {\n\t\tdata: Record<string, unknown>;\n\t\tdescription?: string;\n\t},\n): WidgetSignal {\n\treturn { __type: WIDGET, resource, ...config };\n}\n\nexport function isInterrupt(value: unknown): value is InterruptSignal {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"__type\" in value &&\n\t\t(value as InterruptSignal).__type === INTERRUPT\n\t);\n}\n\nexport function isWidget(value: unknown): value is WidgetSignal {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"__type\" in value &&\n\t\t(value as WidgetSignal).__type === WIDGET\n\t);\n}\n\n// ============================================================================\n// Node & edge definitions\n// ============================================================================\n\nexport type MaybePromise<T> = T | Promise<T>;\n\n/** Configuration for a flow node */\nexport type NodeConfig<\n\tTState extends Record<string, unknown> = Record<string, unknown>,\n> = {\n\t/** Resource to display when this node returns a WidgetSignal */\n\tresource?: RegisteredResource;\n\t/** State key this node fills — enables auto-skip when the field is already in state via `initialState` */\n\tfield?: Extract<keyof TState, string>;\n};\n\n/**\n * Node handler — a single function type for all node kinds.\n * The return value determines behavior:\n * - `Partial<TState>` → action node (state merged, auto-advance)\n * - `InterruptSignal` → interrupt (pause, ask user)\n * - `WidgetSignal` → widget step (pause, show widget)\n */\nexport type NodeHandler<TState> = (\n\tstate: Partial<TState>,\n\tmeta?: Record<string, unknown>,\n) => MaybePromise<Partial<TState> | InterruptSignal | WidgetSignal>;\n\n/**\n * Condition function for conditional edges.\n * Receives current state, returns the name of the next node.\n */\nexport type ConditionFn<TState> = (\n\tstate: Partial<TState>,\n) => string | Promise<string>;\n\nexport type Edge<TState> =\n\t| { type: \"direct\"; to: string }\n\t| { type: \"conditional\"; condition: ConditionFn<TState> };\n\n// ============================================================================\n// Flow config & compiled output\n// ============================================================================\n\nexport type FlowConfig = {\n\t/** Unique identifier for the flow (becomes the MCP tool name) */\n\tid: string;\n\t/** Display title */\n\ttitle: string;\n\t/** Description for the AI (explains when to use this flow) */\n\tdescription: string;\n\t/**\n\t * Describe the flow's state fields so the AI can pre-fill answers\n\t * from the user's message via `initialState`.\n\t * Keys are the field names used in `interrupt({ field })`,\n\t * values are Zod schemas with `.describe()`.\n\t *\n\t * @example\n\t * ```ts\n\t * fields: {\n\t * country: z.string().describe(\"Country the business is based in\"),\n\t * status: z.enum([\"registered\", \"unregistered\"]).describe(\"Business registration status\"),\n\t * }\n\t * ```\n\t */\n\tfields?: Record<string, z.ZodType>;\n\t/** Optional tool annotations */\n\tannotations?: {\n\t\treadOnlyHint?: boolean;\n\t\tidempotentHint?: boolean;\n\t\topenWorldHint?: boolean;\n\t\tdestructiveHint?: boolean;\n\t};\n};\n\n/**\n * A compiled flow — can be registered on an McpServer.\n */\nexport type RegisteredFlow = {\n\tid: string;\n\ttitle: string;\n\tdescription: string;\n\tregister: (server: McpServer) => Promise<void>;\n};\n","import type { ToolCallback } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n\tServerNotification,\n\tServerRequest,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport { buildToolMeta } from \"../resources/meta\";\nimport type { RegisteredResource } from \"../resources/types\";\nimport type {\n\tEdge,\n\tFlowConfig,\n\tMcpServer,\n\tNodeConfig,\n\tNodeHandler,\n\tRegisteredFlow,\n} from \"./@types\";\nimport { END, isInterrupt, isWidget, START } from \"./@types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface CompileInput<TState extends Record<string, unknown>> {\n\tconfig: FlowConfig;\n\tnodes: Map<string, NodeHandler<TState>>;\n\tnodeConfigs: Map<string, NodeConfig<TState>>;\n\tedges: Map<string, Edge<TState>>;\n}\n\ntype FlowToolInput = {\n\taction: \"start\" | \"continue\" | \"widget_result\";\n\tstep?: string;\n\tstate?: Record<string, unknown>;\n\tanswer?: string;\n\twidgetResult?: Record<string, unknown>;\n\tinitialState?: Record<string, unknown>;\n};\n\ntype ExecutionResult = {\n\ttext: string;\n\tdata: Record<string, unknown>;\n\twidgetMeta?: Record<string, unknown>;\n};\n\n// ============================================================================\n// Flow protocol — embedded in tool description\n// ============================================================================\n\n/** Extract a human-readable label from a Zod schema for the AI protocol */\nfunction describeZodField(schema: z.ZodType): string {\n\tconst desc = schema.description ?? \"\";\n\tconst def = (\n\t\tschema as unknown as {\n\t\t\t_zod: { def: { type: string; entries?: Record<string, string> } };\n\t\t}\n\t)._zod?.def;\n\n\tif (def?.type === \"enum\" && def.entries) {\n\t\tconst vals = Object.keys(def.entries)\n\t\t\t.map((v) => `\"${v}\"`)\n\t\t\t.join(\" | \");\n\t\treturn desc ? `${vals} — ${desc}` : vals;\n\t}\n\n\treturn desc;\n}\n\nfunction buildFlowProtocol(config: FlowConfig): string {\n\tconst lines = [\n\t\t\"\",\n\t\t\"## FLOW EXECUTION PROTOCOL\",\n\t\t\"\",\n\t\t\"This tool implements a multi-step conversational flow. Follow this protocol exactly:\",\n\t\t\"\",\n\t\t'1. Call with `action: \"start\"` to begin. If the user\\'s message already',\n\t\t\" contains answers to likely questions, extract them into `initialState`\",\n\t\t\" as `{ field: value }` pairs. The engine will auto-skip questions whose\",\n\t\t\" fields are already filled.\",\n\t\t\" Only extract values the user explicitly stated — do NOT guess or invent values.\",\n\t];\n\n\tif (config.fields) {\n\t\tconst fieldList = Object.entries(config.fields)\n\t\t\t.map(([key, schema]) => {\n\t\t\t\tconst info = describeZodField(schema);\n\t\t\t\treturn info ? `\\`${key}\\` (${info})` : `\\`${key}\\``;\n\t\t\t})\n\t\t\t.join(\", \");\n\t\tlines.push(` Known fields: ${fieldList}.`);\n\t}\n\n\tlines.push(\n\t\t\"2. The response JSON `status` field tells you what to do next:\",\n\t\t' - `\"interrupt\"`: Ask the user the `question`. If a `context` field is present,',\n\t\t\" use it as hidden instructions to enrich your response (do NOT show it verbatim).\",\n\t\t\" Then call again with:\",\n\t\t' `action: \"continue\"`, `step` = the returned `step`, `state` = the returned `state`,',\n\t\t\" `answer` = the user's answer.\",\n\t\t' - `\"widget\"`: A widget UI is being shown. The user will interact with the widget.',\n\t\t\" When the user makes a choice and you need to continue the flow, call again with:\",\n\t\t' `action: \"widget_result\"`, `step` = the returned `step`, `state` = the returned `state`,',\n\t\t\" `answer` = the user's selection.\",\n\t\t' - `\"complete\"`: The flow is done. Present the result to the user.',\n\t\t' - `\"error\"`: Something went wrong. Show the `error` message.',\n\t\t\"\",\n\t\t\"3. ALWAYS pass back the `state` object exactly as received.\",\n\t\t\"4. Do NOT invent state values. Only use `initialState` for information the user explicitly provided.\",\n\t);\n\n\treturn lines.join(\"\\n\");\n}\n\n// ============================================================================\n// Edge resolution\n// ============================================================================\n\nasync function resolveNextNode<TState extends Record<string, unknown>>(\n\tedge: Edge<TState>,\n\tstate: Partial<TState>,\n): Promise<string> {\n\tif (edge.type === \"direct\") return edge.to;\n\treturn edge.condition(state);\n}\n\n// ============================================================================\n// Execution engine\n// ============================================================================\n\nasync function executeFrom<TState extends Record<string, unknown>>(\n\tstartNodeName: string,\n\tinitialState: TState,\n\tnodes: Map<string, NodeHandler<TState>>,\n\tnodeConfigs: Map<string, NodeConfig<TState>>,\n\tedges: Map<string, Edge<TState>>,\n\tflowId: string,\n\tmeta?: Record<string, unknown>,\n): Promise<ExecutionResult> {\n\tlet currentNode = startNodeName;\n\tlet state = { ...initialState };\n\n\t// Safety limit to prevent infinite loops\n\tconst MAX_ITERATIONS = 50;\n\tlet iterations = 0;\n\n\twhile (iterations++ < MAX_ITERATIONS) {\n\t\t// Reached END\n\t\tif (currentNode === END) {\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\tstate,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"complete\", state },\n\t\t\t};\n\t\t}\n\n\t\tconst handler = nodes.get(currentNode);\n\t\tif (!handler) {\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\terror: `Unknown node: \"${currentNode}\"`,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"error\" },\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await handler(state, meta);\n\n\t\t\t// Interrupt signal — pause and ask the user\n\t\t\tif (isInterrupt(result)) {\n\t\t\t\t// Auto-skip: if the field already has a value in state, advance\n\t\t\t\tconst existingValue = state[result.field as keyof TState];\n\t\t\t\tif (\n\t\t\t\t\texistingValue !== undefined &&\n\t\t\t\t\texistingValue !== null &&\n\t\t\t\t\texistingValue !== \"\"\n\t\t\t\t) {\n\t\t\t\t\tconst edge = edges.get(currentNode);\n\t\t\t\t\tif (!edge) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\t\t\terror: `No outgoing edge from node \"${currentNode}\"`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tcurrentNode = await resolveNextNode(edge, state);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"interrupt\",\n\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\tquestion: result.question,\n\t\t\t\t\t\tfield: result.field,\n\t\t\t\t\t\tsuggestions: result.suggestions,\n\t\t\t\t\t\t...(result.context ? { context: result.context } : {}),\n\t\t\t\t\t\tstate,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"interrupt\", step: currentNode, state },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Widget signal — pause and show widget\n\t\t\tif (isWidget(result)) {\n\t\t\t\t// Auto-skip: if the node config declares a field and it's already filled, advance\n\t\t\t\tconst nodeField = nodeConfigs.get(currentNode)?.field;\n\t\t\t\tif (nodeField) {\n\t\t\t\t\tconst existingValue = state[nodeField as keyof TState];\n\t\t\t\t\tif (\n\t\t\t\t\t\texistingValue !== undefined &&\n\t\t\t\t\t\texistingValue !== null &&\n\t\t\t\t\t\texistingValue !== \"\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst edge = edges.get(currentNode);\n\t\t\t\t\t\tif (!edge) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\t\t\t\terror: `No outgoing edge from node \"${currentNode}\"`,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcurrentNode = await resolveNextNode(edge, state);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst resource = result.resource;\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"widget\",\n\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\twidgetId: resource.id,\n\t\t\t\t\t\tdescription: result.description,\n\t\t\t\t\t\tstate,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: {\n\t\t\t\t\t\t...result.data,\n\t\t\t\t\t\t__flow: {\n\t\t\t\t\t\t\tflowId,\n\t\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\twidgetMeta: {\n\t\t\t\t\t\t\"openai/outputTemplate\": resource.openaiUri,\n\t\t\t\t\t\t\"openai/widgetAccessible\": true,\n\t\t\t\t\t\t\"openai/resultCanProduceWidget\": true,\n\t\t\t\t\t\tui: {\n\t\t\t\t\t\t\tresourceUri: resource.mcpUri,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Action node — merge state and auto-advance\n\t\t\tstate = { ...state, ...result } as TState;\n\n\t\t\tconst edge = edges.get(currentNode);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No outgoing edge from node \"${currentNode}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tcurrentNode = await resolveNextNode(edge, state);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\tstep: currentNode,\n\t\t\t\t\terror: message,\n\t\t\t\t\tstate,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"error\", error: message },\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {\n\t\ttext: JSON.stringify({\n\t\t\tstatus: \"error\",\n\t\t\terror: \"Flow exceeded maximum iterations (possible infinite loop)\",\n\t\t}),\n\t\tdata: { status: \"error\" },\n\t};\n}\n\n// ============================================================================\n// Compile\n// ============================================================================\n\nconst inputSchema = {\n\taction: z\n\t\t.enum([\"start\", \"continue\", \"widget_result\"])\n\t\t.describe(\n\t\t\t'\"start\" to begin the flow, \"continue\" after the user answers a question, \"widget_result\" when a widget returns data',\n\t\t),\n\tstep: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe(\"Current step name (from the previous response)\"),\n\tstate: z\n\t\t.record(z.string(), z.unknown())\n\t\t.optional()\n\t\t.describe(\"Flow state — pass back exactly as received\"),\n\tanswer: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe(\"The user's answer (for interrupt steps)\"),\n\twidgetResult: z\n\t\t.record(z.string(), z.unknown())\n\t\t.optional()\n\t\t.describe(\"Data returned by a widget callback\"),\n\tinitialState: z\n\t\t.record(z.string(), z.unknown())\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Pre-filled answers extracted from the user\\'s message (only for action: \"start\")',\n\t\t),\n};\n\nexport function compileFlow<TState extends Record<string, unknown>>(\n\tinput: CompileInput<TState>,\n): RegisteredFlow {\n\tconst { config, nodes, nodeConfigs, edges } = input;\n\tconst protocol = buildFlowProtocol(config);\n\tconst fullDescription = `${config.description}\\n${protocol}`;\n\n\t// Find the first resource from node configs to build tool-level widget metadata.\n\t// This tells OpenAI/Claude that this tool can produce widget UIs.\n\tlet firstResource: RegisteredResource | undefined;\n\tfor (const nc of nodeConfigs.values()) {\n\t\tif (nc.resource) {\n\t\t\tfirstResource = nc.resource;\n\t\t\tbreak;\n\t\t}\n\t}\n\tconst toolMeta = firstResource\n\t\t? buildToolMeta({\n\t\t\t\topenaiTemplateUri: firstResource.openaiUri,\n\t\t\t\tmcpTemplateUri: firstResource.mcpUri,\n\t\t\t\tinvoking: \"Loading...\",\n\t\t\t\tinvoked: \"Loaded\",\n\t\t\t\tautoHeight: firstResource.autoHeight,\n\t\t\t})\n\t\t: undefined;\n\n\tasync function handleToolCall(\n\t\targs: FlowToolInput,\n\t\tmeta?: Record<string, unknown>,\n\t): Promise<ExecutionResult> {\n\t\tconst state = (args.state ?? {}) as TState;\n\n\t\tif (args.action === \"start\") {\n\t\t\tconst startEdge = edges.get(START);\n\t\t\tif (!startEdge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: \"No start edge\",\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Merge pre-filled answers from the user's initial message\n\t\t\tconst startState = (\n\t\t\t\targs.initialState ? { ...state, ...args.initialState } : state\n\t\t\t) as TState;\n\n\t\t\tconst firstNode = await resolveNextNode(startEdge, startState);\n\t\t\treturn executeFrom(\n\t\t\t\tfirstNode,\n\t\t\t\tstartState,\n\t\t\t\tnodes,\n\t\t\t\tnodeConfigs,\n\t\t\t\tedges,\n\t\t\t\tconfig.id,\n\t\t\t\tmeta,\n\t\t\t);\n\t\t}\n\n\t\tif (args.action === \"continue\") {\n\t\t\tif (!args.step) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: 'Missing \"step\" for continue action',\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Apply user's answer to state using the field from the interrupt\n\t\t\tlet updatedState = { ...state };\n\t\t\tif (args.answer) {\n\t\t\t\tconst handler = nodes.get(args.step);\n\t\t\t\tif (handler) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await handler(updatedState, meta);\n\t\t\t\t\t\tif (isInterrupt(result) && result.field) {\n\t\t\t\t\t\t\tupdatedState = {\n\t\t\t\t\t\t\t\t...updatedState,\n\t\t\t\t\t\t\t\t[result.field]: args.answer,\n\t\t\t\t\t\t\t} as TState;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// If re-running the handler fails, still proceed with the answer\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Advance to next node\n\t\t\tconst edge = edges.get(args.step);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No edge from step \"${args.step}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst nextNode = await resolveNextNode(edge, updatedState);\n\t\t\treturn executeFrom(\n\t\t\t\tnextNode,\n\t\t\t\tupdatedState,\n\t\t\t\tnodes,\n\t\t\t\tnodeConfigs,\n\t\t\t\tedges,\n\t\t\t\tconfig.id,\n\t\t\t\tmeta,\n\t\t\t);\n\t\t}\n\n\t\tif (args.action === \"widget_result\") {\n\t\t\tif (!args.step) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: 'Missing \"step\" for widget_result action',\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Merge widget result into state.\n\t\t\t// If `answer` is provided and the node declares a `field`, auto-map it.\n\t\t\tlet widgetUpdate: Record<string, unknown> = args.widgetResult ?? {};\n\t\t\tif (args.answer !== undefined && Object.keys(widgetUpdate).length === 0) {\n\t\t\t\tconst nodeField = nodeConfigs.get(args.step)?.field;\n\t\t\t\tif (nodeField) {\n\t\t\t\t\twidgetUpdate = { [nodeField]: args.answer };\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst updatedState = {\n\t\t\t\t...state,\n\t\t\t\t...widgetUpdate,\n\t\t\t} as TState;\n\n\t\t\t// Advance to next node\n\t\t\tconst edge = edges.get(args.step);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No edge from step \"${args.step}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst nextNode = await resolveNextNode(edge, updatedState);\n\t\t\treturn executeFrom(\n\t\t\t\tnextNode,\n\t\t\t\tupdatedState,\n\t\t\t\tnodes,\n\t\t\t\tnodeConfigs,\n\t\t\t\tedges,\n\t\t\t\tconfig.id,\n\t\t\t\tmeta,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\ttext: JSON.stringify({\n\t\t\t\tstatus: \"error\",\n\t\t\t\terror: `Unknown action: \"${args.action}\"`,\n\t\t\t}),\n\t\t\tdata: { status: \"error\" },\n\t\t};\n\t}\n\n\treturn {\n\t\tid: config.id,\n\t\ttitle: config.title,\n\t\tdescription: fullDescription,\n\n\t\tasync register(server: McpServer): Promise<void> {\n\t\t\tserver.registerTool(\n\t\t\t\tconfig.id,\n\t\t\t\t{\n\t\t\t\t\ttitle: config.title,\n\t\t\t\t\tdescription: fullDescription,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tannotations: config.annotations,\n\t\t\t\t\t...(toolMeta && { _meta: toolMeta }),\n\t\t\t\t},\n\t\t\t\t(async (args: FlowToolInput, extra: unknown) => {\n\t\t\t\t\tconst requestExtra = extra as RequestHandlerExtra<\n\t\t\t\t\t\tServerRequest,\n\t\t\t\t\t\tServerNotification\n\t\t\t\t\t>;\n\t\t\t\t\tconst _meta: Record<string, unknown> = requestExtra._meta ?? {};\n\n\t\t\t\t\tconst result = await handleToolCall(args, _meta);\n\n\t\t\t\t\t// Widget response — include structuredContent + widget metadata\n\t\t\t\t\tif (result.widgetMeta) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: result.text }],\n\t\t\t\t\t\t\tstructuredContent: result.data,\n\t\t\t\t\t\t\t_meta: {\n\t\t\t\t\t\t\t\t...result.widgetMeta,\n\t\t\t\t\t\t\t\t..._meta,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Non-widget response (interrupt, complete, error) — text only\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: result.text }],\n\t\t\t\t\t};\n\t\t\t\t}) as unknown as ToolCallback<typeof inputSchema>,\n\t\t\t);\n\t\t},\n\t};\n}\n","import type { WidgetCSP } from \"./types\";\n\n/**\n * MIME types for widget resources.\n * OpenAI Apps SDK uses \"text/html+skybridge\"\n * MCP Apps uses \"text/html;profile=mcp-app\"\n */\nexport const MIME_TYPE_OPENAI = \"text/html+skybridge\";\nexport const MIME_TYPE_MCP = \"text/html;profile=mcp-app\";\n\n// ---- HTML fetching ----\n\nexport const fetchHtml = async (\n\tbaseUrl: string,\n\tpath: string,\n): Promise<string> => {\n\tconst normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\tconst result = await fetch(`${normalizedBase}${path}`);\n\treturn await result.text();\n};\n\n// ---- OpenAI resource metadata ----\n\ninterface OpenAIResourceMeta {\n\t[key: string]: unknown;\n\t\"openai/widgetDescription\"?: string;\n\t\"openai/widgetPrefersBorder\"?: boolean;\n\t\"openai/widgetDomain\"?: string;\n\t\"openai/widgetCSP\"?: WidgetCSP;\n}\n\nexport function buildOpenAIResourceMeta(config: {\n\tdescription?: string;\n\tprefersBorder?: boolean;\n\twidgetDomain: string;\n\twidgetCSP?: WidgetCSP;\n}): OpenAIResourceMeta {\n\treturn {\n\t\t\"openai/widgetDescription\": config.description,\n\t\t\"openai/widgetPrefersBorder\": config.prefersBorder,\n\t\t\"openai/widgetDomain\": config.widgetDomain,\n\t\t...(config.widgetCSP && { \"openai/widgetCSP\": config.widgetCSP }),\n\t};\n}\n\n// ---- MCP Apps resource metadata ----\n\ninterface McpAppsResourceMeta {\n\t[key: string]: unknown;\n\tui?: {\n\t\tcsp?: {\n\t\t\tconnectDomains?: string[];\n\t\t\tresourceDomains?: string[];\n\t\t\tframeDomains?: string[];\n\t\t\tredirectDomains?: string[];\n\t\t};\n\t\tdomain?: string;\n\t\tprefersBorder?: boolean;\n\t};\n}\n\nexport function buildMcpAppsResourceMeta(config: {\n\tdescription?: string;\n\tprefersBorder?: boolean;\n\twidgetCSP?: WidgetCSP;\n}): McpAppsResourceMeta {\n\tconst csp = config.widgetCSP\n\t\t? {\n\t\t\t\tconnectDomains: config.widgetCSP.connect_domains,\n\t\t\t\tresourceDomains: config.widgetCSP.resource_domains,\n\t\t\t\tframeDomains: config.widgetCSP.frame_domains,\n\t\t\t\tredirectDomains: config.widgetCSP.redirect_domains,\n\t\t\t}\n\t\t: undefined;\n\n\treturn {\n\t\tui: {\n\t\t\t...(csp && { csp }),\n\t\t\t...(config.prefersBorder !== undefined && {\n\t\t\t\tprefersBorder: config.prefersBorder,\n\t\t\t}),\n\t\t},\n\t};\n}\n\n// ---- Tool metadata (references resource URIs) ----\n\nexport function buildToolMeta(config: {\n\topenaiTemplateUri: string;\n\tmcpTemplateUri: string;\n\tinvoking: string;\n\tinvoked: string;\n\tautoHeight?: boolean;\n}) {\n\treturn {\n\t\t// OpenAI metadata\n\t\t\"openai/outputTemplate\": config.openaiTemplateUri,\n\t\t\"openai/toolInvocation/invoking\": config.invoking,\n\t\t\"openai/toolInvocation/invoked\": config.invoked,\n\t\t\"openai/widgetAccessible\": true,\n\t\t\"openai/resultCanProduceWidget\": true,\n\t\t// MCP Apps metadata\n\t\tui: {\n\t\t\tresourceUri: config.mcpTemplateUri,\n\t\t\t...(config.autoHeight && { autoHeight: true }),\n\t\t},\n\t} as const;\n}\n","import type {\n\tConditionFn,\n\tEdge,\n\tFlowConfig,\n\tNodeConfig,\n\tNodeHandler,\n\tRegisteredFlow,\n} from \"./@types\";\nimport { END, START } from \"./@types\";\nimport { compileFlow } from \"./compile\";\n\n/**\n * A LangGraph-inspired state graph builder for MCP tools.\n *\n * @example\n * ```ts\n * const flow = new StateGraph<MyState>({\n * id: \"onboarding\",\n * title: \"User Onboarding\",\n * description: \"Guides users through onboarding\",\n * })\n * .addNode(\"ask_name\", () => interrupt({ question: \"What's your name?\", field: \"name\" }))\n * .addNode(\"greet\", (state) => ({ greeting: `Hello ${state.name}!` }))\n * .addEdge(START, \"ask_name\")\n * .addEdge(\"ask_name\", \"greet\")\n * .addEdge(\"greet\", END)\n * .compile();\n * ```\n */\nexport class StateGraph<TState extends Record<string, unknown>> {\n\tprivate nodes = new Map<string, NodeHandler<TState>>();\n\tprivate nodeConfigs = new Map<string, NodeConfig<TState>>();\n\tprivate edges = new Map<string, Edge<TState>>();\n\tprivate config: FlowConfig;\n\n\tconstructor(config: FlowConfig) {\n\t\tthis.config = config;\n\t}\n\n\t/**\n\t * Add a node with just a handler.\n\t */\n\taddNode(name: string, handler: NodeHandler<TState>): this;\n\t/**\n\t * Add a node with config (e.g., resource, field) and a handler.\n\t */\n\taddNode(\n\t\tname: string,\n\t\tconfig: NodeConfig<TState>,\n\t\thandler: NodeHandler<TState>,\n\t): this;\n\taddNode(\n\t\tname: string,\n\t\tconfigOrHandler: NodeConfig<TState> | NodeHandler<TState>,\n\t\tmaybeHandler?: NodeHandler<TState>,\n\t): this {\n\t\tif (name === START || name === END) {\n\t\t\tthrow new Error(\n\t\t\t\t`\"${name}\" is a reserved name and cannot be used as a node name`,\n\t\t\t);\n\t\t}\n\t\tif (this.nodes.has(name)) {\n\t\t\tthrow new Error(`Node \"${name}\" already exists`);\n\t\t}\n\n\t\tlet handler: NodeHandler<TState>;\n\t\tlet nodeConfig: NodeConfig<TState> = {};\n\n\t\tif (typeof configOrHandler === \"function\") {\n\t\t\thandler = configOrHandler;\n\t\t} else {\n\t\t\tif (!maybeHandler) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`addNode(\"${name}\", config, handler) requires a handler as the third argument`,\n\t\t\t\t);\n\t\t\t}\n\t\t\thandler = maybeHandler;\n\t\t\tnodeConfig = configOrHandler;\n\t\t}\n\n\t\tthis.nodes.set(name, handler);\n\t\tthis.nodeConfigs.set(name, nodeConfig);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a direct edge between two nodes.\n\t *\n\t * Use `START` as `from` to set the entry point.\n\t * Use `END` as `to` to mark a terminal node.\n\t */\n\taddEdge(from: string, to: string): this {\n\t\tif (this.edges.has(from)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Node \"${from}\" already has an outgoing edge. Use addConditionalEdge for branching.`,\n\t\t\t);\n\t\t}\n\t\tthis.edges.set(from, { type: \"direct\", to });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a conditional edge from a node.\n\t *\n\t * The condition function receives current state and returns the name of the next node.\n\t */\n\taddConditionalEdge(from: string, condition: ConditionFn<TState>): this {\n\t\tif (this.edges.has(from)) {\n\t\t\tthrow new Error(`Node \"${from}\" already has an outgoing edge.`);\n\t\t}\n\t\tthis.edges.set(from, { type: \"conditional\", condition });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Compile the graph into a RegisteredFlow that can be registered on an McpServer.\n\t *\n\t * Validates the graph structure and returns a registration-compatible object.\n\t */\n\tcompile(): RegisteredFlow {\n\t\tthis.validate();\n\n\t\treturn compileFlow<TState>({\n\t\t\tconfig: this.config,\n\t\t\tnodes: new Map(this.nodes),\n\t\t\tnodeConfigs: new Map(this.nodeConfigs),\n\t\t\tedges: new Map(this.edges),\n\t\t});\n\t}\n\n\tprivate validate(): void {\n\t\t// Must have a START edge\n\t\tif (!this.edges.has(START)) {\n\t\t\tthrow new Error(\n\t\t\t\t'Flow must have an entry point. Add an edge from START: .addEdge(START, \"first_node\")',\n\t\t\t);\n\t\t}\n\n\t\t// START edge target must exist\n\t\tconst startEdge = this.edges.get(START);\n\t\tif (\n\t\t\tstartEdge?.type === \"direct\" &&\n\t\t\tstartEdge.to !== END &&\n\t\t\t!this.nodes.has(startEdge.to)\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`START edge references non-existent node: \"${startEdge.to}\"`,\n\t\t\t);\n\t\t}\n\n\t\t// All static edge targets must reference existing nodes (or END)\n\t\tfor (const [from, edge] of this.edges) {\n\t\t\tif (from !== START && !this.nodes.has(from)) {\n\t\t\t\tthrow new Error(`Edge from non-existent node: \"${from}\"`);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tedge.type === \"direct\" &&\n\t\t\t\tedge.to !== END &&\n\t\t\t\t!this.nodes.has(edge.to)\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Edge from \"${from}\" references non-existent node: \"${edge.to}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Every node must have an outgoing edge\n\t\tfor (const [name] of this.nodes) {\n\t\t\tif (!this.edges.has(name)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Node \"${name}\" has no outgoing edge. Add one with .addEdge(\"${name}\", ...) or .addConditionalEdge(\"${name}\", ...)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n","import type { FlowConfig } from \"./@types\";\nimport { StateGraph } from \"./state-graph\";\n\n/**\n * Create a new flow graph — convenience factory for `new StateGraph()`.\n *\n * @example\n * ```ts\n * import { createFlow, interrupt, START, END } from \"@waniwani/sdk/mcp\";\n *\n * type MyState = { name: string; email: string };\n *\n * const flow = createFlow<MyState>({\n * id: \"onboarding\",\n * title: \"User Onboarding\",\n * description: \"Guides users through onboarding. Use when a user wants to get started.\",\n * })\n * .addNode(\"ask_name\", () => interrupt({ question: \"What's your name?\", field: \"name\" }))\n * .addNode(\"ask_email\", () => interrupt({ question: \"What's your email?\", field: \"email\" }))\n * .addEdge(START, \"ask_name\")\n * .addEdge(\"ask_name\", \"ask_email\")\n * .addEdge(\"ask_email\", END)\n * .compile();\n * ```\n */\nexport function createFlow<TState extends Record<string, unknown>>(\n\tconfig: FlowConfig,\n): StateGraph<TState> {\n\treturn new StateGraph<TState>(config);\n}\n","import {\n\tbuildMcpAppsResourceMeta,\n\tbuildOpenAIResourceMeta,\n\tfetchHtml,\n\tMIME_TYPE_MCP,\n\tMIME_TYPE_OPENAI,\n} from \"./meta\";\nimport type { McpServer, RegisteredResource, ResourceConfig } from \"./types\";\n\n/**\n * Creates a reusable UI resource (HTML template) that can be attached\n * to tools or flow nodes.\n *\n * @example\n * ```ts\n * const pricingUI = createResource({\n * id: \"pricing_table\",\n * title: \"Pricing Table\",\n * baseUrl: \"https://my-app.com\",\n * htmlPath: \"/widgets/pricing\",\n * widgetDomain: \"my-app.com\",\n * });\n *\n * await pricingUI.register(server);\n * ```\n */\nexport function createResource(config: ResourceConfig): RegisteredResource {\n\tconst {\n\t\tid,\n\t\ttitle,\n\t\tdescription,\n\t\tbaseUrl,\n\t\thtmlPath,\n\t\twidgetDomain,\n\t\tprefersBorder = true,\n\t\tautoHeight,\n\t\twidgetCSP,\n\t} = config;\n\n\tconst openaiUri = `ui://widgets/apps-sdk/${id}.html`;\n\tconst mcpUri = `ui://widgets/ext-apps/${id}.html`;\n\n\t// Lazy HTML — fetched once, shared across all calls\n\tlet htmlPromise: Promise<string> | null = null;\n\tconst getHtml = () => {\n\t\tif (!htmlPromise) htmlPromise = fetchHtml(baseUrl, htmlPath);\n\t\treturn htmlPromise;\n\t};\n\n\t// Use description for UI metadata\n\tconst uiDescription = description;\n\n\tasync function register(server: McpServer): Promise<void> {\n\t\tconst html = await getHtml();\n\n\t\t// Register OpenAI Apps SDK resource\n\t\tserver.registerResource(\n\t\t\t`${id}-openai-widget`,\n\t\t\topenaiUri,\n\t\t\t{\n\t\t\t\ttitle,\n\t\t\t\tdescription: uiDescription,\n\t\t\t\tmimeType: MIME_TYPE_OPENAI,\n\t\t\t\t_meta: {\n\t\t\t\t\t\"openai/widgetDescription\": uiDescription,\n\t\t\t\t\t\"openai/widgetPrefersBorder\": prefersBorder,\n\t\t\t\t},\n\t\t\t},\n\t\t\tasync (uri) => ({\n\t\t\t\tcontents: [\n\t\t\t\t\t{\n\t\t\t\t\t\turi: uri.href,\n\t\t\t\t\t\tmimeType: MIME_TYPE_OPENAI,\n\t\t\t\t\t\ttext: html,\n\t\t\t\t\t\t_meta: buildOpenAIResourceMeta({\n\t\t\t\t\t\t\tdescription: uiDescription,\n\t\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t\t\twidgetDomain,\n\t\t\t\t\t\t\twidgetCSP,\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\n\t\t// Register MCP Apps resource\n\t\tserver.registerResource(\n\t\t\t`${id}-mcp-widget`,\n\t\t\tmcpUri,\n\t\t\t{\n\t\t\t\ttitle,\n\t\t\t\tdescription: uiDescription,\n\t\t\t\tmimeType: MIME_TYPE_MCP,\n\t\t\t\t_meta: {\n\t\t\t\t\tui: {\n\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tasync (uri) => ({\n\t\t\t\tcontents: [\n\t\t\t\t\t{\n\t\t\t\t\t\turi: uri.href,\n\t\t\t\t\t\tmimeType: MIME_TYPE_MCP,\n\t\t\t\t\t\ttext: html,\n\t\t\t\t\t\t_meta: buildMcpAppsResourceMeta({\n\t\t\t\t\t\t\tdescription: uiDescription,\n\t\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t\t\twidgetCSP,\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn {\n\t\tid,\n\t\ttitle,\n\t\tdescription,\n\t\topenaiUri,\n\t\tmcpUri,\n\t\tautoHeight,\n\t\tregister,\n\t};\n}\n","import type { ToolCallback } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { ShapeOutput } from \"@modelcontextprotocol/sdk/server/zod-compat.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n\tServerNotification,\n\tServerRequest,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { z } from \"zod\";\nimport { buildToolMeta } from \"../resources/meta\";\nimport type {\n\tMcpServer,\n\tRegisteredTool,\n\tToolConfig,\n\tToolHandler,\n} from \"./types\";\n\n/**\n * Creates an MCP tool with minimal boilerplate.\n *\n * When `config.resource` is provided, the tool returns `structuredContent` + widget metadata.\n * Without a resource, the tool returns plain text content.\n *\n * @example\n * ```ts\n * // Widget tool (with resource)\n * const pricingTool = createTool({\n * resource: pricingUI,\n * description: \"Show pricing comparison\",\n * inputSchema: { postalCode: z.string() },\n * }, async ({ postalCode }) => ({\n * text: \"Pricing loaded\",\n * data: { postalCode, prices: [] },\n * }));\n *\n * // Plain tool (no resource)\n * const searchTool = createTool({\n * id: \"search\",\n * title: \"Search\",\n * description: \"Search the knowledge base\",\n * inputSchema: { query: z.string() },\n * }, async ({ query }) => ({\n * text: `Results for \"${query}\"`,\n * }));\n * ```\n */\nexport function createTool<TInput extends z.ZodRawShape>(\n\tconfig: ToolConfig<TInput>,\n\thandler: ToolHandler<TInput>,\n): RegisteredTool {\n\tconst { resource, description, inputSchema, annotations } = config;\n\n\tconst id = config.id ?? resource?.id;\n\tconst title = config.title ?? resource?.title;\n\n\tif (!id) {\n\t\tthrow new Error(\n\t\t\t\"createTool: `id` is required when no resource is provided\",\n\t\t);\n\t}\n\tif (!title) {\n\t\tthrow new Error(\n\t\t\t\"createTool: `title` is required when no resource is provided\",\n\t\t);\n\t}\n\n\t// Build widget metadata only when resource is present\n\tconst toolMeta = resource\n\t\t? buildToolMeta({\n\t\t\t\topenaiTemplateUri: resource.openaiUri,\n\t\t\t\tmcpTemplateUri: resource.mcpUri,\n\t\t\t\tinvoking: config.invoking ?? \"Loading...\",\n\t\t\t\tinvoked: config.invoked ?? \"Loaded\",\n\t\t\t\tautoHeight: resource.autoHeight,\n\t\t\t})\n\t\t: undefined;\n\n\treturn {\n\t\tid,\n\t\ttitle,\n\t\tdescription,\n\n\t\tasync register(server: McpServer): Promise<void> {\n\t\t\tserver.registerTool(\n\t\t\t\tid,\n\t\t\t\t{\n\t\t\t\t\ttitle,\n\t\t\t\t\tdescription,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tannotations,\n\t\t\t\t\t...(toolMeta && { _meta: toolMeta }),\n\t\t\t\t},\n\t\t\t\t(async (args: ShapeOutput<TInput>, extra: unknown) => {\n\t\t\t\t\tconst requestExtra = extra as RequestHandlerExtra<\n\t\t\t\t\t\tServerRequest,\n\t\t\t\t\t\tServerNotification\n\t\t\t\t\t>;\n\t\t\t\t\tconst _meta: Record<string, unknown> = requestExtra._meta ?? {};\n\n\t\t\t\t\tconst result = await handler(args, { extra: { _meta } });\n\n\t\t\t\t\t// Widget tool: return structuredContent + widget metadata\n\t\t\t\t\tif (resource && result.data) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: result.text }],\n\t\t\t\t\t\t\tstructuredContent: result.data,\n\t\t\t\t\t\t\t_meta: {\n\t\t\t\t\t\t\t\t...toolMeta,\n\t\t\t\t\t\t\t\t..._meta,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Plain tool: return text content only\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: result.text }],\n\t\t\t\t\t};\n\t\t\t\t}) as unknown as ToolCallback<TInput>,\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * Registers multiple tools on the server\n */\nexport async function registerTools(\n\tserver: McpServer,\n\ttools: RegisteredTool[],\n): Promise<void> {\n\tawait Promise.all(tools.map((t) => t.register(server)));\n}\n"],"mappings":"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","names":["detectPlatform","isOpenAI","isMCPApps","START","END","INTERRUPT","WIDGET","interrupt","config","showWidget","resource","isInterrupt","value","isWidget","z","MIME_TYPE_OPENAI","MIME_TYPE_MCP","fetchHtml","baseUrl","path","normalizedBase","buildOpenAIResourceMeta","config","buildMcpAppsResourceMeta","csp","buildToolMeta","describeZodField","schema","desc","def","vals","v","buildFlowProtocol","config","lines","fieldList","key","info","resolveNextNode","edge","state","executeFrom","startNodeName","initialState","nodes","nodeConfigs","edges","flowId","meta","currentNode","MAX_ITERATIONS","iterations","END","handler","result","isInterrupt","existingValue","isWidget","nodeField","resource","error","message","inputSchema","z","compileFlow","input","protocol","fullDescription","firstResource","nc","toolMeta","buildToolMeta","handleToolCall","args","startEdge","START","startState","firstNode","updatedState","nextNode","widgetUpdate","server","extra","_meta","StateGraph","config","name","configOrHandler","maybeHandler","START","END","handler","nodeConfig","from","to","condition","compileFlow","startEdge","edge","createFlow","config","StateGraph","createResource","config","id","title","description","baseUrl","htmlPath","widgetDomain","prefersBorder","autoHeight","widgetCSP","openaiUri","mcpUri","htmlPromise","getHtml","fetchHtml","uiDescription","register","server","html","MIME_TYPE_OPENAI","uri","buildOpenAIResourceMeta","MIME_TYPE_MCP","buildMcpAppsResourceMeta","createTool","config","handler","resource","description","inputSchema","annotations","id","title","toolMeta","buildToolMeta","server","args","extra","_meta","result","registerTools","tools","t"]}
|
|
1
|
+
{"version":3,"sources":["../../src/mcp/react/widgets/platform.ts","../../src/mcp/server/flows/@types.ts","../../src/mcp/server/flows/compile.ts","../../src/mcp/server/resources/meta.ts","../../src/mcp/server/flows/state-graph.ts","../../src/mcp/server/flows/create-flow.ts","../../src/mcp/server/resources/create-resource.ts","../../src/mcp/server/tools/create-tool.ts"],"sourcesContent":["/**\n * Widget platform types\n */\nexport type WidgetPlatform = \"openai\" | \"mcp-apps\";\n\n/**\n * Detects which platform the widget is running on.\n *\n * OpenAI injects a global `window.openai` object.\n * MCP Apps runs in a sandboxed iframe and uses postMessage.\n */\nexport function detectPlatform(): WidgetPlatform {\n\tif (typeof window !== \"undefined\" && \"openai\" in window) {\n\t\treturn \"openai\";\n\t}\n\treturn \"mcp-apps\";\n}\n\n/**\n * Check if running on OpenAI platform\n */\nexport function isOpenAI(): boolean {\n\treturn detectPlatform() === \"openai\";\n}\n\n/**\n * Check if running on MCP Apps platform\n */\nexport function isMCPApps(): boolean {\n\treturn detectPlatform() === \"mcp-apps\";\n}\n","import type { z } from \"zod\";\nimport type { McpServer, RegisteredResource } from \"../resources/types\";\n\nexport type { McpServer };\n\n// ============================================================================\n// Sentinel constants\n// ============================================================================\n\nexport const START = \"__start__\" as const;\nexport const END = \"__end__\" as const;\n\n// ============================================================================\n// Signal types — returned by node handlers to control flow behavior\n// ============================================================================\n\nconst INTERRUPT = Symbol.for(\"waniwani.flow.interrupt\");\nconst WIDGET = Symbol.for(\"waniwani.flow.widget\");\n\nexport type InterruptSignal = {\n\treadonly __type: typeof INTERRUPT;\n\t/** Question to ask the user */\n\tquestion: string;\n\t/** State key where the answer will be stored */\n\tfield: string;\n\t/** Optional suggestions to present as options */\n\tsuggestions?: string[];\n\t/** Hidden context/instructions for the assistant (not shown to user directly) */\n\tcontext?: string;\n};\n\nexport type WidgetSignal = {\n\treadonly __type: typeof WIDGET;\n\t/** The resource to display */\n\tresource: RegisteredResource;\n\t/** Data to pass to the widget as structuredContent */\n\tdata: Record<string, unknown>;\n\t/** Description of what the widget does (for the AI's context) */\n\tdescription?: string;\n};\n\n/**\n * Create an interrupt signal — pauses the flow and asks the user a text question.\n */\nexport function interrupt(config: {\n\tquestion: string;\n\tfield: string;\n\tsuggestions?: string[];\n\tcontext?: string;\n}): InterruptSignal {\n\treturn { __type: INTERRUPT, ...config };\n}\n\n/**\n * Create a widget signal — pauses the flow and renders a widget UI.\n */\nexport function showWidget(\n\tresource: RegisteredResource,\n\tconfig: {\n\t\tdata: Record<string, unknown>;\n\t\tdescription?: string;\n\t},\n): WidgetSignal {\n\treturn { __type: WIDGET, resource, ...config };\n}\n\nexport function isInterrupt(value: unknown): value is InterruptSignal {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"__type\" in value &&\n\t\t(value as InterruptSignal).__type === INTERRUPT\n\t);\n}\n\nexport function isWidget(value: unknown): value is WidgetSignal {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"__type\" in value &&\n\t\t(value as WidgetSignal).__type === WIDGET\n\t);\n}\n\n// ============================================================================\n// Node & edge definitions\n// ============================================================================\n\nexport type MaybePromise<T> = T | Promise<T>;\n\n/** Configuration for a flow node */\nexport type NodeConfig<\n\tTState extends Record<string, unknown> = Record<string, unknown>,\n> = {\n\t/** Resource to display when this node returns a WidgetSignal */\n\tresource?: RegisteredResource;\n\t/** State key this node fills — enables auto-skip when the field is already in state via `initialState` */\n\tfield?: Extract<keyof TState, string>;\n};\n\n/**\n * Node handler — a single function type for all node kinds.\n * The return value determines behavior:\n * - `Partial<TState>` → action node (state merged, auto-advance)\n * - `InterruptSignal` → interrupt (pause, ask user)\n * - `WidgetSignal` → widget step (pause, show widget)\n */\nexport type NodeHandler<TState> = (\n\tstate: Partial<TState>,\n\tmeta?: Record<string, unknown>,\n) => MaybePromise<Partial<TState> | InterruptSignal | WidgetSignal>;\n\n/**\n * Condition function for conditional edges.\n * Receives current state, returns the name of the next node.\n */\nexport type ConditionFn<TState> = (\n\tstate: Partial<TState>,\n) => string | Promise<string>;\n\nexport type Edge<TState> =\n\t| { type: \"direct\"; to: string }\n\t| { type: \"conditional\"; condition: ConditionFn<TState> };\n\n// ============================================================================\n// Flow config & compiled output\n// ============================================================================\n\nexport type FlowConfig = {\n\t/** Unique identifier for the flow (becomes the MCP tool name) */\n\tid: string;\n\t/** Display title */\n\ttitle: string;\n\t/** Description for the AI (explains when to use this flow) */\n\tdescription: string;\n\t/**\n\t * Describe the flow's state fields so the AI can pre-fill answers\n\t * from the user's message via `initialState`.\n\t * Keys are the field names used in `interrupt({ field })`,\n\t * values are Zod schemas with `.describe()`.\n\t *\n\t * @example\n\t * ```ts\n\t * fields: {\n\t * country: z.string().describe(\"Country the business is based in\"),\n\t * status: z.enum([\"registered\", \"unregistered\"]).describe(\"Business registration status\"),\n\t * }\n\t * ```\n\t */\n\tfields?: Record<string, z.ZodType>;\n\t/** Optional tool annotations */\n\tannotations?: {\n\t\treadOnlyHint?: boolean;\n\t\tidempotentHint?: boolean;\n\t\topenWorldHint?: boolean;\n\t\tdestructiveHint?: boolean;\n\t};\n};\n\n/**\n * A compiled flow — can be registered on an McpServer.\n */\nexport type RegisteredFlow = {\n\tid: string;\n\ttitle: string;\n\tdescription: string;\n\tregister: (server: McpServer) => Promise<void>;\n};\n","import type { ToolCallback } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n\tServerNotification,\n\tServerRequest,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport { buildToolMeta } from \"../resources/meta\";\nimport type { RegisteredResource } from \"../resources/types\";\nimport type {\n\tEdge,\n\tFlowConfig,\n\tMcpServer,\n\tNodeConfig,\n\tNodeHandler,\n\tRegisteredFlow,\n} from \"./@types\";\nimport { END, isInterrupt, isWidget, START } from \"./@types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface CompileInput<TState extends Record<string, unknown>> {\n\tconfig: FlowConfig;\n\tnodes: Map<string, NodeHandler<TState>>;\n\tnodeConfigs: Map<string, NodeConfig<TState>>;\n\tedges: Map<string, Edge<TState>>;\n}\n\ntype FlowToolInput = {\n\taction: \"start\" | \"continue\" | \"widget_result\";\n\tstep?: string;\n\tstate?: Record<string, unknown>;\n\tanswer?: string;\n\twidgetResult?: Record<string, unknown>;\n\tinitialState?: Record<string, unknown>;\n};\n\ntype ExecutionResult = {\n\ttext: string;\n\tdata: Record<string, unknown>;\n\twidgetMeta?: Record<string, unknown>;\n};\n\n// ============================================================================\n// Flow protocol — embedded in tool description\n// ============================================================================\n\n/** Extract a human-readable label from a Zod schema for the AI protocol */\nfunction describeZodField(schema: z.ZodType): string {\n\tconst desc = schema.description ?? \"\";\n\tconst def = (\n\t\tschema as unknown as {\n\t\t\t_zod: { def: { type: string; entries?: Record<string, string> } };\n\t\t}\n\t)._zod?.def;\n\n\tif (def?.type === \"enum\" && def.entries) {\n\t\tconst vals = Object.keys(def.entries)\n\t\t\t.map((v) => `\"${v}\"`)\n\t\t\t.join(\" | \");\n\t\treturn desc ? `${vals} — ${desc}` : vals;\n\t}\n\n\treturn desc;\n}\n\nfunction buildFlowProtocol(config: FlowConfig): string {\n\tconst lines = [\n\t\t\"\",\n\t\t\"## FLOW EXECUTION PROTOCOL\",\n\t\t\"\",\n\t\t\"This tool implements a multi-step conversational flow. Follow this protocol exactly:\",\n\t\t\"\",\n\t\t'1. Call with `action: \"start\"` to begin. If the user\\'s message already',\n\t\t\" contains answers to likely questions, extract them into `initialState`\",\n\t\t\" as `{ field: value }` pairs. The engine will auto-skip questions whose\",\n\t\t\" fields are already filled.\",\n\t\t\" Only extract values the user explicitly stated — do NOT guess or invent values.\",\n\t];\n\n\tif (config.fields) {\n\t\tconst fieldList = Object.entries(config.fields)\n\t\t\t.map(([key, schema]) => {\n\t\t\t\tconst info = describeZodField(schema);\n\t\t\t\treturn info ? `\\`${key}\\` (${info})` : `\\`${key}\\``;\n\t\t\t})\n\t\t\t.join(\", \");\n\t\tlines.push(` Known fields: ${fieldList}.`);\n\t}\n\n\tlines.push(\n\t\t\"2. The response JSON `status` field tells you what to do next:\",\n\t\t' - `\"interrupt\"`: Ask the user the `question`. If a `context` field is present,',\n\t\t\" use it as hidden instructions to enrich your response (do NOT show it verbatim).\",\n\t\t\" Then call again with:\",\n\t\t' `action: \"continue\"`, `step` = the returned `step`, `state` = the returned `state`,',\n\t\t\" `answer` = the user's answer.\",\n\t\t' - `\"widget\"`: A widget UI is being shown. The user will interact with the widget.',\n\t\t\" When the user makes a choice and you need to continue the flow, call again with:\",\n\t\t' `action: \"widget_result\"`, `step` = the returned `step`, `state` = the returned `state`,',\n\t\t\" `answer` = the user's selection.\",\n\t\t' - `\"complete\"`: The flow is done. Present the result to the user.',\n\t\t' - `\"error\"`: Something went wrong. Show the `error` message.',\n\t\t\"\",\n\t\t\"3. ALWAYS pass back the `state` object exactly as received.\",\n\t\t\"4. Do NOT invent state values. Only use `initialState` for information the user explicitly provided.\",\n\t);\n\n\treturn lines.join(\"\\n\");\n}\n\n// ============================================================================\n// Edge resolution\n// ============================================================================\n\nasync function resolveNextNode<TState extends Record<string, unknown>>(\n\tedge: Edge<TState>,\n\tstate: Partial<TState>,\n): Promise<string> {\n\tif (edge.type === \"direct\") return edge.to;\n\treturn edge.condition(state);\n}\n\n// ============================================================================\n// Execution engine\n// ============================================================================\n\nasync function executeFrom<TState extends Record<string, unknown>>(\n\tstartNodeName: string,\n\tinitialState: TState,\n\tnodes: Map<string, NodeHandler<TState>>,\n\tnodeConfigs: Map<string, NodeConfig<TState>>,\n\tedges: Map<string, Edge<TState>>,\n\tflowId: string,\n\tmeta?: Record<string, unknown>,\n): Promise<ExecutionResult> {\n\tlet currentNode = startNodeName;\n\tlet state = { ...initialState };\n\n\t// Safety limit to prevent infinite loops\n\tconst MAX_ITERATIONS = 50;\n\tlet iterations = 0;\n\n\twhile (iterations++ < MAX_ITERATIONS) {\n\t\t// Reached END\n\t\tif (currentNode === END) {\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\tstate,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"complete\", state },\n\t\t\t};\n\t\t}\n\n\t\tconst handler = nodes.get(currentNode);\n\t\tif (!handler) {\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\terror: `Unknown node: \"${currentNode}\"`,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"error\" },\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await handler(state, meta);\n\n\t\t\t// Interrupt signal — pause and ask the user\n\t\t\tif (isInterrupt(result)) {\n\t\t\t\t// Auto-skip: if the field already has a value in state, advance\n\t\t\t\tconst existingValue = state[result.field as keyof TState];\n\t\t\t\tif (\n\t\t\t\t\texistingValue !== undefined &&\n\t\t\t\t\texistingValue !== null &&\n\t\t\t\t\texistingValue !== \"\"\n\t\t\t\t) {\n\t\t\t\t\tconst edge = edges.get(currentNode);\n\t\t\t\t\tif (!edge) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\t\t\terror: `No outgoing edge from node \"${currentNode}\"`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tcurrentNode = await resolveNextNode(edge, state);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"interrupt\",\n\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\tquestion: result.question,\n\t\t\t\t\t\tfield: result.field,\n\t\t\t\t\t\tsuggestions: result.suggestions,\n\t\t\t\t\t\t...(result.context ? { context: result.context } : {}),\n\t\t\t\t\t\tstate,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"interrupt\", step: currentNode, state },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Widget signal — pause and show widget\n\t\t\tif (isWidget(result)) {\n\t\t\t\t// Auto-skip: if the node config declares a field and it's already filled, advance\n\t\t\t\tconst nodeField = nodeConfigs.get(currentNode)?.field;\n\t\t\t\tif (nodeField) {\n\t\t\t\t\tconst existingValue = state[nodeField as keyof TState];\n\t\t\t\t\tif (\n\t\t\t\t\t\texistingValue !== undefined &&\n\t\t\t\t\t\texistingValue !== null &&\n\t\t\t\t\t\texistingValue !== \"\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst edge = edges.get(currentNode);\n\t\t\t\t\t\tif (!edge) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\t\t\t\terror: `No outgoing edge from node \"${currentNode}\"`,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcurrentNode = await resolveNextNode(edge, state);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst resource = result.resource;\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"widget\",\n\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\twidgetId: resource.id,\n\t\t\t\t\t\tdescription: result.description,\n\t\t\t\t\t\tstate,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: {\n\t\t\t\t\t\t...result.data,\n\t\t\t\t\t\t__flow: {\n\t\t\t\t\t\t\tflowId,\n\t\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\twidgetMeta: buildToolMeta({\n\t\t\t\t\t\topenaiTemplateUri: resource.openaiUri,\n\t\t\t\t\t\tmcpTemplateUri: resource.mcpUri,\n\t\t\t\t\t\tinvoking: \"Loading...\",\n\t\t\t\t\t\tinvoked: \"Loaded\",\n\t\t\t\t\t\tautoHeight: resource.autoHeight,\n\t\t\t\t\t}),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Action node — merge state and auto-advance\n\t\t\tstate = { ...state, ...result } as TState;\n\n\t\t\tconst edge = edges.get(currentNode);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No outgoing edge from node \"${currentNode}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tcurrentNode = await resolveNextNode(edge, state);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\tstep: currentNode,\n\t\t\t\t\terror: message,\n\t\t\t\t\tstate,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"error\", error: message },\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {\n\t\ttext: JSON.stringify({\n\t\t\tstatus: \"error\",\n\t\t\terror: \"Flow exceeded maximum iterations (possible infinite loop)\",\n\t\t}),\n\t\tdata: { status: \"error\" },\n\t};\n}\n\n// ============================================================================\n// Compile\n// ============================================================================\n\nconst inputSchema = {\n\taction: z\n\t\t.enum([\"start\", \"continue\", \"widget_result\"])\n\t\t.describe(\n\t\t\t'\"start\" to begin the flow, \"continue\" after the user answers a question, \"widget_result\" when a widget returns data',\n\t\t),\n\tstep: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe(\"Current step name (from the previous response)\"),\n\tstate: z\n\t\t.record(z.string(), z.unknown())\n\t\t.optional()\n\t\t.describe(\"Flow state — pass back exactly as received\"),\n\tanswer: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe(\"The user's answer (for interrupt steps)\"),\n\twidgetResult: z\n\t\t.record(z.string(), z.unknown())\n\t\t.optional()\n\t\t.describe(\"Data returned by a widget callback\"),\n\tinitialState: z\n\t\t.record(z.string(), z.unknown())\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Pre-filled answers extracted from the user\\'s message (only for action: \"start\")',\n\t\t),\n};\n\nexport function compileFlow<TState extends Record<string, unknown>>(\n\tinput: CompileInput<TState>,\n): RegisteredFlow {\n\tconst { config, nodes, nodeConfigs, edges } = input;\n\tconst protocol = buildFlowProtocol(config);\n\tconst fullDescription = `${config.description}\\n${protocol}`;\n\n\t// Find the first resource from node configs to build tool-level widget metadata.\n\t// This tells OpenAI/Claude that this tool can produce widget UIs.\n\tlet firstResource: RegisteredResource | undefined;\n\tfor (const nc of nodeConfigs.values()) {\n\t\tif (nc.resource) {\n\t\t\tfirstResource = nc.resource;\n\t\t\tbreak;\n\t\t}\n\t}\n\tconst toolMeta = firstResource\n\t\t? buildToolMeta({\n\t\t\t\topenaiTemplateUri: firstResource.openaiUri,\n\t\t\t\tmcpTemplateUri: firstResource.mcpUri,\n\t\t\t\tinvoking: \"Loading...\",\n\t\t\t\tinvoked: \"Loaded\",\n\t\t\t\tautoHeight: firstResource.autoHeight,\n\t\t\t})\n\t\t: undefined;\n\n\tasync function handleToolCall(\n\t\targs: FlowToolInput,\n\t\tmeta?: Record<string, unknown>,\n\t): Promise<ExecutionResult> {\n\t\tconst state = (args.state ?? {}) as TState;\n\n\t\tif (args.action === \"start\") {\n\t\t\tconst startEdge = edges.get(START);\n\t\t\tif (!startEdge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: \"No start edge\",\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Merge pre-filled answers from the user's initial message\n\t\t\tconst startState = (\n\t\t\t\targs.initialState ? { ...state, ...args.initialState } : state\n\t\t\t) as TState;\n\n\t\t\tconst firstNode = await resolveNextNode(startEdge, startState);\n\t\t\treturn executeFrom(\n\t\t\t\tfirstNode,\n\t\t\t\tstartState,\n\t\t\t\tnodes,\n\t\t\t\tnodeConfigs,\n\t\t\t\tedges,\n\t\t\t\tconfig.id,\n\t\t\t\tmeta,\n\t\t\t);\n\t\t}\n\n\t\tif (args.action === \"continue\") {\n\t\t\tif (!args.step) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: 'Missing \"step\" for continue action',\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Apply user's answer to state using the field from the interrupt\n\t\t\tlet updatedState = { ...state };\n\t\t\tif (args.answer) {\n\t\t\t\tconst handler = nodes.get(args.step);\n\t\t\t\tif (handler) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await handler(updatedState, meta);\n\t\t\t\t\t\tif (isInterrupt(result) && result.field) {\n\t\t\t\t\t\t\tupdatedState = {\n\t\t\t\t\t\t\t\t...updatedState,\n\t\t\t\t\t\t\t\t[result.field]: args.answer,\n\t\t\t\t\t\t\t} as TState;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// If re-running the handler fails, still proceed with the answer\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Advance to next node\n\t\t\tconst edge = edges.get(args.step);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No edge from step \"${args.step}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst nextNode = await resolveNextNode(edge, updatedState);\n\t\t\treturn executeFrom(\n\t\t\t\tnextNode,\n\t\t\t\tupdatedState,\n\t\t\t\tnodes,\n\t\t\t\tnodeConfigs,\n\t\t\t\tedges,\n\t\t\t\tconfig.id,\n\t\t\t\tmeta,\n\t\t\t);\n\t\t}\n\n\t\tif (args.action === \"widget_result\") {\n\t\t\tif (!args.step) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: 'Missing \"step\" for widget_result action',\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Merge widget result into state.\n\t\t\t// If `answer` is provided and the node declares a `field`, auto-map it.\n\t\t\tlet widgetUpdate: Record<string, unknown> = args.widgetResult ?? {};\n\t\t\tif (args.answer !== undefined && Object.keys(widgetUpdate).length === 0) {\n\t\t\t\tconst nodeField = nodeConfigs.get(args.step)?.field;\n\t\t\t\tif (nodeField) {\n\t\t\t\t\twidgetUpdate = { [nodeField]: args.answer };\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst updatedState = {\n\t\t\t\t...state,\n\t\t\t\t...widgetUpdate,\n\t\t\t} as TState;\n\n\t\t\t// Advance to next node\n\t\t\tconst edge = edges.get(args.step);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No edge from step \"${args.step}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst nextNode = await resolveNextNode(edge, updatedState);\n\t\t\treturn executeFrom(\n\t\t\t\tnextNode,\n\t\t\t\tupdatedState,\n\t\t\t\tnodes,\n\t\t\t\tnodeConfigs,\n\t\t\t\tedges,\n\t\t\t\tconfig.id,\n\t\t\t\tmeta,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\ttext: JSON.stringify({\n\t\t\t\tstatus: \"error\",\n\t\t\t\terror: `Unknown action: \"${args.action}\"`,\n\t\t\t}),\n\t\t\tdata: { status: \"error\" },\n\t\t};\n\t}\n\n\treturn {\n\t\tid: config.id,\n\t\ttitle: config.title,\n\t\tdescription: fullDescription,\n\n\t\tasync register(server: McpServer): Promise<void> {\n\t\t\tserver.registerTool(\n\t\t\t\tconfig.id,\n\t\t\t\t{\n\t\t\t\t\ttitle: config.title,\n\t\t\t\t\tdescription: fullDescription,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tannotations: config.annotations,\n\t\t\t\t\t...(toolMeta && { _meta: toolMeta }),\n\t\t\t\t},\n\t\t\t\t(async (args: FlowToolInput, extra: unknown) => {\n\t\t\t\t\tconst requestExtra = extra as RequestHandlerExtra<\n\t\t\t\t\t\tServerRequest,\n\t\t\t\t\t\tServerNotification\n\t\t\t\t\t>;\n\t\t\t\t\tconst _meta: Record<string, unknown> = requestExtra._meta ?? {};\n\n\t\t\t\t\tconst result = await handleToolCall(args, _meta);\n\n\t\t\t\t\t// Widget response — include structuredContent + widget metadata\n\t\t\t\t\tif (result.widgetMeta) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: result.text }],\n\t\t\t\t\t\t\tstructuredContent: result.data,\n\t\t\t\t\t\t\t_meta: {\n\t\t\t\t\t\t\t\t...result.widgetMeta,\n\t\t\t\t\t\t\t\t..._meta,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Non-widget response (interrupt, complete, error) — text only\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: result.text }],\n\t\t\t\t\t};\n\t\t\t\t}) as unknown as ToolCallback<typeof inputSchema>,\n\t\t\t);\n\t\t},\n\t};\n}\n","import type { WidgetCSP } from \"./types\";\n\n/**\n * MIME types for widget resources.\n * OpenAI Apps SDK uses \"text/html+skybridge\"\n * MCP Apps uses \"text/html;profile=mcp-app\"\n */\nexport const MIME_TYPE_OPENAI = \"text/html+skybridge\";\nexport const MIME_TYPE_MCP = \"text/html;profile=mcp-app\";\n\n// ---- HTML fetching ----\n\nexport const fetchHtml = async (\n\tbaseUrl: string,\n\tpath: string,\n): Promise<string> => {\n\tconst normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\tconst result = await fetch(`${normalizedBase}${path}`);\n\treturn await result.text();\n};\n\n// ---- OpenAI resource metadata ----\n\ninterface OpenAIResourceMeta {\n\t[key: string]: unknown;\n\t\"openai/widgetDescription\"?: string;\n\t\"openai/widgetPrefersBorder\"?: boolean;\n\t\"openai/widgetDomain\"?: string;\n\t\"openai/widgetCSP\"?: WidgetCSP;\n}\n\nexport function buildOpenAIResourceMeta(config: {\n\tdescription?: string;\n\tprefersBorder?: boolean;\n\twidgetDomain: string;\n\twidgetCSP?: WidgetCSP;\n}): OpenAIResourceMeta {\n\treturn {\n\t\t\"openai/widgetDescription\": config.description,\n\t\t\"openai/widgetPrefersBorder\": config.prefersBorder,\n\t\t\"openai/widgetDomain\": config.widgetDomain,\n\t\t...(config.widgetCSP && { \"openai/widgetCSP\": config.widgetCSP }),\n\t};\n}\n\n// ---- MCP Apps resource metadata ----\n\ninterface McpAppsResourceMeta {\n\t[key: string]: unknown;\n\tui?: {\n\t\tcsp?: {\n\t\t\tconnectDomains?: string[];\n\t\t\tresourceDomains?: string[];\n\t\t\tframeDomains?: string[];\n\t\t\tredirectDomains?: string[];\n\t\t};\n\t\tdomain?: string;\n\t\tprefersBorder?: boolean;\n\t};\n}\n\nexport function buildMcpAppsResourceMeta(config: {\n\tdescription?: string;\n\tprefersBorder?: boolean;\n\twidgetCSP?: WidgetCSP;\n}): McpAppsResourceMeta {\n\tconst csp = config.widgetCSP\n\t\t? {\n\t\t\t\tconnectDomains: config.widgetCSP.connect_domains,\n\t\t\t\tresourceDomains: config.widgetCSP.resource_domains,\n\t\t\t\tframeDomains: config.widgetCSP.frame_domains,\n\t\t\t\tredirectDomains: config.widgetCSP.redirect_domains,\n\t\t\t}\n\t\t: undefined;\n\n\treturn {\n\t\tui: {\n\t\t\t...(csp && { csp }),\n\t\t\t...(config.prefersBorder !== undefined && {\n\t\t\t\tprefersBorder: config.prefersBorder,\n\t\t\t}),\n\t\t},\n\t};\n}\n\n// ---- Tool metadata (references resource URIs) ----\n\nexport function buildToolMeta(config: {\n\topenaiTemplateUri: string;\n\tmcpTemplateUri: string;\n\tinvoking: string;\n\tinvoked: string;\n\tautoHeight?: boolean;\n}) {\n\treturn {\n\t\t// OpenAI metadata\n\t\t\"openai/outputTemplate\": config.openaiTemplateUri,\n\t\t\"openai/toolInvocation/invoking\": config.invoking,\n\t\t\"openai/toolInvocation/invoked\": config.invoked,\n\t\t\"openai/widgetAccessible\": true,\n\t\t\"openai/resultCanProduceWidget\": true,\n\t\t// MCP Apps metadata\n\t\tui: {\n\t\t\tresourceUri: config.mcpTemplateUri,\n\t\t\t...(config.autoHeight && { autoHeight: true }),\n\t\t},\n\t} as const;\n}\n","import type {\n\tConditionFn,\n\tEdge,\n\tFlowConfig,\n\tNodeConfig,\n\tNodeHandler,\n\tRegisteredFlow,\n} from \"./@types\";\nimport { END, START } from \"./@types\";\nimport { compileFlow } from \"./compile\";\n\n/**\n * A LangGraph-inspired state graph builder for MCP tools.\n *\n * @example\n * ```ts\n * const flow = new StateGraph<MyState>({\n * id: \"onboarding\",\n * title: \"User Onboarding\",\n * description: \"Guides users through onboarding\",\n * })\n * .addNode(\"ask_name\", () => interrupt({ question: \"What's your name?\", field: \"name\" }))\n * .addNode(\"greet\", (state) => ({ greeting: `Hello ${state.name}!` }))\n * .addEdge(START, \"ask_name\")\n * .addEdge(\"ask_name\", \"greet\")\n * .addEdge(\"greet\", END)\n * .compile();\n * ```\n */\nexport class StateGraph<TState extends Record<string, unknown>> {\n\tprivate nodes = new Map<string, NodeHandler<TState>>();\n\tprivate nodeConfigs = new Map<string, NodeConfig<TState>>();\n\tprivate edges = new Map<string, Edge<TState>>();\n\tprivate config: FlowConfig;\n\n\tconstructor(config: FlowConfig) {\n\t\tthis.config = config;\n\t}\n\n\t/**\n\t * Add a node with just a handler.\n\t */\n\taddNode(name: string, handler: NodeHandler<TState>): this;\n\t/**\n\t * Add a node with config (e.g., resource, field) and a handler.\n\t */\n\taddNode(\n\t\tname: string,\n\t\tconfig: NodeConfig<TState>,\n\t\thandler: NodeHandler<TState>,\n\t): this;\n\taddNode(\n\t\tname: string,\n\t\tconfigOrHandler: NodeConfig<TState> | NodeHandler<TState>,\n\t\tmaybeHandler?: NodeHandler<TState>,\n\t): this {\n\t\tif (name === START || name === END) {\n\t\t\tthrow new Error(\n\t\t\t\t`\"${name}\" is a reserved name and cannot be used as a node name`,\n\t\t\t);\n\t\t}\n\t\tif (this.nodes.has(name)) {\n\t\t\tthrow new Error(`Node \"${name}\" already exists`);\n\t\t}\n\n\t\tlet handler: NodeHandler<TState>;\n\t\tlet nodeConfig: NodeConfig<TState> = {};\n\n\t\tif (typeof configOrHandler === \"function\") {\n\t\t\thandler = configOrHandler;\n\t\t} else {\n\t\t\tif (!maybeHandler) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`addNode(\"${name}\", config, handler) requires a handler as the third argument`,\n\t\t\t\t);\n\t\t\t}\n\t\t\thandler = maybeHandler;\n\t\t\tnodeConfig = configOrHandler;\n\t\t}\n\n\t\tthis.nodes.set(name, handler);\n\t\tthis.nodeConfigs.set(name, nodeConfig);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a direct edge between two nodes.\n\t *\n\t * Use `START` as `from` to set the entry point.\n\t * Use `END` as `to` to mark a terminal node.\n\t */\n\taddEdge(from: string, to: string): this {\n\t\tif (this.edges.has(from)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Node \"${from}\" already has an outgoing edge. Use addConditionalEdge for branching.`,\n\t\t\t);\n\t\t}\n\t\tthis.edges.set(from, { type: \"direct\", to });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a conditional edge from a node.\n\t *\n\t * The condition function receives current state and returns the name of the next node.\n\t */\n\taddConditionalEdge(from: string, condition: ConditionFn<TState>): this {\n\t\tif (this.edges.has(from)) {\n\t\t\tthrow new Error(`Node \"${from}\" already has an outgoing edge.`);\n\t\t}\n\t\tthis.edges.set(from, { type: \"conditional\", condition });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Compile the graph into a RegisteredFlow that can be registered on an McpServer.\n\t *\n\t * Validates the graph structure and returns a registration-compatible object.\n\t */\n\tcompile(): RegisteredFlow {\n\t\tthis.validate();\n\n\t\treturn compileFlow<TState>({\n\t\t\tconfig: this.config,\n\t\t\tnodes: new Map(this.nodes),\n\t\t\tnodeConfigs: new Map(this.nodeConfigs),\n\t\t\tedges: new Map(this.edges),\n\t\t});\n\t}\n\n\tprivate validate(): void {\n\t\t// Must have a START edge\n\t\tif (!this.edges.has(START)) {\n\t\t\tthrow new Error(\n\t\t\t\t'Flow must have an entry point. Add an edge from START: .addEdge(START, \"first_node\")',\n\t\t\t);\n\t\t}\n\n\t\t// START edge target must exist\n\t\tconst startEdge = this.edges.get(START);\n\t\tif (\n\t\t\tstartEdge?.type === \"direct\" &&\n\t\t\tstartEdge.to !== END &&\n\t\t\t!this.nodes.has(startEdge.to)\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`START edge references non-existent node: \"${startEdge.to}\"`,\n\t\t\t);\n\t\t}\n\n\t\t// All static edge targets must reference existing nodes (or END)\n\t\tfor (const [from, edge] of this.edges) {\n\t\t\tif (from !== START && !this.nodes.has(from)) {\n\t\t\t\tthrow new Error(`Edge from non-existent node: \"${from}\"`);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tedge.type === \"direct\" &&\n\t\t\t\tedge.to !== END &&\n\t\t\t\t!this.nodes.has(edge.to)\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Edge from \"${from}\" references non-existent node: \"${edge.to}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Every node must have an outgoing edge\n\t\tfor (const [name] of this.nodes) {\n\t\t\tif (!this.edges.has(name)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Node \"${name}\" has no outgoing edge. Add one with .addEdge(\"${name}\", ...) or .addConditionalEdge(\"${name}\", ...)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n","import type { FlowConfig } from \"./@types\";\nimport { StateGraph } from \"./state-graph\";\n\n/**\n * Create a new flow graph — convenience factory for `new StateGraph()`.\n *\n * @example\n * ```ts\n * import { createFlow, interrupt, START, END } from \"@waniwani/sdk/mcp\";\n *\n * type MyState = { name: string; email: string };\n *\n * const flow = createFlow<MyState>({\n * id: \"onboarding\",\n * title: \"User Onboarding\",\n * description: \"Guides users through onboarding. Use when a user wants to get started.\",\n * })\n * .addNode(\"ask_name\", () => interrupt({ question: \"What's your name?\", field: \"name\" }))\n * .addNode(\"ask_email\", () => interrupt({ question: \"What's your email?\", field: \"email\" }))\n * .addEdge(START, \"ask_name\")\n * .addEdge(\"ask_name\", \"ask_email\")\n * .addEdge(\"ask_email\", END)\n * .compile();\n * ```\n */\nexport function createFlow<TState extends Record<string, unknown>>(\n\tconfig: FlowConfig,\n): StateGraph<TState> {\n\treturn new StateGraph<TState>(config);\n}\n","import {\n\tbuildMcpAppsResourceMeta,\n\tbuildOpenAIResourceMeta,\n\tfetchHtml,\n\tMIME_TYPE_MCP,\n\tMIME_TYPE_OPENAI,\n} from \"./meta\";\nimport type { McpServer, RegisteredResource, ResourceConfig } from \"./types\";\n\n/**\n * Creates a reusable UI resource (HTML template) that can be attached\n * to tools or flow nodes.\n *\n * @example\n * ```ts\n * const pricingUI = createResource({\n * id: \"pricing_table\",\n * title: \"Pricing Table\",\n * baseUrl: \"https://my-app.com\",\n * htmlPath: \"/widgets/pricing\",\n * widgetDomain: \"my-app.com\",\n * });\n *\n * await pricingUI.register(server);\n * ```\n */\nexport function createResource(config: ResourceConfig): RegisteredResource {\n\tconst {\n\t\tid,\n\t\ttitle,\n\t\tdescription,\n\t\tbaseUrl,\n\t\thtmlPath,\n\t\twidgetDomain,\n\t\tprefersBorder = true,\n\t\tautoHeight,\n\t\twidgetCSP,\n\t} = config;\n\n\tconst openaiUri = `ui://widgets/apps-sdk/${id}.html`;\n\tconst mcpUri = `ui://widgets/ext-apps/${id}.html`;\n\n\t// Lazy HTML — fetched once, shared across all calls\n\tlet htmlPromise: Promise<string> | null = null;\n\tconst getHtml = () => {\n\t\tif (!htmlPromise) htmlPromise = fetchHtml(baseUrl, htmlPath);\n\t\treturn htmlPromise;\n\t};\n\n\t// Use description for UI metadata\n\tconst uiDescription = description;\n\n\tasync function register(server: McpServer): Promise<void> {\n\t\tconst html = await getHtml();\n\n\t\t// Register OpenAI Apps SDK resource\n\t\tserver.registerResource(\n\t\t\t`${id}-openai-widget`,\n\t\t\topenaiUri,\n\t\t\t{\n\t\t\t\ttitle,\n\t\t\t\tdescription: uiDescription,\n\t\t\t\tmimeType: MIME_TYPE_OPENAI,\n\t\t\t\t_meta: {\n\t\t\t\t\t\"openai/widgetDescription\": uiDescription,\n\t\t\t\t\t\"openai/widgetPrefersBorder\": prefersBorder,\n\t\t\t\t},\n\t\t\t},\n\t\t\tasync (uri) => ({\n\t\t\t\tcontents: [\n\t\t\t\t\t{\n\t\t\t\t\t\turi: uri.href,\n\t\t\t\t\t\tmimeType: MIME_TYPE_OPENAI,\n\t\t\t\t\t\ttext: html,\n\t\t\t\t\t\t_meta: buildOpenAIResourceMeta({\n\t\t\t\t\t\t\tdescription: uiDescription,\n\t\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t\t\twidgetDomain,\n\t\t\t\t\t\t\twidgetCSP,\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\n\t\t// Register MCP Apps resource\n\t\tserver.registerResource(\n\t\t\t`${id}-mcp-widget`,\n\t\t\tmcpUri,\n\t\t\t{\n\t\t\t\ttitle,\n\t\t\t\tdescription: uiDescription,\n\t\t\t\tmimeType: MIME_TYPE_MCP,\n\t\t\t\t_meta: {\n\t\t\t\t\tui: {\n\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tasync (uri) => ({\n\t\t\t\tcontents: [\n\t\t\t\t\t{\n\t\t\t\t\t\turi: uri.href,\n\t\t\t\t\t\tmimeType: MIME_TYPE_MCP,\n\t\t\t\t\t\ttext: html,\n\t\t\t\t\t\t_meta: buildMcpAppsResourceMeta({\n\t\t\t\t\t\t\tdescription: uiDescription,\n\t\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t\t\twidgetCSP,\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn {\n\t\tid,\n\t\ttitle,\n\t\tdescription,\n\t\topenaiUri,\n\t\tmcpUri,\n\t\tautoHeight,\n\t\tregister,\n\t};\n}\n","import type { ToolCallback } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { ShapeOutput } from \"@modelcontextprotocol/sdk/server/zod-compat.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n\tServerNotification,\n\tServerRequest,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { z } from \"zod\";\nimport { buildToolMeta } from \"../resources/meta\";\nimport type {\n\tMcpServer,\n\tRegisteredTool,\n\tToolConfig,\n\tToolHandler,\n} from \"./types\";\n\n/**\n * Creates an MCP tool with minimal boilerplate.\n *\n * When `config.resource` is provided, the tool returns `structuredContent` + widget metadata.\n * Without a resource, the tool returns plain text content.\n *\n * @example\n * ```ts\n * // Widget tool (with resource)\n * const pricingTool = createTool({\n * resource: pricingUI,\n * description: \"Show pricing comparison\",\n * inputSchema: { postalCode: z.string() },\n * }, async ({ postalCode }) => ({\n * text: \"Pricing loaded\",\n * data: { postalCode, prices: [] },\n * }));\n *\n * // Plain tool (no resource)\n * const searchTool = createTool({\n * id: \"search\",\n * title: \"Search\",\n * description: \"Search the knowledge base\",\n * inputSchema: { query: z.string() },\n * }, async ({ query }) => ({\n * text: `Results for \"${query}\"`,\n * }));\n * ```\n */\nexport function createTool<TInput extends z.ZodRawShape>(\n\tconfig: ToolConfig<TInput>,\n\thandler: ToolHandler<TInput>,\n): RegisteredTool {\n\tconst { resource, description, inputSchema, annotations } = config;\n\n\tconst id = config.id ?? resource?.id;\n\tconst title = config.title ?? resource?.title;\n\n\tif (!id) {\n\t\tthrow new Error(\n\t\t\t\"createTool: `id` is required when no resource is provided\",\n\t\t);\n\t}\n\tif (!title) {\n\t\tthrow new Error(\n\t\t\t\"createTool: `title` is required when no resource is provided\",\n\t\t);\n\t}\n\n\t// Build widget metadata only when resource is present\n\tconst toolMeta = resource\n\t\t? buildToolMeta({\n\t\t\t\topenaiTemplateUri: resource.openaiUri,\n\t\t\t\tmcpTemplateUri: resource.mcpUri,\n\t\t\t\tinvoking: config.invoking ?? \"Loading...\",\n\t\t\t\tinvoked: config.invoked ?? \"Loaded\",\n\t\t\t\tautoHeight: resource.autoHeight,\n\t\t\t})\n\t\t: undefined;\n\n\treturn {\n\t\tid,\n\t\ttitle,\n\t\tdescription,\n\n\t\tasync register(server: McpServer): Promise<void> {\n\t\t\tserver.registerTool(\n\t\t\t\tid,\n\t\t\t\t{\n\t\t\t\t\ttitle,\n\t\t\t\t\tdescription,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tannotations,\n\t\t\t\t\t...(toolMeta && { _meta: toolMeta }),\n\t\t\t\t},\n\t\t\t\t(async (args: ShapeOutput<TInput>, extra: unknown) => {\n\t\t\t\t\tconst requestExtra = extra as RequestHandlerExtra<\n\t\t\t\t\t\tServerRequest,\n\t\t\t\t\t\tServerNotification\n\t\t\t\t\t>;\n\t\t\t\t\tconst _meta: Record<string, unknown> = requestExtra._meta ?? {};\n\n\t\t\t\t\tconst result = await handler(args, { extra: { _meta } });\n\n\t\t\t\t\t// Widget tool: return structuredContent + widget metadata\n\t\t\t\t\tif (resource && result.data) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: result.text }],\n\t\t\t\t\t\t\tstructuredContent: result.data,\n\t\t\t\t\t\t\t_meta: {\n\t\t\t\t\t\t\t\t...toolMeta,\n\t\t\t\t\t\t\t\t..._meta,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Plain tool: return text content only\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: result.text }],\n\t\t\t\t\t};\n\t\t\t\t}) as unknown as ToolCallback<TInput>,\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * Registers multiple tools on the server\n */\nexport async function registerTools(\n\tserver: McpServer,\n\ttools: RegisteredTool[],\n): Promise<void> {\n\tawait Promise.all(tools.map((t) => t.register(server)));\n}\n"],"mappings":"AAWO,SAASA,GAAiC,CAChD,OAAI,OAAO,OAAW,KAAe,WAAY,OACzC,SAED,UACR,CAKO,SAASC,GAAoB,CACnC,OAAOD,EAAe,IAAM,QAC7B,CAKO,SAASE,GAAqB,CACpC,OAAOF,EAAe,IAAM,UAC7B,CCrBO,IAAMG,EAAQ,YACRC,EAAM,UAMbC,EAAY,OAAO,IAAI,yBAAyB,EAChDC,EAAS,OAAO,IAAI,sBAAsB,EA2BzC,SAASC,EAAUC,EAKN,CACnB,MAAO,CAAE,OAAQH,EAAW,GAAGG,CAAO,CACvC,CAKO,SAASC,EACfC,EACAF,EAIe,CACf,MAAO,CAAE,OAAQF,EAAQ,SAAAI,EAAU,GAAGF,CAAO,CAC9C,CAEO,SAASG,EAAYC,EAA0C,CACrE,OACC,OAAOA,GAAU,UACjBA,IAAU,MACV,WAAYA,GACXA,EAA0B,SAAWP,CAExC,CAEO,SAASQ,EAASD,EAAuC,CAC/D,OACC,OAAOA,GAAU,UACjBA,IAAU,MACV,WAAYA,GACXA,EAAuB,SAAWN,CAErC,CC5EA,OAAS,KAAAQ,MAAS,MCCX,IAAMC,EAAmB,sBACnBC,EAAgB,4BAIhBC,EAAY,MACxBC,EACAC,IACqB,CACrB,IAAMC,EAAiBF,EAAQ,SAAS,GAAG,EAAIA,EAAQ,MAAM,EAAG,EAAE,EAAIA,EAEtE,OAAO,MADQ,MAAM,MAAM,GAAGE,CAAc,GAAGD,CAAI,EAAE,GACjC,KAAK,CAC1B,EAYO,SAASE,EAAwBC,EAKjB,CACtB,MAAO,CACN,2BAA4BA,EAAO,YACnC,6BAA8BA,EAAO,cACrC,sBAAuBA,EAAO,aAC9B,GAAIA,EAAO,WAAa,CAAE,mBAAoBA,EAAO,SAAU,CAChE,CACD,CAkBO,SAASC,EAAyBD,EAIjB,CACvB,IAAME,EAAMF,EAAO,UAChB,CACA,eAAgBA,EAAO,UAAU,gBACjC,gBAAiBA,EAAO,UAAU,iBAClC,aAAcA,EAAO,UAAU,cAC/B,gBAAiBA,EAAO,UAAU,gBACnC,EACC,OAEH,MAAO,CACN,GAAI,CACH,GAAIE,GAAO,CAAE,IAAAA,CAAI,EACjB,GAAIF,EAAO,gBAAkB,QAAa,CACzC,cAAeA,EAAO,aACvB,CACD,CACD,CACD,CAIO,SAASG,EAAcH,EAM3B,CACF,MAAO,CAEN,wBAAyBA,EAAO,kBAChC,iCAAkCA,EAAO,SACzC,gCAAiCA,EAAO,QACxC,0BAA2B,GAC3B,gCAAiC,GAEjC,GAAI,CACH,YAAaA,EAAO,eACpB,GAAIA,EAAO,YAAc,CAAE,WAAY,EAAK,CAC7C,CACD,CACD,CDzDA,SAASI,EAAiBC,EAA2B,CACpD,IAAMC,EAAOD,EAAO,aAAe,GAC7BE,EACLF,EAGC,MAAM,IAER,GAAIE,GAAK,OAAS,QAAUA,EAAI,QAAS,CACxC,IAAMC,EAAO,OAAO,KAAKD,EAAI,OAAO,EAClC,IAAKE,GAAM,IAAIA,CAAC,GAAG,EACnB,KAAK,KAAK,EACZ,OAAOH,EAAO,GAAGE,CAAI,WAAMF,CAAI,GAAKE,CACrC,CAEA,OAAOF,CACR,CAEA,SAASI,EAAkBC,EAA4B,CACtD,IAAMC,EAAQ,CACb,GACA,6BACA,GACA,uFACA,GACA,0EACA,4EACA,4EACA,gCACA,yFACD,EAEA,GAAID,EAAO,OAAQ,CAClB,IAAME,EAAY,OAAO,QAAQF,EAAO,MAAM,EAC5C,IAAI,CAAC,CAACG,EAAKT,CAAM,IAAM,CACvB,IAAMU,EAAOX,EAAiBC,CAAM,EACpC,OAAOU,EAAO,KAAKD,CAAG,OAAOC,CAAI,IAAM,KAAKD,CAAG,IAChD,CAAC,EACA,KAAK,IAAI,EACXF,EAAM,KAAK,oBAAoBC,CAAS,GAAG,CAC5C,CAEA,OAAAD,EAAM,KACL,iEACA,oFACA,wFACA,6BACA,2FACA,qCACA,uFACA,wFACA,gGACA,wCACA,uEACA,kEACA,GACA,8DACA,sGACD,EAEOA,EAAM,KAAK;AAAA,CAAI,CACvB,CAMA,eAAeI,EACdC,EACAC,EACkB,CAClB,OAAID,EAAK,OAAS,SAAiBA,EAAK,GACjCA,EAAK,UAAUC,CAAK,CAC5B,CAMA,eAAeC,EACdC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EAC2B,CAC3B,IAAIC,EAAcP,EACdF,EAAQ,CAAE,GAAGG,CAAa,EAGxBO,EAAiB,GACnBC,EAAa,EAEjB,KAAOA,IAAeD,GAAgB,CAErC,GAAID,IAAgBG,EACnB,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,WACR,MAAAZ,CACD,CAAC,EACD,KAAM,CAAE,OAAQ,WAAY,MAAAA,CAAM,CACnC,EAGD,IAAMa,EAAUT,EAAM,IAAIK,CAAW,EACrC,GAAI,CAACI,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,kBAAkBJ,CAAW,GACrC,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAGD,GAAI,CACH,IAAMK,EAAS,MAAMD,EAAQb,EAAOQ,CAAI,EAGxC,GAAIO,EAAYD,CAAM,EAAG,CAExB,IAAME,EAAgBhB,EAAMc,EAAO,KAAqB,EACxD,GAECE,GAAkB,MAClBA,IAAkB,GACjB,CACD,IAAMjB,EAAOO,EAAM,IAAIG,CAAW,EAClC,GAAI,CAACV,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,+BAA+BU,CAAW,GAClD,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAEDA,EAAc,MAAMX,EAAgBC,EAAMC,CAAK,EAC/C,QACD,CAEA,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,YACR,KAAMS,EACN,SAAUK,EAAO,SACjB,MAAOA,EAAO,MACd,YAAaA,EAAO,YACpB,GAAIA,EAAO,QAAU,CAAE,QAASA,EAAO,OAAQ,EAAI,CAAC,EACpD,MAAAd,CACD,CAAC,EACD,KAAM,CAAE,OAAQ,YAAa,KAAMS,EAAa,MAAAT,CAAM,CACvD,CACD,CAGA,GAAIiB,EAASH,CAAM,EAAG,CAErB,IAAMI,EAAYb,EAAY,IAAII,CAAW,GAAG,MAChD,GAAIS,EAAW,CACd,IAAMF,EAAgBhB,EAAMkB,CAAyB,EACrD,GAECF,GAAkB,MAClBA,IAAkB,GACjB,CACD,IAAMjB,EAAOO,EAAM,IAAIG,CAAW,EAClC,GAAI,CAACV,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,+BAA+BU,CAAW,GAClD,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAEDA,EAAc,MAAMX,EAAgBC,EAAMC,CAAK,EAC/C,QACD,CACD,CAEA,IAAMmB,EAAWL,EAAO,SACxB,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,SACR,KAAML,EACN,SAAUU,EAAS,GACnB,YAAaL,EAAO,YACpB,MAAAd,CACD,CAAC,EACD,KAAM,CACL,GAAGc,EAAO,KACV,OAAQ,CACP,OAAAP,EACA,KAAME,EACN,MAAAT,CACD,CACD,EACA,WAAYoB,EAAc,CACzB,kBAAmBD,EAAS,UAC5B,eAAgBA,EAAS,OACzB,SAAU,aACV,QAAS,SACT,WAAYA,EAAS,UACtB,CAAC,CACF,CACD,CAGAnB,EAAQ,CAAE,GAAGA,EAAO,GAAGc,CAAO,EAE9B,IAAMf,EAAOO,EAAM,IAAIG,CAAW,EAClC,GAAI,CAACV,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,+BAA+BU,CAAW,GAClD,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAEDA,EAAc,MAAMX,EAAgBC,EAAMC,CAAK,CAChD,OAASqB,EAAO,CACf,IAAMC,EAAUD,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACrE,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,KAAMZ,EACN,MAAOa,EACP,MAAAtB,CACD,CAAC,EACD,KAAM,CAAE,OAAQ,QAAS,MAAOsB,CAAQ,CACzC,CACD,CACD,CAEA,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,2DACR,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,CACD,CAMA,IAAMC,EAAc,CACnB,OAAQC,EACN,KAAK,CAAC,QAAS,WAAY,eAAe,CAAC,EAC3C,SACA,qHACD,EACD,KAAMA,EACJ,OAAO,EACP,SAAS,EACT,SAAS,gDAAgD,EAC3D,MAAOA,EACL,OAAOA,EAAE,OAAO,EAAGA,EAAE,QAAQ,CAAC,EAC9B,SAAS,EACT,SAAS,iDAA4C,EACvD,OAAQA,EACN,OAAO,EACP,SAAS,EACT,SAAS,yCAAyC,EACpD,aAAcA,EACZ,OAAOA,EAAE,OAAO,EAAGA,EAAE,QAAQ,CAAC,EAC9B,SAAS,EACT,SAAS,oCAAoC,EAC/C,aAAcA,EACZ,OAAOA,EAAE,OAAO,EAAGA,EAAE,QAAQ,CAAC,EAC9B,SAAS,EACT,SACA,iFACD,CACF,EAEO,SAASC,EACfC,EACiB,CACjB,GAAM,CAAE,OAAAjC,EAAQ,MAAAW,EAAO,YAAAC,EAAa,MAAAC,CAAM,EAAIoB,EACxCC,EAAWnC,EAAkBC,CAAM,EACnCmC,EAAkB,GAAGnC,EAAO,WAAW;AAAA,EAAKkC,CAAQ,GAItDE,EACJ,QAAWC,KAAMzB,EAAY,OAAO,EACnC,GAAIyB,EAAG,SAAU,CAChBD,EAAgBC,EAAG,SACnB,KACD,CAED,IAAMC,EAAWF,EACdT,EAAc,CACd,kBAAmBS,EAAc,UACjC,eAAgBA,EAAc,OAC9B,SAAU,aACV,QAAS,SACT,WAAYA,EAAc,UAC3B,CAAC,EACA,OAEH,eAAeG,EACdC,EACAzB,EAC2B,CAC3B,IAAMR,EAASiC,EAAK,OAAS,CAAC,EAE9B,GAAIA,EAAK,SAAW,QAAS,CAC5B,IAAMC,EAAY5B,EAAM,IAAI6B,CAAK,EACjC,GAAI,CAACD,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,eACR,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAID,IAAME,EACLH,EAAK,aAAe,CAAE,GAAGjC,EAAO,GAAGiC,EAAK,YAAa,EAAIjC,EAGpDqC,EAAY,MAAMvC,EAAgBoC,EAAWE,CAAU,EAC7D,OAAOnC,EACNoC,EACAD,EACAhC,EACAC,EACAC,EACAb,EAAO,GACPe,CACD,CACD,CAEA,GAAIyB,EAAK,SAAW,WAAY,CAC/B,GAAI,CAACA,EAAK,KACT,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,oCACR,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAID,IAAIK,EAAe,CAAE,GAAGtC,CAAM,EAC9B,GAAIiC,EAAK,OAAQ,CAChB,IAAMpB,EAAUT,EAAM,IAAI6B,EAAK,IAAI,EACnC,GAAIpB,EACH,GAAI,CACH,IAAMC,EAAS,MAAMD,EAAQyB,EAAc9B,CAAI,EAC3CO,EAAYD,CAAM,GAAKA,EAAO,QACjCwB,EAAe,CACd,GAAGA,EACH,CAACxB,EAAO,KAAK,EAAGmB,EAAK,MACtB,EAEF,MAAQ,CAER,CAEF,CAGA,IAAMlC,EAAOO,EAAM,IAAI2B,EAAK,IAAI,EAChC,GAAI,CAAClC,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,sBAAsBkC,EAAK,IAAI,GACvC,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAED,IAAMM,EAAW,MAAMzC,EAAgBC,EAAMuC,CAAY,EACzD,OAAOrC,EACNsC,EACAD,EACAlC,EACAC,EACAC,EACAb,EAAO,GACPe,CACD,CACD,CAEA,GAAIyB,EAAK,SAAW,gBAAiB,CACpC,GAAI,CAACA,EAAK,KACT,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,yCACR,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAKD,IAAIO,EAAwCP,EAAK,cAAgB,CAAC,EAClE,GAAIA,EAAK,SAAW,QAAa,OAAO,KAAKO,CAAY,EAAE,SAAW,EAAG,CACxE,IAAMtB,EAAYb,EAAY,IAAI4B,EAAK,IAAI,GAAG,MAC1Cf,IACHsB,EAAe,CAAE,CAACtB,CAAS,EAAGe,EAAK,MAAO,EAE5C,CACA,IAAMK,EAAe,CACpB,GAAGtC,EACH,GAAGwC,CACJ,EAGMzC,EAAOO,EAAM,IAAI2B,EAAK,IAAI,EAChC,GAAI,CAAClC,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,sBAAsBkC,EAAK,IAAI,GACvC,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAED,IAAMM,EAAW,MAAMzC,EAAgBC,EAAMuC,CAAY,EACzD,OAAOrC,EACNsC,EACAD,EACAlC,EACAC,EACAC,EACAb,EAAO,GACPe,CACD,CACD,CAEA,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,oBAAoByB,EAAK,MAAM,GACvC,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,CACD,CAEA,MAAO,CACN,GAAIxC,EAAO,GACX,MAAOA,EAAO,MACd,YAAamC,EAEb,MAAM,SAASa,EAAkC,CAChDA,EAAO,aACNhD,EAAO,GACP,CACC,MAAOA,EAAO,MACd,YAAamC,EACb,YAAAL,EACA,YAAa9B,EAAO,YACpB,GAAIsC,GAAY,CAAE,MAAOA,CAAS,CACnC,GACC,MAAOE,EAAqBS,IAAmB,CAK/C,IAAMC,EAJeD,EAI+B,OAAS,CAAC,EAExD5B,EAAS,MAAMkB,EAAeC,EAAMU,CAAK,EAG/C,OAAI7B,EAAO,WACH,CACN,QAAS,CAAC,CAAE,KAAM,OAAiB,KAAMA,EAAO,IAAK,CAAC,EACtD,kBAAmBA,EAAO,KAC1B,MAAO,CACN,GAAGA,EAAO,WACV,GAAG6B,CACJ,CACD,EAIM,CACN,QAAS,CAAC,CAAE,KAAM,OAAiB,KAAM7B,EAAO,IAAK,CAAC,CACvD,CACD,EACD,CACD,CACD,CACD,CEtgBO,IAAM8B,EAAN,KAAyD,CACvD,MAAQ,IAAI,IACZ,YAAc,IAAI,IAClB,MAAQ,IAAI,IACZ,OAER,YAAYC,EAAoB,CAC/B,KAAK,OAASA,CACf,CAcA,QACCC,EACAC,EACAC,EACO,CACP,GAAIF,IAASG,GAASH,IAASI,EAC9B,MAAM,IAAI,MACT,IAAIJ,CAAI,wDACT,EAED,GAAI,KAAK,MAAM,IAAIA,CAAI,EACtB,MAAM,IAAI,MAAM,SAASA,CAAI,kBAAkB,EAGhD,IAAIK,EACAC,EAAiC,CAAC,EAEtC,GAAI,OAAOL,GAAoB,WAC9BI,EAAUJ,MACJ,CACN,GAAI,CAACC,EACJ,MAAM,IAAI,MACT,YAAYF,CAAI,8DACjB,EAEDK,EAAUH,EACVI,EAAaL,CACd,CAEA,YAAK,MAAM,IAAID,EAAMK,CAAO,EAC5B,KAAK,YAAY,IAAIL,EAAMM,CAAU,EAC9B,IACR,CAQA,QAAQC,EAAcC,EAAkB,CACvC,GAAI,KAAK,MAAM,IAAID,CAAI,EACtB,MAAM,IAAI,MACT,SAASA,CAAI,uEACd,EAED,YAAK,MAAM,IAAIA,EAAM,CAAE,KAAM,SAAU,GAAAC,CAAG,CAAC,EACpC,IACR,CAOA,mBAAmBD,EAAcE,EAAsC,CACtE,GAAI,KAAK,MAAM,IAAIF,CAAI,EACtB,MAAM,IAAI,MAAM,SAASA,CAAI,iCAAiC,EAE/D,YAAK,MAAM,IAAIA,EAAM,CAAE,KAAM,cAAe,UAAAE,CAAU,CAAC,EAChD,IACR,CAOA,SAA0B,CACzB,YAAK,SAAS,EAEPC,EAAoB,CAC1B,OAAQ,KAAK,OACb,MAAO,IAAI,IAAI,KAAK,KAAK,EACzB,YAAa,IAAI,IAAI,KAAK,WAAW,EACrC,MAAO,IAAI,IAAI,KAAK,KAAK,CAC1B,CAAC,CACF,CAEQ,UAAiB,CAExB,GAAI,CAAC,KAAK,MAAM,IAAIP,CAAK,EACxB,MAAM,IAAI,MACT,sFACD,EAID,IAAMQ,EAAY,KAAK,MAAM,IAAIR,CAAK,EACtC,GACCQ,GAAW,OAAS,UACpBA,EAAU,KAAOP,GACjB,CAAC,KAAK,MAAM,IAAIO,EAAU,EAAE,EAE5B,MAAM,IAAI,MACT,6CAA6CA,EAAU,EAAE,GAC1D,EAID,OAAW,CAACJ,EAAMK,CAAI,IAAK,KAAK,MAAO,CACtC,GAAIL,IAASJ,GAAS,CAAC,KAAK,MAAM,IAAII,CAAI,EACzC,MAAM,IAAI,MAAM,iCAAiCA,CAAI,GAAG,EAEzD,GACCK,EAAK,OAAS,UACdA,EAAK,KAAOR,GACZ,CAAC,KAAK,MAAM,IAAIQ,EAAK,EAAE,EAEvB,MAAM,IAAI,MACT,cAAcL,CAAI,oCAAoCK,EAAK,EAAE,GAC9D,CAEF,CAGA,OAAW,CAACZ,CAAI,IAAK,KAAK,MACzB,GAAI,CAAC,KAAK,MAAM,IAAIA,CAAI,EACvB,MAAM,IAAI,MACT,SAASA,CAAI,kDAAkDA,CAAI,mCAAmCA,CAAI,SAC3G,CAGH,CACD,ECtJO,SAASa,EACfC,EACqB,CACrB,OAAO,IAAIC,EAAmBD,CAAM,CACrC,CCHO,SAASE,EAAeC,EAA4C,CAC1E,GAAM,CACL,GAAAC,EACA,MAAAC,EACA,YAAAC,EACA,QAAAC,EACA,SAAAC,EACA,aAAAC,EACA,cAAAC,EAAgB,GAChB,WAAAC,EACA,UAAAC,CACD,EAAIT,EAEEU,EAAY,yBAAyBT,CAAE,QACvCU,EAAS,yBAAyBV,CAAE,QAGtCW,EAAsC,KACpCC,EAAU,KACVD,IAAaA,EAAcE,EAAUV,EAASC,CAAQ,GACpDO,GAIFG,EAAgBZ,EAEtB,eAAea,EAASC,EAAkC,CACzD,IAAMC,EAAO,MAAML,EAAQ,EAG3BI,EAAO,iBACN,GAAGhB,CAAE,iBACLS,EACA,CACC,MAAAR,EACA,YAAaa,EACb,SAAUI,EACV,MAAO,CACN,2BAA4BJ,EAC5B,6BAA8BR,CAC/B,CACD,EACA,MAAOa,IAAS,CACf,SAAU,CACT,CACC,IAAKA,EAAI,KACT,SAAUD,EACV,KAAMD,EACN,MAAOG,EAAwB,CAC9B,YAAaN,EACb,cAAAR,EACA,aAAAD,EACA,UAAAG,CACD,CAAC,CACF,CACD,CACD,EACD,EAGAQ,EAAO,iBACN,GAAGhB,CAAE,cACLU,EACA,CACC,MAAAT,EACA,YAAaa,EACb,SAAUO,EACV,MAAO,CACN,GAAI,CACH,cAAAf,CACD,CACD,CACD,EACA,MAAOa,IAAS,CACf,SAAU,CACT,CACC,IAAKA,EAAI,KACT,SAAUE,EACV,KAAMJ,EACN,MAAOK,EAAyB,CAC/B,YAAaR,EACb,cAAAR,EACA,UAAAE,CACD,CAAC,CACF,CACD,CACD,EACD,CACD,CAEA,MAAO,CACN,GAAAR,EACA,MAAAC,EACA,YAAAC,EACA,UAAAO,EACA,OAAAC,EACA,WAAAH,EACA,SAAAQ,CACD,CACD,CChFO,SAASQ,EACfC,EACAC,EACiB,CACjB,GAAM,CAAE,SAAAC,EAAU,YAAAC,EAAa,YAAAC,EAAa,YAAAC,CAAY,EAAIL,EAEtDM,EAAKN,EAAO,IAAME,GAAU,GAC5BK,EAAQP,EAAO,OAASE,GAAU,MAExC,GAAI,CAACI,EACJ,MAAM,IAAI,MACT,2DACD,EAED,GAAI,CAACC,EACJ,MAAM,IAAI,MACT,8DACD,EAID,IAAMC,EAAWN,EACdO,EAAc,CACd,kBAAmBP,EAAS,UAC5B,eAAgBA,EAAS,OACzB,SAAUF,EAAO,UAAY,aAC7B,QAASA,EAAO,SAAW,SAC3B,WAAYE,EAAS,UACtB,CAAC,EACA,OAEH,MAAO,CACN,GAAAI,EACA,MAAAC,EACA,YAAAJ,EAEA,MAAM,SAASO,EAAkC,CAChDA,EAAO,aACNJ,EACA,CACC,MAAAC,EACA,YAAAJ,EACA,YAAAC,EACA,YAAAC,EACA,GAAIG,GAAY,CAAE,MAAOA,CAAS,CACnC,GACC,MAAOG,EAA2BC,IAAmB,CAKrD,IAAMC,EAJeD,EAI+B,OAAS,CAAC,EAExDE,EAAS,MAAMb,EAAQU,EAAM,CAAE,MAAO,CAAE,MAAAE,CAAM,CAAE,CAAC,EAGvD,OAAIX,GAAYY,EAAO,KACf,CACN,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAMA,EAAO,IAAK,CAAC,EAC7C,kBAAmBA,EAAO,KAC1B,MAAO,CACN,GAAGN,EACH,GAAGK,CACJ,CACD,EAIM,CACN,QAAS,CAAC,CAAE,KAAM,OAAiB,KAAMC,EAAO,IAAK,CAAC,CACvD,CACD,EACD,CACD,CACD,CACD,CAKA,eAAsBC,EACrBL,EACAM,EACgB,CAChB,MAAM,QAAQ,IAAIA,EAAM,IAAKC,GAAMA,EAAE,SAASP,CAAM,CAAC,CAAC,CACvD","names":["detectPlatform","isOpenAI","isMCPApps","START","END","INTERRUPT","WIDGET","interrupt","config","showWidget","resource","isInterrupt","value","isWidget","z","MIME_TYPE_OPENAI","MIME_TYPE_MCP","fetchHtml","baseUrl","path","normalizedBase","buildOpenAIResourceMeta","config","buildMcpAppsResourceMeta","csp","buildToolMeta","describeZodField","schema","desc","def","vals","v","buildFlowProtocol","config","lines","fieldList","key","info","resolveNextNode","edge","state","executeFrom","startNodeName","initialState","nodes","nodeConfigs","edges","flowId","meta","currentNode","MAX_ITERATIONS","iterations","END","handler","result","isInterrupt","existingValue","isWidget","nodeField","resource","buildToolMeta","error","message","inputSchema","z","compileFlow","input","protocol","fullDescription","firstResource","nc","toolMeta","handleToolCall","args","startEdge","START","startState","firstNode","updatedState","nextNode","widgetUpdate","server","extra","_meta","StateGraph","config","name","configOrHandler","maybeHandler","START","END","handler","nodeConfig","from","to","condition","compileFlow","startEdge","edge","createFlow","config","StateGraph","createResource","config","id","title","description","baseUrl","htmlPath","widgetDomain","prefersBorder","autoHeight","widgetCSP","openaiUri","mcpUri","htmlPromise","getHtml","fetchHtml","uiDescription","register","server","html","MIME_TYPE_OPENAI","uri","buildOpenAIResourceMeta","MIME_TYPE_MCP","buildMcpAppsResourceMeta","createTool","config","handler","resource","description","inputSchema","annotations","id","title","toolMeta","buildToolMeta","server","args","extra","_meta","result","registerTools","tools","t"]}
|
package/dist/mcp/react.d.ts
CHANGED
|
@@ -152,6 +152,12 @@ interface UnifiedWidgetClient {
|
|
|
152
152
|
* On MCP Apps, this establishes the postMessage connection.
|
|
153
153
|
*/
|
|
154
154
|
connect(): Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* Close the connection to the host and clean up resources.
|
|
157
|
+
* On OpenAI, this is a no-op.
|
|
158
|
+
* On MCP Apps, this closes the transport and removes event listeners.
|
|
159
|
+
*/
|
|
160
|
+
close(): Promise<void>;
|
|
155
161
|
/**
|
|
156
162
|
* Get the tool output (structured content returned by the tool handler).
|
|
157
163
|
* This is the main data source for widget rendering.
|
package/dist/mcp/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{a as $}from"../chunk-DGSC74SV.js";import{b as T}from"../chunk-ZUGQBRJF.js";import{Fragment as Se,jsx as E,jsxs as Te}from"react/jsx-runtime";function Ce({baseUrl:e}){return Te(Se,{children:[E("base",{href:e}),E("script",{children:`window.innerBaseUrl = ${JSON.stringify(e)}`}),E("script",{children:'window.__isChatGptApp = typeof window.openai !== "undefined";'}),E("script",{children:"("+(()=>{let t=window.innerBaseUrl,
|
|
2
|
+
import{a as $}from"../chunk-DGSC74SV.js";import{b as T}from"../chunk-ZUGQBRJF.js";import{Fragment as Se,jsx as E,jsxs as Te}from"react/jsx-runtime";function Ce({baseUrl:e}){return Te(Se,{children:[E("base",{href:e}),E("script",{children:`window.innerBaseUrl = ${JSON.stringify(e)}`}),E("script",{children:'window.__isChatGptApp = typeof window.openai !== "undefined";'}),E("script",{children:"("+(()=>{let t=window.innerBaseUrl,s=document.documentElement;new MutationObserver(m=>{m.forEach(n=>{if(n.type==="attributes"&&n.target===s){let i=n.attributeName;i&&i!=="suppresshydrationwarning"&&i!=="class"&&i!=="style"&&s.removeAttribute(i)}})}).observe(s,{attributes:!0,attributeOldValue:!0});let c=history.replaceState;history.replaceState=(m,n,i)=>{let r=new URL(i??"",window.location.href),g=r.pathname+r.search+r.hash;c.call(history,n,g)};let p=history.pushState;history.pushState=(m,n,i)=>{let r=new URL(i??"",window.location.href),g=r.pathname+r.search+r.hash;p.call(history,n,g)};let f=new URL(t).origin,h=window.self!==window.top;if(window.addEventListener("click",m=>{let n=m?.target?.closest("a");if(!n||!n.href)return;let i=new URL(n.href,window.location.href);if(i.origin!==window.location.origin&&i.origin!==f)try{window.openai&&(window.openai?.openExternal({href:n.href}),m.preventDefault())}catch{console.warn("openExternal failed, likely not in OpenAI client")}},!0),h&&window.location.origin!==f){let m=window.fetch;window.fetch=(n,i)=>{let r;if(typeof n=="string"||n instanceof URL?r=new URL(n,window.location.href):r=new URL(n.url,window.location.href),r.origin===f)return typeof n=="string"||n instanceof URL?n=r.toString():n=new Request(r.toString(),n),m.call(window,n,{...i,mode:"cors"});if(r.origin===window.location.origin){let g=new URL(t);return g.pathname=r.pathname,g.search=r.search,g.hash=r.hash,r=g,typeof n=="string"||n instanceof URL?n=r.toString():n=new Request(r.toString(),n),m.call(window,n,{...i,mode:"cors"})}return m.call(window,n,i)}}}).toString()+")()"})]})}import{useCallback as w,useEffect as M,useRef as K,useState as b}from"react";var J={theme:"dark",userAgent:{device:{type:"desktop"},capabilities:{hover:!0,touch:!1}},locale:"en",maxHeight:800,displayMode:"inline",safeArea:{insets:{top:0,bottom:0,left:0,right:0}},toolInput:{},toolOutput:null,toolResponseMetadata:null,widgetState:null},N={...J};function R(e){typeof window>"u"||window.openai||(N={...J,toolOutput:e??null},window.openai={...N,requestDisplayMode:async({mode:t})=>(v("displayMode",t),{mode:t}),callTool:async(t,s)=>(console.log(`[DevMode] callTool: ${t}`,s),{result:JSON.stringify({mock:!0,tool:t,args:s})}),sendFollowUpMessage:async({prompt:t})=>{console.log(`[DevMode] sendFollowUpMessage: ${t}`)},openExternal:({href:t})=>{console.log(`[DevMode] openExternal: ${t}`),window.open(t,"_blank")},setWidgetState:async t=>{v("widgetState",t)}},window.dispatchEvent(new T({globals:N})))}function v(e,t){typeof window>"u"||!window.openai||(N[e]=t,window.openai[e]=t,window.dispatchEvent(new T({globals:{[e]:t}})))}function U(){return{...N}}function W(e){v("toolOutput",e)}function O(e){v("displayMode",e)}function A(e){v("theme",e)}import{Fragment as B,jsx as o,jsxs as l}from"react/jsx-runtime";var L=150;function q({className:e}){return l("svg",{className:e,viewBox:"0 0 24 24",fill:"none",role:"img","aria-label":"Dev Controls",children:[o("rect",{x:"4",y:"4",width:"10",height:"10",rx:"2",stroke:"currentColor",strokeWidth:"1.5"}),o("rect",{x:"10",y:"10",width:"10",height:"10",rx:"2",stroke:"currentColor",strokeWidth:"1.5",fill:"currentColor",fillOpacity:"0.15"})]})}function Ne({className:e}){return o("svg",{"aria-hidden":"true",className:e,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:o("path",{d:"M18 6L6 18M6 6l12 12"})})}function Re({className:e}){return o("svg",{"aria-hidden":"true",className:e,viewBox:"0 0 16 16",fill:"none",children:o("rect",{x:"2",y:"4",width:"12",height:"8",rx:"1.5",stroke:"currentColor",strokeWidth:"1.25"})})}function We({className:e}){return l("svg",{"aria-hidden":"true",className:e,viewBox:"0 0 16 16",fill:"none",children:[o("rect",{x:"1.5",y:"3",width:"13",height:"10",rx:"1.5",stroke:"currentColor",strokeWidth:"1.25"}),o("rect",{x:"8.5",y:"7",width:"5",height:"4",rx:"0.75",fill:"currentColor"})]})}function Oe({className:e}){return o("svg",{"aria-hidden":"true",className:e,viewBox:"0 0 16 16",fill:"none",children:o("path",{d:"M2 5.5V3.5C2 2.95 2.45 2.5 3 2.5H5.5M10.5 2.5H13C13.55 2.5 14 2.95 14 3.5V5.5M14 10.5V12.5C14 13.05 13.55 13.5 13 13.5H10.5M5.5 13.5H3C2.45 13.5 2 13.05 2 12.5V10.5",stroke:"currentColor",strokeWidth:"1.25",strokeLinecap:"round",strokeLinejoin:"round"})})}function Ae({className:e}){return l("svg",{"aria-hidden":"true",className:e,viewBox:"0 0 16 16",fill:"none",children:[o("circle",{cx:"8",cy:"8",r:"2.5",stroke:"currentColor",strokeWidth:"1.25"}),o("path",{d:"M8 2v1.5M8 12.5V14M2 8h1.5M12.5 8H14M4.11 4.11l1.06 1.06M10.83 10.83l1.06 1.06M4.11 11.89l1.06-1.06M10.83 5.17l1.06-1.06",stroke:"currentColor",strokeWidth:"1.25",strokeLinecap:"round"})]})}function De({className:e}){return o("svg",{"aria-hidden":"true",className:e,viewBox:"0 0 16 16",fill:"none",children:o("path",{d:"M13.5 9.5a5.5 5.5 0 01-7-7 5.5 5.5 0 107 7z",stroke:"currentColor",strokeWidth:"1.25",strokeLinecap:"round",strokeLinejoin:"round"})})}function Ie({className:e}){return l("svg",{"aria-hidden":"true",className:e,viewBox:"0 0 16 16",fill:"none",children:[o("path",{d:"M2.5 8a5.5 5.5 0 019.37-3.9M13.5 8a5.5 5.5 0 01-9.37 3.9",stroke:"currentColor",strokeWidth:"1.25",strokeLinecap:"round"}),o("path",{d:"M12.5 2v3h-3M3.5 14v-3h3",stroke:"currentColor",strokeWidth:"1.25",strokeLinecap:"round",strokeLinejoin:"round"})]})}var Ee=`
|
|
3
3
|
@keyframes devPanelSlideIn {
|
|
4
4
|
0% {
|
|
5
5
|
opacity: 0;
|
|
@@ -47,22 +47,22 @@ import{a as $}from"../chunk-DGSC74SV.js";import{b as T}from"../chunk-ZUGQBRJF.js
|
|
|
47
47
|
.dev-json-editor::-webkit-scrollbar-thumb:hover {
|
|
48
48
|
background: rgba(255, 255, 255, 0.15);
|
|
49
49
|
}
|
|
50
|
-
`;function Y({defaultProps:e,widgetPaths:t,children:
|
|
50
|
+
`;function Y({defaultProps:e,widgetPaths:t,children:s}){let[u,c]=b(!1),[p,f]=b(!1);return M(()=>{if(new URLSearchParams(window.location.search).get("platform")==="mcp-apps"){c(!0);return}if(t&&t.length>0){let m=window.location.pathname,n=t.some(i=>m===i||m.startsWith(`${i}/`));f(n),n&&R(e)}else R(e),f(!0);c(!0)},[e,t]),u?p?l(B,{children:[o(Ue,{children:s}),o(Le,{defaultProps:e})]}):o(B,{children:s}):null}function Ue({children:e}){return o("div",{className:"min-h-screen bg-[#212121] flex items-center justify-center p-8",children:o("div",{className:"relative w-full max-w-md overflow-hidden rounded-2xl sm:rounded-3xl border border-[#414141]",style:{boxShadow:"0px 0px 0px 1px #414141, 0px 4px 14px rgba(0,0,0,0.24)"},children:e})})}function V({options:e,value:t,onChange:s}){let u=e.findIndex(c=>c.value===t);return l("div",{className:"relative flex p-0.5 rounded-lg",style:{background:"rgba(255, 255, 255, 0.04)",border:"1px solid rgba(255, 255, 255, 0.06)"},children:[o("div",{className:"absolute top-0.5 bottom-0.5 rounded-md transition-transform duration-150 ease-out",style:{width:`calc(${100/e.length}% - 2px)`,left:"2px",transform:`translateX(calc(${u*100}% + ${u*2}px))`,background:"rgba(255, 255, 255, 0.1)"}}),e.map(c=>l("button",{type:"button",onClick:()=>s(c.value),className:`
|
|
51
51
|
relative z-10 flex-1 flex items-center justify-center gap-1.5 px-2 py-1.5
|
|
52
52
|
text-xs font-medium rounded-md transition-colors duration-150
|
|
53
|
-
${t===
|
|
54
|
-
`,children:[
|
|
53
|
+
${t===c.value?"text-white":"text-gray-400 hover:text-gray-300"}
|
|
54
|
+
`,children:[c.icon,o("span",{className:"capitalize",children:c.label})]},c.value))]})}function Le({defaultProps:e}){let[t,s]=b(()=>typeof window>"u"?!1:localStorage.getItem("dev-controls-open")==="true"),[u,c]=b(!1),[p,f]=b(t),[h,m]=b("inline"),[n,i]=b("dark"),[r,g]=b(!1),[P,z]=b(()=>JSON.stringify(e??{},null,2)),[C,H]=b(null),S=K(null),he=K(null),j=K(null);M(()=>{let a=U();m(a.displayMode),i(a.theme)},[]),M(()=>{typeof window>"u"||(window.openai||(window.openai={}),window.openai.safeArea={insets:{top:0,bottom:r?L:0,left:0,right:0}},window.dispatchEvent(new T({globals:{safeArea:window.openai.safeArea}})))},[r]),M(()=>{localStorage.setItem("dev-controls-open",String(t))},[t]);let G=w(()=>{f(!0),requestAnimationFrame(()=>{s(!0)})},[]),y=w(()=>{c(!0),s(!1),setTimeout(()=>{f(!1),c(!1)},150)},[]),F=w(()=>{t?y():G()},[t,G,y]);M(()=>{let a=x=>{(x.metaKey||x.ctrlKey)&&x.shiftKey&&x.key==="d"&&(x.preventDefault(),F()),x.key==="Escape"&&t&&y()};return window.addEventListener("keydown",a),()=>window.removeEventListener("keydown",a)},[t,F,y]),M(()=>{if(!t)return;let a=x=>{j.current&&!j.current.contains(x.target)&&y()};return document.addEventListener("mousedown",a),()=>document.removeEventListener("mousedown",a)},[t,y]);let xe=w(a=>{m(a),O(a)},[]),be=w(a=>{i(a),A(a)},[]),I=w(a=>{try{let x=JSON.parse(a);H(null),W(x)}catch{H("Invalid JSON")}},[]),we=w(a=>{z(a),S.current&&clearTimeout(S.current),S.current=setTimeout(()=>{I(a)},500)},[I]),ye=w(()=>{S.current&&clearTimeout(S.current),I(P)},[I,P]),ve=w(()=>{let a=JSON.stringify(e??{},null,2);z(a),H(null),W(e??{}),m("inline"),O("inline"),i("dark"),A("dark")},[e]),ke=[{value:"inline",label:"inline",icon:o(Re,{className:"w-3.5 h-3.5"})},{value:"pip",label:"pip",icon:o(We,{className:"w-3.5 h-3.5"})},{value:"fullscreen",label:"full",icon:o(Oe,{className:"w-3.5 h-3.5"})}],Me=[{value:"light",label:"light",icon:o(Ae,{className:"w-3.5 h-3.5"})},{value:"dark",label:"dark",icon:o(De,{className:"w-3.5 h-3.5"})}];return l(B,{children:[o("style",{dangerouslySetInnerHTML:{__html:Ee}}),l("div",{ref:j,className:"fixed bottom-4 right-4 z-[9999] font-['Inter',_system-ui,_sans-serif]",children:[l("button",{type:"button",onClick:F,className:"group relative flex items-center justify-center w-11 h-11 rounded-xl transition-all duration-200 ease-out hover:scale-105 active:scale-95",style:{background:"rgba(14, 14, 16, 0.95)",backdropFilter:"blur(16px)",WebkitBackdropFilter:"blur(16px)",border:"1px solid rgba(255, 255, 255, 0.08)",boxShadow:`
|
|
55
55
|
0 4px 12px rgba(0, 0, 0, 0.4),
|
|
56
56
|
0 0 0 1px rgba(255, 255, 255, 0.05),
|
|
57
57
|
inset 0 1px 0 rgba(255, 255, 255, 0.04)
|
|
58
|
-
`},"aria-label":"Toggle Dev Controls","aria-expanded":t,children:[o(q,{className:`w-5 h-5 transition-all duration-200 ${t?"text-indigo-400 scale-110":"text-gray-300 group-hover:text-white"}`}),o("div",{className:"absolute inset-0 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none",style:{background:"radial-gradient(circle at center, rgba(99, 102, 241, 0.15) 0%, transparent 70%)"}})]}),p&&l("div",{ref:he,className:`absolute bottom-14 right-0 w-80 ${t&&!
|
|
58
|
+
`},"aria-label":"Toggle Dev Controls","aria-expanded":t,children:[o(q,{className:`w-5 h-5 transition-all duration-200 ${t?"text-indigo-400 scale-110":"text-gray-300 group-hover:text-white"}`}),o("div",{className:"absolute inset-0 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none",style:{background:"radial-gradient(circle at center, rgba(99, 102, 241, 0.15) 0%, transparent 70%)"}})]}),p&&l("div",{ref:he,className:`absolute bottom-14 right-0 w-80 ${t&&!u?"dev-panel-enter":"dev-panel-exit"}`,style:{maxHeight:"calc(100vh - 120px)",background:"rgba(14, 14, 16, 0.92)",backdropFilter:"blur(24px)",WebkitBackdropFilter:"blur(24px)",border:"1px solid rgba(255, 255, 255, 0.06)",borderRadius:"16px",boxShadow:`
|
|
59
59
|
0 25px 50px -12px rgba(0, 0, 0, 0.6),
|
|
60
60
|
0 0 0 1px rgba(255, 255, 255, 0.05),
|
|
61
61
|
inset 0 1px 0 rgba(255, 255, 255, 0.04)
|
|
62
|
-
`},children:[l("div",{className:"flex items-center justify-between px-4 py-3",style:{borderBottom:"1px solid rgba(255, 255, 255, 0.06)"},children:[l("div",{className:"flex items-center gap-2",children:[o(q,{className:"w-4 h-4 text-gray-400"}),o("span",{className:"text-sm font-medium text-white",children:"Dev Controls"})]}),l("div",{className:"flex items-center gap-2",children:[o("span",{className:"text-[10px] font-medium text-gray-500 px-1.5 py-0.5 rounded",style:{background:"rgba(255, 255, 255, 0.04)",border:"1px solid rgba(255, 255, 255, 0.06)"},children:typeof navigator<"u"&&navigator.platform?.includes("Mac")?"\u2318\u21E7D":"Ctrl+Shift+D"}),o("button",{type:"button",onClick:y,className:"p-1 rounded-md text-gray-500 hover:text-gray-300 hover:bg-white/5 transition-colors",children:o(Ne,{className:"w-4 h-4"})})]})]}),l("div",{className:"p-4 space-y-5 overflow-y-auto",style:{maxHeight:"calc(100vh - 200px)"},children:[l("div",{children:[o("label",{htmlFor:"display-mode",className:"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2",children:"Display Mode"}),o(V,{options:ke,value:
|
|
62
|
+
`},children:[l("div",{className:"flex items-center justify-between px-4 py-3",style:{borderBottom:"1px solid rgba(255, 255, 255, 0.06)"},children:[l("div",{className:"flex items-center gap-2",children:[o(q,{className:"w-4 h-4 text-gray-400"}),o("span",{className:"text-sm font-medium text-white",children:"Dev Controls"})]}),l("div",{className:"flex items-center gap-2",children:[o("span",{className:"text-[10px] font-medium text-gray-500 px-1.5 py-0.5 rounded",style:{background:"rgba(255, 255, 255, 0.04)",border:"1px solid rgba(255, 255, 255, 0.06)"},children:typeof navigator<"u"&&navigator.platform?.includes("Mac")?"\u2318\u21E7D":"Ctrl+Shift+D"}),o("button",{type:"button",onClick:y,className:"p-1 rounded-md text-gray-500 hover:text-gray-300 hover:bg-white/5 transition-colors",children:o(Ne,{className:"w-4 h-4"})})]})]}),l("div",{className:"p-4 space-y-5 overflow-y-auto",style:{maxHeight:"calc(100vh - 200px)"},children:[l("div",{children:[o("label",{htmlFor:"display-mode",className:"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2",children:"Display Mode"}),o(V,{options:ke,value:h,onChange:xe})]}),l("div",{children:[o("label",{htmlFor:"theme",className:"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2",children:"Theme"}),o(V,{options:Me,value:n,onChange:be})]}),l("div",{children:[o("label",{htmlFor:"safe-area",className:"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2",children:"Safe Area (Chat Input)"}),l("button",{type:"button",onClick:()=>g(!r),className:`w-full flex items-center justify-between px-3 py-2.5 rounded-lg text-xs font-medium transition-all duration-150 ${r?"text-emerald-400":"text-gray-400"}`,style:{background:r?"rgba(34, 197, 94, 0.1)":"rgba(255, 255, 255, 0.04)",border:r?"1px solid rgba(34, 197, 94, 0.3)":"1px solid rgba(255, 255, 255, 0.06)"},children:[o("span",{children:"Mock ChatGPT Input Bar"}),o("span",{className:`px-2 py-0.5 rounded text-[10px] font-semibold ${r?"bg-emerald-500/20 text-emerald-400":"bg-gray-500/20 text-gray-500"}`,children:r?"ON":"OFF"})]}),r&&l("p",{className:"text-[10px] text-gray-500 mt-1.5",children:["bottom: ",L,"px"]})]}),l("div",{children:[o("label",{htmlFor:"widget-props",className:"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2",children:"Widget Props"}),o("textarea",{value:P,onChange:a=>we(a.target.value),onBlur:ye,className:"dev-json-editor w-full min-h-[160px] text-xs text-gray-200 p-3 rounded-lg resize-none focus:outline-none transition-colors",style:{background:"rgba(0, 0, 0, 0.3)",border:C?"1px solid rgba(239, 68, 68, 0.5)":"1px solid rgba(255, 255, 255, 0.06)",fontFamily:"'JetBrains Mono', 'SF Mono', 'Fira Code', monospace",lineHeight:1.6},onFocus:a=>{C||(a.target.style.borderColor="rgba(99, 102, 241, 0.5)")},onBlurCapture:a=>{C||(a.target.style.borderColor="rgba(255, 255, 255, 0.06)")},spellCheck:!1}),C&&l("p",{className:"text-red-400 text-[11px] mt-1.5 flex items-center gap-1",children:[o("svg",{"aria-hidden":"true","aria-label":"Error",className:"w-3 h-3",viewBox:"0 0 16 16",fill:"currentColor",children:o("path",{d:"M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM7 4.5h2v4H7v-4zm0 5h2v2H7v-2z"})}),C]})]}),l("button",{type:"button",onClick:ve,className:"w-full flex items-center justify-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-gray-400 transition-all duration-150 hover:text-gray-200",style:{background:"rgba(255, 255, 255, 0.04)",border:"1px solid rgba(255, 255, 255, 0.06)"},onMouseEnter:a=>{a.currentTarget.style.background="rgba(255, 255, 255, 0.08)",a.currentTarget.style.borderColor="rgba(255, 255, 255, 0.1)"},onMouseLeave:a=>{a.currentTarget.style.background="rgba(255, 255, 255, 0.04)",a.currentTarget.style.borderColor="rgba(255, 255, 255, 0.06)"},children:[o(Ie,{className:"w-3.5 h-3.5"}),"Reset to Defaults"]})]})]})]}),r&&l("div",{className:"fixed bottom-0 left-0 right-0 z-[9998] flex items-center justify-center pointer-events-none",style:{height:`${L}px`,background:"linear-gradient(to top, #1a1a1a 0%, #1a1a1a 80%, transparent 100%)"},children:[l("div",{className:"w-full max-w-2xl mx-4 px-4 py-3 rounded-2xl bg-[#2f2f2f] border border-[#424242] flex items-center gap-3",children:[o("div",{className:"w-8 h-8 rounded-full bg-[#424242] flex items-center justify-center",children:o("svg",{"aria-hidden":"true",className:"w-4 h-4 text-gray-400",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:o("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 4v16m8-8H4"})})}),o("div",{className:"flex-1 text-gray-400 text-sm",children:"Ask me anything..."}),o("div",{className:"w-8 h-8 rounded-full bg-[#424242] flex items-center justify-center",children:o("svg",{"aria-hidden":"true",className:"w-4 h-4 text-gray-400",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:o("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"})})})]}),l("div",{className:"absolute bottom-1 text-[10px] text-gray-600 font-mono",children:["Mock SafeArea: bottom=",L,"px"]})]})]})}import{useCallback as Fe}from"react";import D,{createContext as Pe,useCallback as Q,useContext as He,useEffect as Z,useState as _,useSyncExternalStore as je}from"react";async function X(){let{detectPlatform:e}=await import("../platform-GKYYQBCS.js");if(e()==="openai"){let{OpenAIWidgetClient:s}=await import("../openai-client-MVV4KJUO.js");return new s}else{let{MCPAppsWidgetClient:s}=await import("../mcp-apps-client-Z62VBB36.js");return new s}}var ee=Pe(null);function te({children:e,loading:t=null,onError:s}){let[u,c]=_(null),[p,f]=_(null),[h,m]=_(!0);return Z(()=>{let n=!0,i=null;async function r(){try{let g=await X();await g.connect(),n?(i=g,c(g),m(!1)):g.close()}catch(g){n&&(console.error("error",g),f(g instanceof Error?g:new Error(String(g))),m(!1))}}return r(),()=>{n=!1,i?.close()}},[]),Z(()=>{if(!u)return;let n=i=>{document.documentElement.classList.toggle("dark",i==="dark")};return n(u.getTheme()),u.onThemeChange(n)},[u]),p&&s?D.createElement(D.Fragment,null,s(p)):h||!u?D.createElement(D.Fragment,null,t):D.createElement(ee.Provider,{value:u},e)}function d(e){let t=He(ee);if(!t)throw new Error("useWidgetClient must be used within a WidgetProvider");let s=Q(p=>e==="toolOutput"?t.onToolResult(()=>p()):e==="theme"?t.onThemeChange(()=>p()):e==="displayMode"?t.onDisplayModeChange(()=>p()):e==="safeArea"?t.onSafeAreaChange(()=>p()):e==="maxHeight"?t.onMaxHeightChange(()=>p()):e==="toolResponseMetadata"?t.onToolResponseMetadataChange(()=>p()):e==="widgetState"?t.onWidgetStateChange(()=>p()):()=>{},[t,e]),u=Q(()=>e==="toolOutput"?t.getToolOutput():e==="theme"?t.getTheme():e==="displayMode"?t.getDisplayMode():e==="locale"?t.getLocale():e==="safeArea"?t.getSafeArea():e==="maxHeight"?t.getMaxHeight():e==="toolResponseMetadata"?t.getToolResponseMetadata():e==="widgetState"?t.getWidgetState():null,[t,e]),c=je(s,u,u);return e?c:t}function oe(){let e=d();return Fe((t,s)=>e.callTool(t,s),[e])}function ne(){return d("displayMode")}import{useSyncExternalStore as Ke}from"react";function re(){return Ke(()=>()=>{},()=>typeof window>"u"?!1:window.__isChatGptApp===!0,()=>!1)}function ae(){return d("locale")}function ie(){return d("maxHeight")}import{useCallback as Be}from"react";function se(){let e=d();return Be(t=>e.openExternal(t),[e])}import{useCallback as _e}from"react";function le(){let e=d();return _e(t=>e.requestDisplayMode(t),[e])}function de(){return d("safeArea")}import{useCallback as ze}from"react";function ce(){let e=d();return ze(t=>e.sendFollowUp(t),[e])}function ue(){return d("theme")}function pe(){return d("toolOutput")}function me(){return d("toolResponseMetadata")}import{useCallback as Ge,useEffect as $e,useState as Je}from"react";function ge(e){let t=d("widgetState"),[s,u]=Je(()=>t??(typeof e=="function"?e():e??null));$e(()=>{u(t)},[t]);let c=Ge(p=>{u(f=>{let h=typeof p=="function"?p(f):p;return $()==="openai"&&h!=null&&window.openai?.setWidgetState(h),h})},[]);return[s,c]}import{jsx as k,jsxs as fe}from"react/jsx-runtime";var qe=()=>fe("div",{className:"flex flex-col items-center justify-center h-full min-h-[120px] gap-4",children:[fe("div",{className:"flex gap-2",children:[k("div",{className:"w-3 h-3 rounded-full bg-gradient-to-r from-blue-400 to-cyan-400 animate-bounce [animation-delay:-0.3s]"}),k("div",{className:"w-3 h-3 rounded-full bg-gradient-to-r from-cyan-400 to-teal-400 animate-bounce [animation-delay:-0.15s]"}),k("div",{className:"w-3 h-3 rounded-full bg-gradient-to-r from-teal-400 to-emerald-400 animate-bounce"})]}),k("p",{className:"text-sm font-medium text-transparent bg-clip-text bg-gradient-to-r from-slate-400 via-slate-200 to-slate-400 bg-[length:200%_100%] animate-[shimmer_1.5s_ease-in-out_infinite]",children:"Loading widget..."}),k("div",{className:"absolute inset-0 flex items-center justify-center pointer-events-none",children:k("div",{className:"w-16 h-16 rounded-full border-2 border-blue-400/20 animate-ping"})}),k("style",{children:`
|
|
63
63
|
@keyframes shimmer {
|
|
64
64
|
0% { background-position: 200% 0; }
|
|
65
65
|
100% { background-position: -200% 0; }
|
|
66
66
|
}
|
|
67
|
-
`})]});export{Y as DevModeProvider,Ce as InitializeNextJsInChatGpt,qe as LoadingWidget,te as WidgetProvider,
|
|
67
|
+
`})]});export{Y as DevModeProvider,Ce as InitializeNextJsInChatGpt,qe as LoadingWidget,te as WidgetProvider,U as getMockState,R as initializeMockOpenAI,O as updateMockDisplayMode,v as updateMockGlobal,A as updateMockTheme,W as updateMockToolOutput,oe as useCallTool,ne as useDisplayMode,re as useIsChatGptApp,ae as useLocale,ie as useMaxHeight,se as useOpenExternal,le as useRequestDisplayMode,de as useSafeArea,ce as useSendFollowUp,ue as useTheme,pe as useToolOutput,me as useToolResponseMetadata,d as useWidgetClient,ge as useWidgetState};
|
|
68
68
|
//# sourceMappingURL=react.js.map
|
package/dist/mcp/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/mcp/react/components/initialize-next-in-chat-gpt.tsx","../../src/mcp/react/dev/dev-controls.tsx","../../src/mcp/react/dev/mock-openai.ts","../../src/mcp/react/hooks/use-call-tool.ts","../../src/mcp/react/hooks/use-widget.ts","../../src/mcp/react/widgets/widget-client.ts","../../src/mcp/react/hooks/use-display-mode.ts","../../src/mcp/react/hooks/use-is-chatgpt-app.ts","../../src/mcp/react/hooks/use-locale.ts","../../src/mcp/react/hooks/use-max-height.ts","../../src/mcp/react/hooks/use-open-external.ts","../../src/mcp/react/hooks/use-request-display-mode.ts","../../src/mcp/react/hooks/use-safe-area.ts","../../src/mcp/react/hooks/use-send-follow-up.ts","../../src/mcp/react/hooks/use-theme.ts","../../src/mcp/react/hooks/use-tool-output.ts","../../src/mcp/react/hooks/use-tool-response-metadata.ts","../../src/mcp/react/hooks/use-widget-state.ts","../../src/mcp/react/widgets/loading-widget.tsx"],"sourcesContent":["/**\n *\n * Initializes & patches Next.js functionalities\n * so it can be run inside the ChatGPT iframe:\n * - history.pushState / history.replaceState - Prevents full-origin URLs in history\n * - window.fetch - Rewrites same-origin requests to use the correct base URL\n * - html attribute observer - Prevents ChatGPT from modifying the root element\n *\n * More information about this component can be found here:\n *\n * https://vercel.com/blog/running-next-js-inside-chatgpt-a-deep-dive-into-native-app-integration\n */\nexport function InitializeNextJsInChatGpt({ baseUrl }: { baseUrl: string }) {\n\treturn (\n\t\t<>\n\t\t\t<base href={baseUrl}></base>\n\t\t\t<script>{`window.innerBaseUrl = ${JSON.stringify(baseUrl)}`}</script>\n\t\t\t<script>{`window.__isChatGptApp = typeof window.openai !== \"undefined\";`}</script>\n\t\t\t<script>\n\t\t\t\t{\"(\" +\n\t\t\t\t\t(() => {\n\t\t\t\t\t\tconst baseUrl = window.innerBaseUrl;\n\t\t\t\t\t\tconst htmlElement = document.documentElement;\n\t\t\t\t\t\tconst observer = new MutationObserver((mutations) => {\n\t\t\t\t\t\t\tmutations.forEach((mutation) => {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tmutation.type === \"attributes\" &&\n\t\t\t\t\t\t\t\t\tmutation.target === htmlElement\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tconst attrName = mutation.attributeName;\n\t\t\t\t\t\t\t\t\t// Preserve class/style so consumers can use html.dark { ... } for theming\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\tattrName &&\n\t\t\t\t\t\t\t\t\t\tattrName !== \"suppresshydrationwarning\" &&\n\t\t\t\t\t\t\t\t\t\tattrName !== \"class\" &&\n\t\t\t\t\t\t\t\t\t\tattrName !== \"style\"\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\thtmlElement.removeAttribute(attrName);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t\tobserver.observe(htmlElement, {\n\t\t\t\t\t\t\tattributes: true,\n\t\t\t\t\t\t\tattributeOldValue: true,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst originalReplaceState = history.replaceState;\n\t\t\t\t\t\thistory.replaceState = (_s, unused, url) => {\n\t\t\t\t\t\t\tconst u = new URL(url ?? \"\", window.location.href);\n\t\t\t\t\t\t\tconst href = u.pathname + u.search + u.hash;\n\t\t\t\t\t\t\toriginalReplaceState.call(history, unused, href);\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst originalPushState = history.pushState;\n\t\t\t\t\t\thistory.pushState = (_s, unused, url) => {\n\t\t\t\t\t\t\tconst u = new URL(url ?? \"\", window.location.href);\n\t\t\t\t\t\t\tconst href = u.pathname + u.search + u.hash;\n\t\t\t\t\t\t\toriginalPushState.call(history, unused, href);\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst appOrigin = new URL(baseUrl).origin;\n\t\t\t\t\t\tconst isInIframe = window.self !== window.top;\n\n\t\t\t\t\t\twindow.addEventListener(\n\t\t\t\t\t\t\t\"click\",\n\t\t\t\t\t\t\t(e) => {\n\t\t\t\t\t\t\t\tconst a = (e?.target as HTMLElement)?.closest(\"a\");\n\t\t\t\t\t\t\t\tif (!a || !a.href) return;\n\t\t\t\t\t\t\t\tconst url = new URL(a.href, window.location.href);\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\turl.origin !== window.location.origin &&\n\t\t\t\t\t\t\t\t\turl.origin !== appOrigin\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tif (window.openai) {\n\t\t\t\t\t\t\t\t\t\t\twindow.openai?.openExternal({ href: a.href });\n\t\t\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t\t\t\"openExternal failed, likely not in OpenAI client\",\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (isInIframe && window.location.origin !== appOrigin) {\n\t\t\t\t\t\t\tconst originalFetch = window.fetch;\n\n\t\t\t\t\t\t\twindow.fetch = (\n\t\t\t\t\t\t\t\tinput: URL | RequestInfo,\n\t\t\t\t\t\t\t\tinit?: RequestInit,\n\t\t\t\t\t\t\t): Promise<Response> => {\n\t\t\t\t\t\t\t\tlet url: URL;\n\t\t\t\t\t\t\t\tif (typeof input === \"string\" || input instanceof URL) {\n\t\t\t\t\t\t\t\t\turl = new URL(input, window.location.href);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\turl = new URL(input.url, window.location.href);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (url.origin === appOrigin) {\n\t\t\t\t\t\t\t\t\tif (typeof input === \"string\" || input instanceof URL) {\n\t\t\t\t\t\t\t\t\t\tinput = url.toString();\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tinput = new Request(url.toString(), input);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturn originalFetch.call(window, input, {\n\t\t\t\t\t\t\t\t\t\t...init,\n\t\t\t\t\t\t\t\t\t\tmode: \"cors\",\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (url.origin === window.location.origin) {\n\t\t\t\t\t\t\t\t\tconst newUrl = new URL(baseUrl);\n\t\t\t\t\t\t\t\t\tnewUrl.pathname = url.pathname;\n\t\t\t\t\t\t\t\t\tnewUrl.search = url.search;\n\t\t\t\t\t\t\t\t\tnewUrl.hash = url.hash;\n\t\t\t\t\t\t\t\t\turl = newUrl;\n\n\t\t\t\t\t\t\t\t\tif (typeof input === \"string\" || input instanceof URL) {\n\t\t\t\t\t\t\t\t\t\tinput = url.toString();\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tinput = new Request(url.toString(), input);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturn originalFetch.call(window, input, {\n\t\t\t\t\t\t\t\t\t\t...init,\n\t\t\t\t\t\t\t\t\t\tmode: \"cors\",\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn originalFetch.call(window, input, init);\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}).toString() +\n\t\t\t\t\t\")()\"}\n\t\t\t</script>\n\t\t</>\n\t);\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { DisplayMode, Theme } from \"../hooks/@types\";\nimport { SetGlobalsEvent } from \"../hooks/@types\";\nimport {\n\tgetMockState,\n\tinitializeMockOpenAI,\n\tupdateMockDisplayMode,\n\tupdateMockTheme,\n\tupdateMockToolOutput,\n} from \"./mock-openai\";\n\nconst MOCK_SAFE_AREA_HEIGHT = 150;\n\n// ============================================================================\n// SVG Icons\n// ============================================================================\n\nfunction DevIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\trole=\"img\"\n\t\t\taria-label=\"Dev Controls\"\n\t\t>\n\t\t\t{/* Minimal: two stacked squares offset */}\n\t\t\t<rect\n\t\t\t\tx=\"4\"\n\t\t\t\ty=\"4\"\n\t\t\t\twidth=\"10\"\n\t\t\t\theight=\"10\"\n\t\t\t\trx=\"2\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.5\"\n\t\t\t/>\n\t\t\t<rect\n\t\t\t\tx=\"10\"\n\t\t\t\ty=\"10\"\n\t\t\t\twidth=\"10\"\n\t\t\t\theight=\"10\"\n\t\t\t\trx=\"2\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.5\"\n\t\t\t\tfill=\"currentColor\"\n\t\t\t\tfillOpacity=\"0.15\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction CloseIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t>\n\t\t\t<path d=\"M18 6L6 18M6 6l12 12\" />\n\t\t</svg>\n\t);\n}\n\nfunction InlineIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<rect\n\t\t\t\tx=\"2\"\n\t\t\t\ty=\"4\"\n\t\t\t\twidth=\"12\"\n\t\t\t\theight=\"8\"\n\t\t\t\trx=\"1.5\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction PipIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<rect\n\t\t\t\tx=\"1.5\"\n\t\t\t\ty=\"3\"\n\t\t\t\twidth=\"13\"\n\t\t\t\theight=\"10\"\n\t\t\t\trx=\"1.5\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t/>\n\t\t\t<rect x=\"8.5\" y=\"7\" width=\"5\" height=\"4\" rx=\"0.75\" fill=\"currentColor\" />\n\t\t</svg>\n\t);\n}\n\nfunction FullscreenIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<path\n\t\t\t\td=\"M2 5.5V3.5C2 2.95 2.45 2.5 3 2.5H5.5M10.5 2.5H13C13.55 2.5 14 2.95 14 3.5V5.5M14 10.5V12.5C14 13.05 13.55 13.5 13 13.5H10.5M5.5 13.5H3C2.45 13.5 2 13.05 2 12.5V10.5\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction SunIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<circle cx=\"8\" cy=\"8\" r=\"2.5\" stroke=\"currentColor\" strokeWidth=\"1.25\" />\n\t\t\t<path\n\t\t\t\td=\"M8 2v1.5M8 12.5V14M2 8h1.5M12.5 8H14M4.11 4.11l1.06 1.06M10.83 10.83l1.06 1.06M4.11 11.89l1.06-1.06M10.83 5.17l1.06-1.06\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction MoonIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<path\n\t\t\t\td=\"M13.5 9.5a5.5 5.5 0 01-7-7 5.5 5.5 0 107 7z\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction ResetIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<path\n\t\t\t\td=\"M2.5 8a5.5 5.5 0 019.37-3.9M13.5 8a5.5 5.5 0 01-9.37 3.9\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\td=\"M12.5 2v3h-3M3.5 14v-3h3\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\n// ============================================================================\n// Styles\n// ============================================================================\n\nconst panelAnimationStyles = `\n @keyframes devPanelSlideIn {\n 0% {\n opacity: 0;\n transform: translateY(12px) scale(0.98);\n }\n 100% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n @keyframes devPanelSlideOut {\n 0% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translateY(8px) scale(0.98);\n }\n }\n\n .dev-panel-enter {\n animation: devPanelSlideIn 200ms cubic-bezier(0.16, 1, 0.3, 1) forwards;\n }\n\n .dev-panel-exit {\n animation: devPanelSlideOut 150ms cubic-bezier(0.4, 0, 1, 1) forwards;\n }\n\n .dev-json-editor::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n }\n\n .dev-json-editor::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .dev-json-editor::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.1);\n border-radius: 3px;\n }\n\n .dev-json-editor::-webkit-scrollbar-thumb:hover {\n background: rgba(255, 255, 255, 0.15);\n }\n`;\n\n// ============================================================================\n// Components\n// ============================================================================\n\ninterface DevControlsProps {\n\tdefaultProps?: Record<string, unknown>;\n\twidgetPaths?: string[];\n}\n\nexport function DevModeProvider({\n\tdefaultProps,\n\twidgetPaths,\n\tchildren,\n}: DevControlsProps & { children: React.ReactNode }) {\n\tconst [isInitialized, setIsInitialized] = useState(false);\n\tconst [isWidgetPage, setIsWidgetPage] = useState(false);\n\n\tuseEffect(() => {\n\t\t// When loaded inside the MCP harness iframe, skip the OpenAI mock\n\t\t// so the real MCPAppsWidgetClient + App class is used instead.\n\t\tconst params = new URLSearchParams(window.location.search);\n\t\tif (params.get(\"platform\") === \"mcp-apps\") {\n\t\t\tsetIsInitialized(true);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if current path is a widget page\n\t\tif (widgetPaths && widgetPaths.length > 0) {\n\t\t\tconst currentPath = window.location.pathname;\n\t\t\tconst isWidget = widgetPaths.some(\n\t\t\t\t(path) => currentPath === path || currentPath.startsWith(`${path}/`),\n\t\t\t);\n\t\t\tsetIsWidgetPage(isWidget);\n\n\t\t\tif (isWidget) {\n\t\t\t\tinitializeMockOpenAI(defaultProps);\n\t\t\t}\n\t\t} else {\n\t\t\t// If no widgetPaths specified, treat all pages as widget pages (backwards compat)\n\t\t\tinitializeMockOpenAI(defaultProps);\n\t\t\tsetIsWidgetPage(true);\n\t\t}\n\t\tsetIsInitialized(true);\n\t}, [defaultProps, widgetPaths]);\n\n\tif (!isInitialized) {\n\t\treturn null;\n\t}\n\n\t// If not a widget page, just render children without the dev frame\n\tif (!isWidgetPage) {\n\t\treturn <>{children}</>;\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t<WidgetPreviewFrame>{children}</WidgetPreviewFrame>\n\t\t\t<DevControls defaultProps={defaultProps} />\n\t\t</>\n\t);\n}\n\nfunction WidgetPreviewFrame({ children }: { children: React.ReactNode }) {\n\treturn (\n\t\t<div className=\"min-h-screen bg-[#212121] flex items-center justify-center p-8\">\n\t\t\t<div\n\t\t\t\tclassName=\"relative w-full max-w-md overflow-hidden rounded-2xl sm:rounded-3xl border border-[#414141]\"\n\t\t\t\tstyle={{\n\t\t\t\t\tboxShadow: \"0px 0px 0px 1px #414141, 0px 4px 14px rgba(0,0,0,0.24)\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ============================================================================\n// Segmented Control\n// ============================================================================\n\ninterface SegmentOption<T extends string> {\n\tvalue: T;\n\tlabel: string;\n\ticon: React.ReactNode;\n}\n\ninterface SegmentedControlProps<T extends string> {\n\toptions: SegmentOption<T>[];\n\tvalue: T;\n\tonChange: (value: T) => void;\n}\n\nfunction SegmentedControl<T extends string>({\n\toptions,\n\tvalue,\n\tonChange,\n}: SegmentedControlProps<T>) {\n\tconst activeIndex = options.findIndex((opt) => opt.value === value);\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"relative flex p-0.5 rounded-lg\"\n\t\t\tstyle={{\n\t\t\t\tbackground: \"rgba(255, 255, 255, 0.04)\",\n\t\t\t\tborder: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t}}\n\t\t>\n\t\t\t{/* Sliding indicator */}\n\t\t\t<div\n\t\t\t\tclassName=\"absolute top-0.5 bottom-0.5 rounded-md transition-transform duration-150 ease-out\"\n\t\t\t\tstyle={{\n\t\t\t\t\twidth: `calc(${100 / options.length}% - 2px)`,\n\t\t\t\t\tleft: \"2px\",\n\t\t\t\t\ttransform: `translateX(calc(${activeIndex * 100}% + ${activeIndex * 2}px))`,\n\t\t\t\t\tbackground: \"rgba(255, 255, 255, 0.1)\",\n\t\t\t\t}}\n\t\t\t/>\n\n\t\t\t{options.map((option) => (\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tkey={option.value}\n\t\t\t\t\tonClick={() => onChange(option.value)}\n\t\t\t\t\tclassName={`\n relative z-10 flex-1 flex items-center justify-center gap-1.5 px-2 py-1.5\n text-xs font-medium rounded-md transition-colors duration-150\n ${value === option.value ? \"text-white\" : \"text-gray-400 hover:text-gray-300\"}\n `}\n\t\t\t\t>\n\t\t\t\t\t{option.icon}\n\t\t\t\t\t<span className=\"capitalize\">{option.label}</span>\n\t\t\t\t</button>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n\n// ============================================================================\n// Main DevControls Component\n// ============================================================================\n\nfunction DevControls({ defaultProps }: DevControlsProps) {\n\tconst [isOpen, setIsOpen] = useState(() => {\n\t\tif (typeof window === \"undefined\") return false;\n\t\treturn localStorage.getItem(\"dev-controls-open\") === \"true\";\n\t});\n\tconst [isAnimating, setIsAnimating] = useState(false);\n\tconst [shouldRender, setShouldRender] = useState(isOpen);\n\tconst [displayMode, setDisplayMode] = useState<DisplayMode>(\"inline\");\n\tconst [theme, setTheme] = useState<Theme>(\"dark\");\n\tconst [showSafeArea, setShowSafeArea] = useState(false);\n\tconst [propsJson, setPropsJson] = useState(() =>\n\t\tJSON.stringify(defaultProps ?? {}, null, 2),\n\t);\n\tconst [jsonError, setJsonError] = useState<string | null>(null);\n\tconst debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst panelRef = useRef<HTMLDivElement>(null);\n\tconst containerRef = useRef<HTMLDivElement>(null);\n\n\t// Initialize from mock state\n\tuseEffect(() => {\n\t\tconst state = getMockState();\n\t\tsetDisplayMode(state.displayMode);\n\t\tsetTheme(state.theme);\n\t}, []);\n\n\t// Update safeArea when toggled\n\tuseEffect(() => {\n\t\tif (typeof window === \"undefined\") return;\n\t\tif (!window.openai) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: window.openai may not exist yet\n\t\t\t(window as any).openai = {};\n\t\t}\n\t\twindow.openai.safeArea = {\n\t\t\tinsets: {\n\t\t\t\ttop: 0,\n\t\t\t\tbottom: showSafeArea ? MOCK_SAFE_AREA_HEIGHT : 0,\n\t\t\t\tleft: 0,\n\t\t\t\tright: 0,\n\t\t\t},\n\t\t};\n\t\twindow.dispatchEvent(\n\t\t\tnew SetGlobalsEvent({ globals: { safeArea: window.openai.safeArea } }),\n\t\t);\n\t}, [showSafeArea]);\n\n\t// Persist open state\n\tuseEffect(() => {\n\t\tlocalStorage.setItem(\"dev-controls-open\", String(isOpen));\n\t}, [isOpen]);\n\n\tconst openPanel = useCallback(() => {\n\t\tsetShouldRender(true);\n\t\t// Small delay to ensure DOM is ready for animation\n\t\trequestAnimationFrame(() => {\n\t\t\tsetIsOpen(true);\n\t\t});\n\t}, []);\n\n\tconst closePanel = useCallback(() => {\n\t\tsetIsAnimating(true);\n\t\tsetIsOpen(false);\n\t\t// Wait for exit animation\n\t\tsetTimeout(() => {\n\t\t\tsetShouldRender(false);\n\t\t\tsetIsAnimating(false);\n\t\t}, 150);\n\t}, []);\n\n\tconst togglePanel = useCallback(() => {\n\t\tif (isOpen) {\n\t\t\tclosePanel();\n\t\t} else {\n\t\t\topenPanel();\n\t\t}\n\t}, [isOpen, openPanel, closePanel]);\n\n\t// Keyboard shortcuts\n\tuseEffect(() => {\n\t\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\t\t// Cmd/Ctrl + Shift + D to toggle\n\t\t\tif ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === \"d\") {\n\t\t\t\te.preventDefault();\n\t\t\t\ttogglePanel();\n\t\t\t}\n\t\t\t// Escape to close\n\t\t\tif (e.key === \"Escape\" && isOpen) {\n\t\t\t\tclosePanel();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"keydown\", handleKeyDown);\n\t\treturn () => window.removeEventListener(\"keydown\", handleKeyDown);\n\t}, [isOpen, togglePanel, closePanel]);\n\n\t// Click outside to close\n\tuseEffect(() => {\n\t\tif (!isOpen) return;\n\n\t\tconst handleClickOutside = (e: MouseEvent) => {\n\t\t\tif (\n\t\t\t\tcontainerRef.current &&\n\t\t\t\t!containerRef.current.contains(e.target as Node)\n\t\t\t) {\n\t\t\t\tclosePanel();\n\t\t\t}\n\t\t};\n\n\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\treturn () => document.removeEventListener(\"mousedown\", handleClickOutside);\n\t}, [isOpen, closePanel]);\n\n\tconst handleDisplayModeChange = useCallback((mode: DisplayMode) => {\n\t\tsetDisplayMode(mode);\n\t\tupdateMockDisplayMode(mode);\n\t}, []);\n\n\tconst handleThemeChange = useCallback((newTheme: Theme) => {\n\t\tsetTheme(newTheme);\n\t\tupdateMockTheme(newTheme);\n\t}, []);\n\n\tconst applyProps = useCallback((json: string) => {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(json);\n\t\t\tsetJsonError(null);\n\t\t\tupdateMockToolOutput(parsed);\n\t\t} catch {\n\t\t\tsetJsonError(\"Invalid JSON\");\n\t\t}\n\t}, []);\n\n\tconst handlePropsChange = useCallback(\n\t\t(json: string) => {\n\t\t\tsetPropsJson(json);\n\t\t\tif (debounceRef.current) {\n\t\t\t\tclearTimeout(debounceRef.current);\n\t\t\t}\n\t\t\tdebounceRef.current = setTimeout(() => {\n\t\t\t\tapplyProps(json);\n\t\t\t}, 500);\n\t\t},\n\t\t[applyProps],\n\t);\n\n\tconst handlePropsBlur = useCallback(() => {\n\t\tif (debounceRef.current) {\n\t\t\tclearTimeout(debounceRef.current);\n\t\t}\n\t\tapplyProps(propsJson);\n\t}, [applyProps, propsJson]);\n\n\tconst handleReset = useCallback(() => {\n\t\tconst defaultJson = JSON.stringify(defaultProps ?? {}, null, 2);\n\t\tsetPropsJson(defaultJson);\n\t\tsetJsonError(null);\n\t\tupdateMockToolOutput(defaultProps ?? {});\n\t\tsetDisplayMode(\"inline\");\n\t\tupdateMockDisplayMode(\"inline\");\n\t\tsetTheme(\"dark\");\n\t\tupdateMockTheme(\"dark\");\n\t}, [defaultProps]);\n\n\tconst displayModeOptions: SegmentOption<DisplayMode>[] = [\n\t\t{\n\t\t\tvalue: \"inline\",\n\t\t\tlabel: \"inline\",\n\t\t\ticon: <InlineIcon className=\"w-3.5 h-3.5\" />,\n\t\t},\n\t\t{ value: \"pip\", label: \"pip\", icon: <PipIcon className=\"w-3.5 h-3.5\" /> },\n\t\t{\n\t\t\tvalue: \"fullscreen\",\n\t\t\tlabel: \"full\",\n\t\t\ticon: <FullscreenIcon className=\"w-3.5 h-3.5\" />,\n\t\t},\n\t];\n\n\tconst themeOptions: SegmentOption<Theme>[] = [\n\t\t{\n\t\t\tvalue: \"light\",\n\t\t\tlabel: \"light\",\n\t\t\ticon: <SunIcon className=\"w-3.5 h-3.5\" />,\n\t\t},\n\t\t{\n\t\t\tvalue: \"dark\",\n\t\t\tlabel: \"dark\",\n\t\t\ticon: <MoonIcon className=\"w-3.5 h-3.5\" />,\n\t\t},\n\t];\n\n\treturn (\n\t\t<>\n\t\t\t{/* Inject animation styles */}\n\t\t\t{/** biome-ignore lint/security/noDangerouslySetInnerHtml: we need to inject styles into the DOM */}\n\t\t\t<style dangerouslySetInnerHTML={{ __html: panelAnimationStyles }} />\n\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\tclassName=\"fixed bottom-4 right-4 z-[9999] font-['Inter',_system-ui,_sans-serif]\"\n\t\t\t>\n\t\t\t\t{/* Toggle Button */}\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={togglePanel}\n\t\t\t\t\tclassName=\"group relative flex items-center justify-center w-11 h-11 rounded-xl transition-all duration-200 ease-out hover:scale-105 active:scale-95\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackground: \"rgba(14, 14, 16, 0.95)\",\n\t\t\t\t\t\tbackdropFilter: \"blur(16px)\",\n\t\t\t\t\t\tWebkitBackdropFilter: \"blur(16px)\",\n\t\t\t\t\t\tborder: \"1px solid rgba(255, 255, 255, 0.08)\",\n\t\t\t\t\t\tboxShadow: `\n 0 4px 12px rgba(0, 0, 0, 0.4),\n 0 0 0 1px rgba(255, 255, 255, 0.05),\n inset 0 1px 0 rgba(255, 255, 255, 0.04)\n `,\n\t\t\t\t\t}}\n\t\t\t\t\taria-label=\"Toggle Dev Controls\"\n\t\t\t\t\taria-expanded={isOpen}\n\t\t\t\t>\n\t\t\t\t\t<DevIcon\n\t\t\t\t\t\tclassName={`w-5 h-5 transition-all duration-200 ${\n\t\t\t\t\t\t\tisOpen\n\t\t\t\t\t\t\t\t? \"text-indigo-400 scale-110\"\n\t\t\t\t\t\t\t\t: \"text-gray-300 group-hover:text-white\"\n\t\t\t\t\t\t}`}\n\t\t\t\t\t/>\n\n\t\t\t\t\t{/* Subtle glow on hover */}\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"absolute inset-0 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none\"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tbackground:\n\t\t\t\t\t\t\t\t\"radial-gradient(circle at center, rgba(99, 102, 241, 0.15) 0%, transparent 70%)\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t</button>\n\n\t\t\t\t{/* Panel */}\n\t\t\t\t{shouldRender && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tref={panelRef}\n\t\t\t\t\t\tclassName={`absolute bottom-14 right-0 w-80 ${\n\t\t\t\t\t\t\tisOpen && !isAnimating ? \"dev-panel-enter\" : \"dev-panel-exit\"\n\t\t\t\t\t\t}`}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tmaxHeight: \"calc(100vh - 120px)\",\n\t\t\t\t\t\t\tbackground: \"rgba(14, 14, 16, 0.92)\",\n\t\t\t\t\t\t\tbackdropFilter: \"blur(24px)\",\n\t\t\t\t\t\t\tWebkitBackdropFilter: \"blur(24px)\",\n\t\t\t\t\t\t\tborder: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t\t\t\t\tborderRadius: \"16px\",\n\t\t\t\t\t\t\tboxShadow: `\n 0 25px 50px -12px rgba(0, 0, 0, 0.6),\n 0 0 0 1px rgba(255, 255, 255, 0.05),\n inset 0 1px 0 rgba(255, 255, 255, 0.04)\n `,\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{/* Header */}\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"flex items-center justify-between px-4 py-3\"\n\t\t\t\t\t\t\tstyle={{ borderBottom: \"1px solid rgba(255, 255, 255, 0.06)\" }}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<DevIcon className=\"w-4 h-4 text-gray-400\" />\n\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium text-white\">\n\t\t\t\t\t\t\t\t\tDev Controls\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t{/* Keyboard shortcut badge */}\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclassName=\"text-[10px] font-medium text-gray-500 px-1.5 py-0.5 rounded\"\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tbackground: \"rgba(255, 255, 255, 0.04)\",\n\t\t\t\t\t\t\t\t\t\tborder: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{typeof navigator !== \"undefined\" &&\n\t\t\t\t\t\t\t\t\tnavigator.platform?.includes(\"Mac\")\n\t\t\t\t\t\t\t\t\t\t? \"⌘⇧D\"\n\t\t\t\t\t\t\t\t\t\t: \"Ctrl+Shift+D\"}\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t{/* Close button */}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={closePanel}\n\t\t\t\t\t\t\t\t\tclassName=\"p-1 rounded-md text-gray-500 hover:text-gray-300 hover:bg-white/5 transition-colors\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<CloseIcon className=\"w-4 h-4\" />\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Content */}\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"p-4 space-y-5 overflow-y-auto\"\n\t\t\t\t\t\t\tstyle={{ maxHeight: \"calc(100vh - 200px)\" }}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{/* Display Mode */}\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\thtmlFor=\"display-mode\"\n\t\t\t\t\t\t\t\t\tclassName=\"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tDisplay Mode\n\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t<SegmentedControl\n\t\t\t\t\t\t\t\t\toptions={displayModeOptions}\n\t\t\t\t\t\t\t\t\tvalue={displayMode}\n\t\t\t\t\t\t\t\t\tonChange={handleDisplayModeChange}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Theme */}\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\thtmlFor=\"theme\"\n\t\t\t\t\t\t\t\t\tclassName=\"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tTheme\n\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t<SegmentedControl\n\t\t\t\t\t\t\t\t\toptions={themeOptions}\n\t\t\t\t\t\t\t\t\tvalue={theme}\n\t\t\t\t\t\t\t\t\tonChange={handleThemeChange}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Safe Area Mock */}\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\thtmlFor=\"safe-area\"\n\t\t\t\t\t\t\t\t\tclassName=\"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tSafe Area (Chat Input)\n\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => setShowSafeArea(!showSafeArea)}\n\t\t\t\t\t\t\t\t\tclassName={`w-full flex items-center justify-between px-3 py-2.5 rounded-lg text-xs font-medium transition-all duration-150 ${\n\t\t\t\t\t\t\t\t\t\tshowSafeArea ? \"text-emerald-400\" : \"text-gray-400\"\n\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tbackground: showSafeArea\n\t\t\t\t\t\t\t\t\t\t\t? \"rgba(34, 197, 94, 0.1)\"\n\t\t\t\t\t\t\t\t\t\t\t: \"rgba(255, 255, 255, 0.04)\",\n\t\t\t\t\t\t\t\t\t\tborder: showSafeArea\n\t\t\t\t\t\t\t\t\t\t\t? \"1px solid rgba(34, 197, 94, 0.3)\"\n\t\t\t\t\t\t\t\t\t\t\t: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span>Mock ChatGPT Input Bar</span>\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName={`px-2 py-0.5 rounded text-[10px] font-semibold ${\n\t\t\t\t\t\t\t\t\t\t\tshowSafeArea\n\t\t\t\t\t\t\t\t\t\t\t\t? \"bg-emerald-500/20 text-emerald-400\"\n\t\t\t\t\t\t\t\t\t\t\t\t: \"bg-gray-500/20 text-gray-500\"\n\t\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{showSafeArea ? \"ON\" : \"OFF\"}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t{showSafeArea && (\n\t\t\t\t\t\t\t\t\t<p className=\"text-[10px] text-gray-500 mt-1.5\">\n\t\t\t\t\t\t\t\t\t\tbottom: {MOCK_SAFE_AREA_HEIGHT}px\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Widget Props */}\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\thtmlFor=\"widget-props\"\n\t\t\t\t\t\t\t\t\tclassName=\"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tWidget Props\n\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\t\t\tvalue={propsJson}\n\t\t\t\t\t\t\t\t\tonChange={(e) => handlePropsChange(e.target.value)}\n\t\t\t\t\t\t\t\t\tonBlur={handlePropsBlur}\n\t\t\t\t\t\t\t\t\tclassName=\"dev-json-editor w-full min-h-[160px] text-xs text-gray-200 p-3 rounded-lg resize-none focus:outline-none transition-colors\"\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tbackground: \"rgba(0, 0, 0, 0.3)\",\n\t\t\t\t\t\t\t\t\t\tborder: jsonError\n\t\t\t\t\t\t\t\t\t\t\t? \"1px solid rgba(239, 68, 68, 0.5)\"\n\t\t\t\t\t\t\t\t\t\t\t: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t\t\t\t\t\t\t\tfontFamily:\n\t\t\t\t\t\t\t\t\t\t\t\"'JetBrains Mono', 'SF Mono', 'Fira Code', monospace\",\n\t\t\t\t\t\t\t\t\t\tlineHeight: 1.6,\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonFocus={(e) => {\n\t\t\t\t\t\t\t\t\t\tif (!jsonError) {\n\t\t\t\t\t\t\t\t\t\t\te.target.style.borderColor = \"rgba(99, 102, 241, 0.5)\";\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonBlurCapture={(e) => {\n\t\t\t\t\t\t\t\t\t\tif (!jsonError) {\n\t\t\t\t\t\t\t\t\t\t\te.target.style.borderColor = \"rgba(255, 255, 255, 0.06)\";\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t{jsonError && (\n\t\t\t\t\t\t\t\t\t<p className=\"text-red-400 text-[11px] mt-1.5 flex items-center gap-1\">\n\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"Error\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"w-3 h-3\"\n\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<path d=\"M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM7 4.5h2v4H7v-4zm0 5h2v2H7v-2z\" />\n\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t{jsonError}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Reset Button */}\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={handleReset}\n\t\t\t\t\t\t\t\tclassName=\"w-full flex items-center justify-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-gray-400 transition-all duration-150 hover:text-gray-200\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tbackground: \"rgba(255, 255, 255, 0.04)\",\n\t\t\t\t\t\t\t\t\tborder: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tonMouseEnter={(e) => {\n\t\t\t\t\t\t\t\t\te.currentTarget.style.background =\n\t\t\t\t\t\t\t\t\t\t\"rgba(255, 255, 255, 0.08)\";\n\t\t\t\t\t\t\t\t\te.currentTarget.style.borderColor =\n\t\t\t\t\t\t\t\t\t\t\"rgba(255, 255, 255, 0.1)\";\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tonMouseLeave={(e) => {\n\t\t\t\t\t\t\t\t\te.currentTarget.style.background =\n\t\t\t\t\t\t\t\t\t\t\"rgba(255, 255, 255, 0.04)\";\n\t\t\t\t\t\t\t\t\te.currentTarget.style.borderColor =\n\t\t\t\t\t\t\t\t\t\t\"rgba(255, 255, 255, 0.06)\";\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<ResetIcon className=\"w-3.5 h-3.5\" />\n\t\t\t\t\t\t\t\tReset to Defaults\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Mock ChatGPT Input Bar */}\n\t\t\t{showSafeArea && (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"fixed bottom-0 left-0 right-0 z-[9998] flex items-center justify-center pointer-events-none\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\theight: `${MOCK_SAFE_AREA_HEIGHT}px`,\n\t\t\t\t\t\tbackground:\n\t\t\t\t\t\t\t\"linear-gradient(to top, #1a1a1a 0%, #1a1a1a 80%, transparent 100%)\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<div className=\"w-full max-w-2xl mx-4 px-4 py-3 rounded-2xl bg-[#2f2f2f] border border-[#424242] flex items-center gap-3\">\n\t\t\t\t\t\t<div className=\"w-8 h-8 rounded-full bg-[#424242] flex items-center justify-center\">\n\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\tclassName=\"w-4 h-4 text-gray-400\"\n\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t\t\td=\"M12 4v16m8-8H4\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex-1 text-gray-400 text-sm\">\n\t\t\t\t\t\t\tAsk me anything...\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"w-8 h-8 rounded-full bg-[#424242] flex items-center justify-center\">\n\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\tclassName=\"w-4 h-4 text-gray-400\"\n\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t\t\td=\"M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"absolute bottom-1 text-[10px] text-gray-600 font-mono\">\n\t\t\t\t\t\tMock SafeArea: bottom={MOCK_SAFE_AREA_HEIGHT}px\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</>\n\t);\n}\n","import {\n\ttype DisplayMode,\n\ttype OpenAIGlobals,\n\tSetGlobalsEvent,\n\ttype Theme,\n} from \"../hooks/@types\";\n\nconst DEFAULT_MOCK_CONFIG: Omit<OpenAIGlobals, \"setWidgetState\"> = {\n\ttheme: \"dark\",\n\tuserAgent: {\n\t\tdevice: { type: \"desktop\" },\n\t\tcapabilities: { hover: true, touch: false },\n\t},\n\tlocale: \"en\",\n\tmaxHeight: 800,\n\tdisplayMode: \"inline\",\n\tsafeArea: { insets: { top: 0, bottom: 0, left: 0, right: 0 } },\n\ttoolInput: {},\n\ttoolOutput: null,\n\ttoolResponseMetadata: null,\n\twidgetState: null,\n};\n\nlet mockState: Omit<OpenAIGlobals, \"setWidgetState\"> = {\n\t...DEFAULT_MOCK_CONFIG,\n};\n\nexport function initializeMockOpenAI(\n\tinitialToolOutput?: Record<string, unknown>,\n): void {\n\tif (typeof window === \"undefined\") return;\n\tif (window.openai) return; // Already initialized (real ChatGPT or previous mock)\n\n\tmockState = {\n\t\t...DEFAULT_MOCK_CONFIG,\n\t\ttoolOutput: initialToolOutput ?? null,\n\t};\n\n\twindow.openai = {\n\t\t...mockState,\n\t\t// Mock API functions\n\t\trequestDisplayMode: async ({ mode }) => {\n\t\t\tupdateMockGlobal(\"displayMode\", mode);\n\t\t\treturn { mode };\n\t\t},\n\t\tcallTool: async (name, args) => {\n\t\t\tconsole.log(`[DevMode] callTool: ${name}`, args);\n\t\t\treturn { result: JSON.stringify({ mock: true, tool: name, args }) };\n\t\t},\n\t\tsendFollowUpMessage: async ({ prompt }) => {\n\t\t\tconsole.log(`[DevMode] sendFollowUpMessage: ${prompt}`);\n\t\t},\n\t\topenExternal: ({ href }) => {\n\t\t\tconsole.log(`[DevMode] openExternal: ${href}`);\n\t\t\twindow.open(href, \"_blank\");\n\t\t},\n\t\tsetWidgetState: async (state) => {\n\t\t\tupdateMockGlobal(\"widgetState\", state);\n\t\t},\n\t};\n\n\t// Dispatch initial event so hooks pick up the values\n\twindow.dispatchEvent(new SetGlobalsEvent({ globals: mockState }));\n}\n\nexport function updateMockGlobal<K extends keyof OpenAIGlobals>(\n\tkey: K,\n\tvalue: OpenAIGlobals[K],\n): void {\n\tif (typeof window === \"undefined\" || !window.openai) return;\n\n\t(mockState as Record<string, unknown>)[key] = value;\n\t(window.openai as Record<string, unknown>)[key] = value;\n\n\twindow.dispatchEvent(new SetGlobalsEvent({ globals: { [key]: value } }));\n}\n\nexport function getMockState(): typeof mockState {\n\treturn { ...mockState };\n}\n\nexport function updateMockToolOutput(props: Record<string, unknown>): void {\n\tupdateMockGlobal(\"toolOutput\", props);\n}\n\nexport function updateMockDisplayMode(mode: DisplayMode): void {\n\tupdateMockGlobal(\"displayMode\", mode);\n}\n\nexport function updateMockTheme(theme: Theme): void {\n\tupdateMockGlobal(\"theme\", theme);\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport type { ToolCallResult } from \"../widgets/widget-client\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get a function to call other tools.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns A function to call tools with their name and arguments\n */\nexport function useCallTool(): (\n\tname: string,\n\targs: Record<string, unknown>,\n) => Promise<ToolCallResult> {\n\tconst client = useWidgetClient();\n\treturn useCallback(\n\t\t(name: string, args: Record<string, unknown>) =>\n\t\t\tclient.callTool(name, args),\n\t\t[client],\n\t);\n}\n","\"use client\";\n\nimport React, {\n\tcreateContext,\n\ttype ReactNode,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseState,\n\tuseSyncExternalStore,\n} from \"react\";\nimport {\n\tcreateWidgetClient,\n\ttype UnifiedWidgetClient,\n} from \"../widgets/widget-client\";\nimport type { DisplayMode, SafeArea, Theme, UnknownObject } from \"./@types\";\n\n/**\n * Context for the unified widget client.\n */\nconst WidgetClientContext = createContext<UnifiedWidgetClient | null>(null);\n\n/**\n * Provider props\n */\ninterface WidgetProviderProps {\n\tchildren: ReactNode;\n\t/** Optional loading component while connecting */\n\tloading?: ReactNode;\n\t/** Optional error component */\n\tonError?: (error: Error) => ReactNode;\n}\n\n/**\n * Provider component that initializes the correct widget client based on platform.\n * Wrap your widget component with this provider.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <WidgetProvider loading={<Spinner />}>\n * <MyWidget />\n * </WidgetProvider>\n * );\n * }\n * ```\n */\nexport function WidgetProvider({\n\tchildren,\n\tloading = null,\n\tonError,\n}: WidgetProviderProps) {\n\tconst [client, setClient] = useState<UnifiedWidgetClient | null>(null);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [isConnecting, setIsConnecting] = useState(true);\n\n\tuseEffect(() => {\n\t\tlet mounted = true;\n\n\t\tasync function initClient() {\n\t\t\ttry {\n\t\t\t\tconst widgetClient = await createWidgetClient();\n\n\t\t\t\tawait widgetClient.connect();\n\n\t\t\t\tif (mounted) {\n\t\t\t\t\tsetClient(widgetClient);\n\t\t\t\t\tsetIsConnecting(false);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tif (mounted) {\n\t\t\t\t\tconsole.error(\"error\", err);\n\t\t\t\t\tsetError(err instanceof Error ? err : new Error(String(err)));\n\t\t\t\t\tsetIsConnecting(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tinitClient();\n\n\t\treturn () => {\n\t\t\tmounted = false;\n\t\t};\n\t}, []);\n\n\t// Sync theme to <html> class so consumers can use html.dark { ... } or dark: variants\n\tuseEffect(() => {\n\t\tif (!client) return;\n\n\t\tconst syncTheme = (theme: Theme) => {\n\t\t\tdocument.documentElement.classList.toggle(\"dark\", theme === \"dark\");\n\t\t};\n\n\t\tsyncTheme(client.getTheme());\n\t\treturn client.onThemeChange(syncTheme);\n\t}, [client]);\n\n\tif (error && onError) {\n\t\treturn React.createElement(React.Fragment, null, onError(error));\n\t}\n\n\tif (isConnecting || !client) {\n\t\treturn React.createElement(React.Fragment, null, loading);\n\t}\n\n\treturn React.createElement(\n\t\tWidgetClientContext.Provider,\n\t\t{ value: client },\n\t\tchildren,\n\t);\n}\n\n/**\n * Keys that can be selected from the widget client.\n */\ntype WidgetKey =\n\t| \"toolOutput\"\n\t| \"theme\"\n\t| \"displayMode\"\n\t| \"locale\"\n\t| \"safeArea\"\n\t| \"maxHeight\"\n\t| \"toolResponseMetadata\"\n\t| \"widgetState\";\n\n/**\n * Value types for each widget key.\n */\ntype WidgetKeyValues = {\n\ttoolOutput: Record<string, unknown> | null;\n\ttheme: Theme;\n\tdisplayMode: DisplayMode;\n\tlocale: string;\n\tsafeArea: SafeArea | null;\n\tmaxHeight: number | null;\n\ttoolResponseMetadata: UnknownObject | null;\n\twidgetState: UnknownObject | null;\n};\n\n/**\n * Get the unified widget client instance.\n * Must be used within a WidgetProvider.\n *\n * @example\n * ```tsx\n * // Full client for actions\n * const client = useWidgetClient();\n * client.callTool(\"foo\", {});\n *\n * // Key selector for reactive values\n * const toolOutput = useWidgetClient(\"toolOutput\");\n * const theme = useWidgetClient(\"theme\");\n * ```\n */\nexport function useWidgetClient(): UnifiedWidgetClient;\nexport function useWidgetClient<K extends WidgetKey>(\n\tkey: K,\n): WidgetKeyValues[K];\nexport function useWidgetClient<K extends WidgetKey>(key?: K) {\n\tconst client = useContext(WidgetClientContext);\n\n\tif (!client) {\n\t\tthrow new Error(\"useWidgetClient must be used within a WidgetProvider\");\n\t}\n\n\t// Key selector - use useSyncExternalStore\n\tconst subscribe = useCallback(\n\t\t(onChange: () => void) => {\n\t\t\tif (key === \"toolOutput\") return client.onToolResult(() => onChange());\n\t\t\tif (key === \"theme\") return client.onThemeChange(() => onChange());\n\t\t\tif (key === \"displayMode\")\n\t\t\t\treturn client.onDisplayModeChange(() => onChange());\n\t\t\tif (key === \"safeArea\") return client.onSafeAreaChange(() => onChange());\n\t\t\tif (key === \"maxHeight\")\n\t\t\t\treturn client.onMaxHeightChange(() => onChange());\n\t\t\tif (key === \"toolResponseMetadata\")\n\t\t\t\treturn client.onToolResponseMetadataChange(() => onChange());\n\t\t\tif (key === \"widgetState\")\n\t\t\t\treturn client.onWidgetStateChange(() => onChange());\n\t\t\treturn () => {};\n\t\t},\n\t\t[client, key],\n\t);\n\n\tconst getSnapshot = useCallback(() => {\n\t\tif (key === \"toolOutput\") return client.getToolOutput();\n\t\tif (key === \"theme\") return client.getTheme();\n\t\tif (key === \"displayMode\") return client.getDisplayMode();\n\t\tif (key === \"locale\") return client.getLocale();\n\t\tif (key === \"safeArea\") return client.getSafeArea();\n\t\tif (key === \"maxHeight\") return client.getMaxHeight();\n\t\tif (key === \"toolResponseMetadata\") return client.getToolResponseMetadata();\n\t\tif (key === \"widgetState\") return client.getWidgetState();\n\t\treturn null;\n\t}, [client, key]);\n\n\tconst store = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n\t// No key - return full client\n\tif (!key) return client;\n\n\treturn store;\n}\n","import type {\n\tDisplayMode,\n\tSafeArea,\n\tTheme,\n\tUnknownObject,\n} from \"../hooks/@types\";\n\n/**\n * Result from calling a tool\n */\nexport type ToolCallResult = {\n\tcontent?: Array<{ type: string; text?: string }>;\n\tstructuredContent?: Record<string, unknown>;\n};\n\n/**\n * Tool result notification (what the host pushes to the widget)\n */\nexport type ToolResult = {\n\tcontent?: Array<{ type: string; text?: string }>;\n\tstructuredContent?: Record<string, unknown>;\n};\n\n/**\n * Host context - all values available from the host.\n */\nexport type HostContext = {\n\ttheme: Theme;\n\tlocale: string;\n\tdisplayMode: DisplayMode;\n\tmaxHeight: number | null;\n\tsafeArea: SafeArea | null;\n\ttoolOutput: UnknownObject | null;\n\ttoolResponseMetadata: UnknownObject | null;\n\twidgetState: UnknownObject | null;\n};\n\n/**\n * Store interface for useSyncExternalStore compatibility.\n */\nexport type HostContextStore<K extends keyof HostContext> = {\n\tsubscribe: (onStoreChange: () => void) => () => void;\n\tgetSnapshot: () => HostContext[K];\n};\n\n/**\n * Unified widget client interface that works on both OpenAI and MCP Apps.\n *\n * Platform-specific behavior:\n * - Display mode: OpenAI-only. MCP Apps returns \"inline\" and requestDisplayMode is a no-op.\n * - Follow-up messages: Unified API, different underlying implementations.\n */\nexport interface UnifiedWidgetClient {\n\t/**\n\t * Connect to the host. Must be called before using other methods.\n\t * On OpenAI, this is a no-op (already connected via window.openai).\n\t * On MCP Apps, this establishes the postMessage connection.\n\t */\n\tconnect(): Promise<void>;\n\n\t/**\n\t * Get the tool output (structured content returned by the tool handler).\n\t * This is the main data source for widget rendering.\n\t */\n\tgetToolOutput<T = Record<string, unknown>>(): T | null;\n\n\t/**\n\t * Register a callback for when tool results are received.\n\t * On OpenAI, this subscribes to toolOutput changes.\n\t * On MCP Apps, this sets app.ontoolresult.\n\t */\n\tonToolResult(callback: (result: ToolResult) => void): () => void;\n\n\t/**\n\t * Call another tool on the server.\n\t */\n\tcallTool(\n\t\tname: string,\n\t\targs: Record<string, unknown>,\n\t): Promise<ToolCallResult>;\n\n\t/**\n\t * Open an external URL.\n\t * On OpenAI: openai.openExternal({ href })\n\t * On MCP Apps: app.sendOpenLink(url)\n\t */\n\topenExternal(url: string): void;\n\n\t/**\n\t * Send a follow-up message to the AI.\n\t * On OpenAI: openai.sendFollowUpMessage({ prompt })\n\t * On MCP Apps: app.sendMessages([{ role: 'user', content: { type: 'text', text: prompt } }])\n\t */\n\tsendFollowUp(prompt: string): void;\n\n\t/**\n\t * Get the current theme.\n\t */\n\tgetTheme(): Theme;\n\n\t/**\n\t * Subscribe to theme changes.\n\t */\n\tonThemeChange(callback: (theme: Theme) => void): () => void;\n\n\t/**\n\t * Get the current locale.\n\t */\n\tgetLocale(): string;\n\n\t/**\n\t * Get the current display mode.\n\t * OpenAI-only: returns \"pip\" | \"inline\" | \"fullscreen\"\n\t * MCP Apps: always returns \"inline\"\n\t */\n\tgetDisplayMode(): DisplayMode;\n\n\t/**\n\t * Request a display mode change.\n\t * OpenAI-only: requests the mode from the host.\n\t * MCP Apps: no-op (returns current mode).\n\t */\n\trequestDisplayMode(mode: DisplayMode): Promise<DisplayMode>;\n\n\t/**\n\t * Subscribe to display mode changes.\n\t * OpenAI-only: subscribes to displayMode changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonDisplayModeChange(callback: (mode: DisplayMode) => void): () => void;\n\n\t/**\n\t * Get the safe area insets.\n\t * OpenAI-only: returns insets from window.openai.safeArea.\n\t * MCP Apps: returns null.\n\t */\n\tgetSafeArea(): SafeArea | null;\n\n\t/**\n\t * Subscribe to safe area changes.\n\t * OpenAI-only: subscribes to safeArea changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonSafeAreaChange(callback: (safeArea: SafeArea | null) => void): () => void;\n\n\t/**\n\t * Get the max height constraint.\n\t * OpenAI-only: returns maxHeight from window.openai.maxHeight.\n\t * MCP Apps: returns null.\n\t */\n\tgetMaxHeight(): number | null;\n\n\t/**\n\t * Subscribe to max height changes.\n\t * OpenAI-only: subscribes to maxHeight changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonMaxHeightChange(callback: (maxHeight: number | null) => void): () => void;\n\n\t/**\n\t * Get the tool response metadata.\n\t * OpenAI-only: returns metadata from window.openai.toolResponseMetadata.\n\t * MCP Apps: returns null.\n\t */\n\tgetToolResponseMetadata(): UnknownObject | null;\n\n\t/**\n\t * Subscribe to tool response metadata changes.\n\t * OpenAI-only: subscribes to toolResponseMetadata changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonToolResponseMetadataChange(\n\t\tcallback: (metadata: UnknownObject | null) => void,\n\t): () => void;\n\n\t/**\n\t * Get the widget state.\n\t * OpenAI-only: returns state from window.openai.widgetState.\n\t * MCP Apps: returns null.\n\t */\n\tgetWidgetState(): UnknownObject | null;\n\n\t/**\n\t * Subscribe to widget state changes.\n\t * OpenAI-only: subscribes to widgetState changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonWidgetStateChange(\n\t\tcallback: (state: UnknownObject | null) => void,\n\t): () => void;\n}\n\n/**\n * Creates a unified widget client for the current platform.\n */\nexport async function createWidgetClient(): Promise<UnifiedWidgetClient> {\n\tconst { detectPlatform } = await import(\"./platform\");\n\tconst platform = detectPlatform();\n\n\tif (platform === \"openai\") {\n\t\tconst { OpenAIWidgetClient } = await import(\"./openai-client\");\n\t\treturn new OpenAIWidgetClient();\n\t} else {\n\t\tconst { MCPAppsWidgetClient } = await import(\"./mcp-apps-client\");\n\t\treturn new MCPAppsWidgetClient();\n\t}\n}\n","\"use client\";\n\nimport type { DisplayMode } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get the current display mode.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns The current display mode (\"pip\" | \"inline\" | \"fullscreen\")\n */\nexport function useDisplayMode(): DisplayMode {\n\treturn useWidgetClient(\"displayMode\");\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\n\n/**\n * Check if running in ChatGPT app (OpenAI-only).\n * Returns false on MCP Apps.\n *\n * @returns Whether the widget is running in ChatGPT app\n */\nexport function useIsChatGptApp(): boolean {\n\treturn useSyncExternalStore(\n\t\t() => () => {},\n\t\t() => {\n\t\t\tif (typeof window === \"undefined\") return false;\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: __isChatGptApp is injected by ChatGPT\n\t\t\treturn (window as any).__isChatGptApp === true;\n\t\t},\n\t\t() => false,\n\t);\n}\n","\"use client\";\n\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get the current locale.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns The current locale string (e.g., \"en-US\")\n */\nexport function useLocale(): string {\n\treturn useWidgetClient(\"locale\");\n}\n","\"use client\";\n\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get the maximum height available for the widget (OpenAI-only).\n * Useful for responsive layouts that need to adapt to container constraints.\n * Returns null on MCP Apps.\n *\n * @returns The maximum height in pixels, or null if not available\n */\nexport function useMaxHeight(): number | null {\n\treturn useWidgetClient(\"maxHeight\");\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get a function to open external URLs.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns A function that opens external URLs\n */\nexport function useOpenExternal(): (url: string) => void {\n\tconst client = useWidgetClient();\n\treturn useCallback((url: string) => client.openExternal(url), [client]);\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport type { DisplayMode } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get a function to request display mode changes.\n * On MCP Apps, this may be a no-op depending on host support.\n *\n * @returns A function to request a specific display mode\n */\nexport function useRequestDisplayMode(): (\n\tmode: DisplayMode,\n) => Promise<DisplayMode> {\n\tconst client = useWidgetClient();\n\treturn useCallback(\n\t\t(mode: DisplayMode) => client.requestDisplayMode(mode),\n\t\t[client],\n\t);\n}\n","\"use client\";\n\nimport type { SafeArea } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get safe area insets (OpenAI-only).\n * Useful for ensuring UI elements don't get hidden behind the chat input.\n * Returns null on MCP Apps.\n *\n * @returns The safe area insets, or null if not available\n */\nexport function useSafeArea(): SafeArea | null {\n\treturn useWidgetClient(\"safeArea\");\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get a function to send follow-up messages to the AI.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns A function that sends a follow-up message\n */\nexport function useSendFollowUp(): (prompt: string) => void {\n\tconst client = useWidgetClient();\n\treturn useCallback((prompt: string) => client.sendFollowUp(prompt), [client]);\n}\n","\"use client\";\n\nimport type { Theme } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get the current theme.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns The current theme (\"light\" | \"dark\")\n */\nexport function useTheme(): Theme {\n\treturn useWidgetClient(\"theme\");\n}\n","\"use client\";\n\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get the tool output (structured content returned by the tool handler).\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns The tool output or null\n */\nexport function useToolOutput<T extends Record<string, unknown>>(): T | null {\n\treturn useWidgetClient(\"toolOutput\") as T | null;\n}\n","\"use client\";\n\nimport type { UnknownObject } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get tool response metadata (OpenAI-only).\n * Contains identifiers like `openai/widgetSessionId` for correlating\n * multiple tool calls or logs for the same widget instance.\n * Returns null on MCP Apps.\n *\n * @returns The tool response metadata object or null if not available\n */\nexport function useToolResponseMetadata(): UnknownObject | null {\n\treturn useWidgetClient(\"toolResponseMetadata\");\n}\n","\"use client\";\n\nimport { type SetStateAction, useCallback, useEffect, useState } from \"react\";\nimport { detectPlatform } from \"../widgets/platform\";\nimport type { UnknownObject } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Widget state that persists across widget lifecycles (OpenAI-only).\n * State is synchronized with the ChatGPT parent window and survives widget minimize/restore.\n * On MCP Apps, returns [null, no-op].\n *\n * @param defaultState - Initial state value or function to compute it\n * @returns A tuple of [state, setState] similar to useState\n */\nexport function useWidgetState<T extends UnknownObject>(\n\tdefaultState?: T | (() => T | null) | null,\n): readonly [T | null, (state: SetStateAction<T | null>) => void] {\n\tconst widgetStateFromWindow = useWidgetClient(\"widgetState\") as T | null;\n\n\tconst [widgetState, _setWidgetState] = useState<T | null>(() => {\n\t\tif (widgetStateFromWindow != null) {\n\t\t\treturn widgetStateFromWindow;\n\t\t}\n\t\treturn typeof defaultState === \"function\"\n\t\t\t? defaultState()\n\t\t\t: (defaultState ?? null);\n\t});\n\n\tuseEffect(() => {\n\t\t_setWidgetState(widgetStateFromWindow);\n\t}, [widgetStateFromWindow]);\n\n\tconst setWidgetState = useCallback((state: SetStateAction<T | null>) => {\n\t\t_setWidgetState((prevState) => {\n\t\t\tconst newState = typeof state === \"function\" ? state(prevState) : state;\n\n\t\t\tif (detectPlatform() === \"openai\" && newState != null) {\n\t\t\t\twindow.openai?.setWidgetState(newState);\n\t\t\t}\n\n\t\t\treturn newState;\n\t\t});\n\t}, []);\n\n\treturn [widgetState, setWidgetState] as const;\n}\n","export const LoadingWidget = () => {\n\treturn (\n\t\t<div className=\"flex flex-col items-center justify-center h-full min-h-[120px] gap-4\">\n\t\t\t{/* Animated dots */}\n\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t<div className=\"w-3 h-3 rounded-full bg-gradient-to-r from-blue-400 to-cyan-400 animate-bounce [animation-delay:-0.3s]\" />\n\t\t\t\t<div className=\"w-3 h-3 rounded-full bg-gradient-to-r from-cyan-400 to-teal-400 animate-bounce [animation-delay:-0.15s]\" />\n\t\t\t\t<div className=\"w-3 h-3 rounded-full bg-gradient-to-r from-teal-400 to-emerald-400 animate-bounce\" />\n\t\t\t</div>\n\n\t\t\t{/* Shimmer text */}\n\t\t\t<p className=\"text-sm font-medium text-transparent bg-clip-text bg-gradient-to-r from-slate-400 via-slate-200 to-slate-400 bg-[length:200%_100%] animate-[shimmer_1.5s_ease-in-out_infinite]\">\n\t\t\t\tLoading widget...\n\t\t\t</p>\n\n\t\t\t{/* Pulsing ring */}\n\t\t\t<div className=\"absolute inset-0 flex items-center justify-center pointer-events-none\">\n\t\t\t\t<div className=\"w-16 h-16 rounded-full border-2 border-blue-400/20 animate-ping\" />\n\t\t\t</div>\n\n\t\t\t<style>{`\n @keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n `}</style>\n\t\t</div>\n\t);\n};\n"],"mappings":"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","names":["Fragment","jsx","jsxs","InitializeNextJsInChatGpt","baseUrl","htmlElement","mutations","mutation","attrName","originalReplaceState","_s","unused","url","u","href","originalPushState","appOrigin","isInIframe","e","a","originalFetch","input","init","newUrl","useCallback","useEffect","useRef","useState","DEFAULT_MOCK_CONFIG","mockState","initializeMockOpenAI","initialToolOutput","mode","updateMockGlobal","name","args","prompt","href","state","SetGlobalsEvent","key","value","getMockState","updateMockToolOutput","props","updateMockDisplayMode","updateMockTheme","theme","Fragment","jsx","jsxs","MOCK_SAFE_AREA_HEIGHT","DevIcon","className","CloseIcon","InlineIcon","PipIcon","FullscreenIcon","SunIcon","MoonIcon","ResetIcon","panelAnimationStyles","DevModeProvider","defaultProps","widgetPaths","children","isInitialized","setIsInitialized","useState","isWidgetPage","setIsWidgetPage","useEffect","currentPath","isWidget","path","initializeMockOpenAI","WidgetPreviewFrame","DevControls","SegmentedControl","options","value","onChange","activeIndex","opt","option","isOpen","setIsOpen","isAnimating","setIsAnimating","shouldRender","setShouldRender","displayMode","setDisplayMode","theme","setTheme","showSafeArea","setShowSafeArea","propsJson","setPropsJson","jsonError","setJsonError","debounceRef","useRef","panelRef","containerRef","state","getMockState","SetGlobalsEvent","openPanel","useCallback","closePanel","togglePanel","handleKeyDown","e","handleClickOutside","handleDisplayModeChange","mode","updateMockDisplayMode","handleThemeChange","newTheme","updateMockTheme","applyProps","json","parsed","updateMockToolOutput","handlePropsChange","handlePropsBlur","handleReset","defaultJson","displayModeOptions","themeOptions","useCallback","React","createContext","useCallback","useContext","useEffect","useState","useSyncExternalStore","createWidgetClient","detectPlatform","OpenAIWidgetClient","MCPAppsWidgetClient","WidgetClientContext","createContext","WidgetProvider","children","loading","onError","client","setClient","useState","error","setError","isConnecting","setIsConnecting","useEffect","mounted","initClient","widgetClient","createWidgetClient","err","syncTheme","theme","React","useWidgetClient","key","useContext","subscribe","useCallback","onChange","getSnapshot","store","useSyncExternalStore","useCallTool","client","useWidgetClient","useCallback","name","args","useDisplayMode","useWidgetClient","useSyncExternalStore","useIsChatGptApp","useLocale","useWidgetClient","useMaxHeight","useWidgetClient","useCallback","useOpenExternal","client","useWidgetClient","useCallback","url","useCallback","useRequestDisplayMode","client","useWidgetClient","useCallback","mode","useSafeArea","useWidgetClient","useCallback","useSendFollowUp","client","useWidgetClient","useCallback","prompt","useTheme","useWidgetClient","useToolOutput","useWidgetClient","useToolResponseMetadata","useWidgetClient","useCallback","useEffect","useState","useWidgetState","defaultState","widgetStateFromWindow","useWidgetClient","widgetState","_setWidgetState","useState","useEffect","setWidgetState","useCallback","state","prevState","newState","detectPlatform","jsx","jsxs","LoadingWidget"]}
|
|
1
|
+
{"version":3,"sources":["../../src/mcp/react/components/initialize-next-in-chat-gpt.tsx","../../src/mcp/react/dev/dev-controls.tsx","../../src/mcp/react/dev/mock-openai.ts","../../src/mcp/react/hooks/use-call-tool.ts","../../src/mcp/react/hooks/use-widget.ts","../../src/mcp/react/widgets/widget-client.ts","../../src/mcp/react/hooks/use-display-mode.ts","../../src/mcp/react/hooks/use-is-chatgpt-app.ts","../../src/mcp/react/hooks/use-locale.ts","../../src/mcp/react/hooks/use-max-height.ts","../../src/mcp/react/hooks/use-open-external.ts","../../src/mcp/react/hooks/use-request-display-mode.ts","../../src/mcp/react/hooks/use-safe-area.ts","../../src/mcp/react/hooks/use-send-follow-up.ts","../../src/mcp/react/hooks/use-theme.ts","../../src/mcp/react/hooks/use-tool-output.ts","../../src/mcp/react/hooks/use-tool-response-metadata.ts","../../src/mcp/react/hooks/use-widget-state.ts","../../src/mcp/react/widgets/loading-widget.tsx"],"sourcesContent":["/**\n *\n * Initializes & patches Next.js functionalities\n * so it can be run inside the ChatGPT iframe:\n * - history.pushState / history.replaceState - Prevents full-origin URLs in history\n * - window.fetch - Rewrites same-origin requests to use the correct base URL\n * - html attribute observer - Prevents ChatGPT from modifying the root element\n *\n * More information about this component can be found here:\n *\n * https://vercel.com/blog/running-next-js-inside-chatgpt-a-deep-dive-into-native-app-integration\n */\nexport function InitializeNextJsInChatGpt({ baseUrl }: { baseUrl: string }) {\n\treturn (\n\t\t<>\n\t\t\t<base href={baseUrl}></base>\n\t\t\t<script>{`window.innerBaseUrl = ${JSON.stringify(baseUrl)}`}</script>\n\t\t\t<script>{`window.__isChatGptApp = typeof window.openai !== \"undefined\";`}</script>\n\t\t\t<script>\n\t\t\t\t{\"(\" +\n\t\t\t\t\t(() => {\n\t\t\t\t\t\tconst baseUrl = window.innerBaseUrl;\n\t\t\t\t\t\tconst htmlElement = document.documentElement;\n\t\t\t\t\t\tconst observer = new MutationObserver((mutations) => {\n\t\t\t\t\t\t\tmutations.forEach((mutation) => {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tmutation.type === \"attributes\" &&\n\t\t\t\t\t\t\t\t\tmutation.target === htmlElement\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tconst attrName = mutation.attributeName;\n\t\t\t\t\t\t\t\t\t// Preserve class/style so consumers can use html.dark { ... } for theming\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\tattrName &&\n\t\t\t\t\t\t\t\t\t\tattrName !== \"suppresshydrationwarning\" &&\n\t\t\t\t\t\t\t\t\t\tattrName !== \"class\" &&\n\t\t\t\t\t\t\t\t\t\tattrName !== \"style\"\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\thtmlElement.removeAttribute(attrName);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t\tobserver.observe(htmlElement, {\n\t\t\t\t\t\t\tattributes: true,\n\t\t\t\t\t\t\tattributeOldValue: true,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst originalReplaceState = history.replaceState;\n\t\t\t\t\t\thistory.replaceState = (_s, unused, url) => {\n\t\t\t\t\t\t\tconst u = new URL(url ?? \"\", window.location.href);\n\t\t\t\t\t\t\tconst href = u.pathname + u.search + u.hash;\n\t\t\t\t\t\t\toriginalReplaceState.call(history, unused, href);\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst originalPushState = history.pushState;\n\t\t\t\t\t\thistory.pushState = (_s, unused, url) => {\n\t\t\t\t\t\t\tconst u = new URL(url ?? \"\", window.location.href);\n\t\t\t\t\t\t\tconst href = u.pathname + u.search + u.hash;\n\t\t\t\t\t\t\toriginalPushState.call(history, unused, href);\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst appOrigin = new URL(baseUrl).origin;\n\t\t\t\t\t\tconst isInIframe = window.self !== window.top;\n\n\t\t\t\t\t\twindow.addEventListener(\n\t\t\t\t\t\t\t\"click\",\n\t\t\t\t\t\t\t(e) => {\n\t\t\t\t\t\t\t\tconst a = (e?.target as HTMLElement)?.closest(\"a\");\n\t\t\t\t\t\t\t\tif (!a || !a.href) return;\n\t\t\t\t\t\t\t\tconst url = new URL(a.href, window.location.href);\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\turl.origin !== window.location.origin &&\n\t\t\t\t\t\t\t\t\turl.origin !== appOrigin\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tif (window.openai) {\n\t\t\t\t\t\t\t\t\t\t\twindow.openai?.openExternal({ href: a.href });\n\t\t\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t\t\t\"openExternal failed, likely not in OpenAI client\",\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (isInIframe && window.location.origin !== appOrigin) {\n\t\t\t\t\t\t\tconst originalFetch = window.fetch;\n\n\t\t\t\t\t\t\twindow.fetch = (\n\t\t\t\t\t\t\t\tinput: URL | RequestInfo,\n\t\t\t\t\t\t\t\tinit?: RequestInit,\n\t\t\t\t\t\t\t): Promise<Response> => {\n\t\t\t\t\t\t\t\tlet url: URL;\n\t\t\t\t\t\t\t\tif (typeof input === \"string\" || input instanceof URL) {\n\t\t\t\t\t\t\t\t\turl = new URL(input, window.location.href);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\turl = new URL(input.url, window.location.href);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (url.origin === appOrigin) {\n\t\t\t\t\t\t\t\t\tif (typeof input === \"string\" || input instanceof URL) {\n\t\t\t\t\t\t\t\t\t\tinput = url.toString();\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tinput = new Request(url.toString(), input);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturn originalFetch.call(window, input, {\n\t\t\t\t\t\t\t\t\t\t...init,\n\t\t\t\t\t\t\t\t\t\tmode: \"cors\",\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (url.origin === window.location.origin) {\n\t\t\t\t\t\t\t\t\tconst newUrl = new URL(baseUrl);\n\t\t\t\t\t\t\t\t\tnewUrl.pathname = url.pathname;\n\t\t\t\t\t\t\t\t\tnewUrl.search = url.search;\n\t\t\t\t\t\t\t\t\tnewUrl.hash = url.hash;\n\t\t\t\t\t\t\t\t\turl = newUrl;\n\n\t\t\t\t\t\t\t\t\tif (typeof input === \"string\" || input instanceof URL) {\n\t\t\t\t\t\t\t\t\t\tinput = url.toString();\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tinput = new Request(url.toString(), input);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturn originalFetch.call(window, input, {\n\t\t\t\t\t\t\t\t\t\t...init,\n\t\t\t\t\t\t\t\t\t\tmode: \"cors\",\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn originalFetch.call(window, input, init);\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}).toString() +\n\t\t\t\t\t\")()\"}\n\t\t\t</script>\n\t\t</>\n\t);\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { DisplayMode, Theme } from \"../hooks/@types\";\nimport { SetGlobalsEvent } from \"../hooks/@types\";\nimport {\n\tgetMockState,\n\tinitializeMockOpenAI,\n\tupdateMockDisplayMode,\n\tupdateMockTheme,\n\tupdateMockToolOutput,\n} from \"./mock-openai\";\n\nconst MOCK_SAFE_AREA_HEIGHT = 150;\n\n// ============================================================================\n// SVG Icons\n// ============================================================================\n\nfunction DevIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\trole=\"img\"\n\t\t\taria-label=\"Dev Controls\"\n\t\t>\n\t\t\t{/* Minimal: two stacked squares offset */}\n\t\t\t<rect\n\t\t\t\tx=\"4\"\n\t\t\t\ty=\"4\"\n\t\t\t\twidth=\"10\"\n\t\t\t\theight=\"10\"\n\t\t\t\trx=\"2\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.5\"\n\t\t\t/>\n\t\t\t<rect\n\t\t\t\tx=\"10\"\n\t\t\t\ty=\"10\"\n\t\t\t\twidth=\"10\"\n\t\t\t\theight=\"10\"\n\t\t\t\trx=\"2\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.5\"\n\t\t\t\tfill=\"currentColor\"\n\t\t\t\tfillOpacity=\"0.15\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction CloseIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t>\n\t\t\t<path d=\"M18 6L6 18M6 6l12 12\" />\n\t\t</svg>\n\t);\n}\n\nfunction InlineIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<rect\n\t\t\t\tx=\"2\"\n\t\t\t\ty=\"4\"\n\t\t\t\twidth=\"12\"\n\t\t\t\theight=\"8\"\n\t\t\t\trx=\"1.5\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction PipIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<rect\n\t\t\t\tx=\"1.5\"\n\t\t\t\ty=\"3\"\n\t\t\t\twidth=\"13\"\n\t\t\t\theight=\"10\"\n\t\t\t\trx=\"1.5\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t/>\n\t\t\t<rect x=\"8.5\" y=\"7\" width=\"5\" height=\"4\" rx=\"0.75\" fill=\"currentColor\" />\n\t\t</svg>\n\t);\n}\n\nfunction FullscreenIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<path\n\t\t\t\td=\"M2 5.5V3.5C2 2.95 2.45 2.5 3 2.5H5.5M10.5 2.5H13C13.55 2.5 14 2.95 14 3.5V5.5M14 10.5V12.5C14 13.05 13.55 13.5 13 13.5H10.5M5.5 13.5H3C2.45 13.5 2 13.05 2 12.5V10.5\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction SunIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<circle cx=\"8\" cy=\"8\" r=\"2.5\" stroke=\"currentColor\" strokeWidth=\"1.25\" />\n\t\t\t<path\n\t\t\t\td=\"M8 2v1.5M8 12.5V14M2 8h1.5M12.5 8H14M4.11 4.11l1.06 1.06M10.83 10.83l1.06 1.06M4.11 11.89l1.06-1.06M10.83 5.17l1.06-1.06\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction MoonIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<path\n\t\t\t\td=\"M13.5 9.5a5.5 5.5 0 01-7-7 5.5 5.5 0 107 7z\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction ResetIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\tfill=\"none\"\n\t\t>\n\t\t\t<path\n\t\t\t\td=\"M2.5 8a5.5 5.5 0 019.37-3.9M13.5 8a5.5 5.5 0 01-9.37 3.9\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\td=\"M12.5 2v3h-3M3.5 14v-3h3\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"1.25\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\n// ============================================================================\n// Styles\n// ============================================================================\n\nconst panelAnimationStyles = `\n @keyframes devPanelSlideIn {\n 0% {\n opacity: 0;\n transform: translateY(12px) scale(0.98);\n }\n 100% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n\n @keyframes devPanelSlideOut {\n 0% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n 100% {\n opacity: 0;\n transform: translateY(8px) scale(0.98);\n }\n }\n\n .dev-panel-enter {\n animation: devPanelSlideIn 200ms cubic-bezier(0.16, 1, 0.3, 1) forwards;\n }\n\n .dev-panel-exit {\n animation: devPanelSlideOut 150ms cubic-bezier(0.4, 0, 1, 1) forwards;\n }\n\n .dev-json-editor::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n }\n\n .dev-json-editor::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .dev-json-editor::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.1);\n border-radius: 3px;\n }\n\n .dev-json-editor::-webkit-scrollbar-thumb:hover {\n background: rgba(255, 255, 255, 0.15);\n }\n`;\n\n// ============================================================================\n// Components\n// ============================================================================\n\ninterface DevControlsProps {\n\tdefaultProps?: Record<string, unknown>;\n\twidgetPaths?: string[];\n}\n\nexport function DevModeProvider({\n\tdefaultProps,\n\twidgetPaths,\n\tchildren,\n}: DevControlsProps & { children: React.ReactNode }) {\n\tconst [isInitialized, setIsInitialized] = useState(false);\n\tconst [isWidgetPage, setIsWidgetPage] = useState(false);\n\n\tuseEffect(() => {\n\t\t// When loaded inside the MCP harness iframe, skip the OpenAI mock\n\t\t// so the real MCPAppsWidgetClient + App class is used instead.\n\t\tconst params = new URLSearchParams(window.location.search);\n\t\tif (params.get(\"platform\") === \"mcp-apps\") {\n\t\t\tsetIsInitialized(true);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if current path is a widget page\n\t\tif (widgetPaths && widgetPaths.length > 0) {\n\t\t\tconst currentPath = window.location.pathname;\n\t\t\tconst isWidget = widgetPaths.some(\n\t\t\t\t(path) => currentPath === path || currentPath.startsWith(`${path}/`),\n\t\t\t);\n\t\t\tsetIsWidgetPage(isWidget);\n\n\t\t\tif (isWidget) {\n\t\t\t\tinitializeMockOpenAI(defaultProps);\n\t\t\t}\n\t\t} else {\n\t\t\t// If no widgetPaths specified, treat all pages as widget pages (backwards compat)\n\t\t\tinitializeMockOpenAI(defaultProps);\n\t\t\tsetIsWidgetPage(true);\n\t\t}\n\t\tsetIsInitialized(true);\n\t}, [defaultProps, widgetPaths]);\n\n\tif (!isInitialized) {\n\t\treturn null;\n\t}\n\n\t// If not a widget page, just render children without the dev frame\n\tif (!isWidgetPage) {\n\t\treturn <>{children}</>;\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t<WidgetPreviewFrame>{children}</WidgetPreviewFrame>\n\t\t\t<DevControls defaultProps={defaultProps} />\n\t\t</>\n\t);\n}\n\nfunction WidgetPreviewFrame({ children }: { children: React.ReactNode }) {\n\treturn (\n\t\t<div className=\"min-h-screen bg-[#212121] flex items-center justify-center p-8\">\n\t\t\t<div\n\t\t\t\tclassName=\"relative w-full max-w-md overflow-hidden rounded-2xl sm:rounded-3xl border border-[#414141]\"\n\t\t\t\tstyle={{\n\t\t\t\t\tboxShadow: \"0px 0px 0px 1px #414141, 0px 4px 14px rgba(0,0,0,0.24)\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ============================================================================\n// Segmented Control\n// ============================================================================\n\ninterface SegmentOption<T extends string> {\n\tvalue: T;\n\tlabel: string;\n\ticon: React.ReactNode;\n}\n\ninterface SegmentedControlProps<T extends string> {\n\toptions: SegmentOption<T>[];\n\tvalue: T;\n\tonChange: (value: T) => void;\n}\n\nfunction SegmentedControl<T extends string>({\n\toptions,\n\tvalue,\n\tonChange,\n}: SegmentedControlProps<T>) {\n\tconst activeIndex = options.findIndex((opt) => opt.value === value);\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"relative flex p-0.5 rounded-lg\"\n\t\t\tstyle={{\n\t\t\t\tbackground: \"rgba(255, 255, 255, 0.04)\",\n\t\t\t\tborder: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t}}\n\t\t>\n\t\t\t{/* Sliding indicator */}\n\t\t\t<div\n\t\t\t\tclassName=\"absolute top-0.5 bottom-0.5 rounded-md transition-transform duration-150 ease-out\"\n\t\t\t\tstyle={{\n\t\t\t\t\twidth: `calc(${100 / options.length}% - 2px)`,\n\t\t\t\t\tleft: \"2px\",\n\t\t\t\t\ttransform: `translateX(calc(${activeIndex * 100}% + ${activeIndex * 2}px))`,\n\t\t\t\t\tbackground: \"rgba(255, 255, 255, 0.1)\",\n\t\t\t\t}}\n\t\t\t/>\n\n\t\t\t{options.map((option) => (\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tkey={option.value}\n\t\t\t\t\tonClick={() => onChange(option.value)}\n\t\t\t\t\tclassName={`\n relative z-10 flex-1 flex items-center justify-center gap-1.5 px-2 py-1.5\n text-xs font-medium rounded-md transition-colors duration-150\n ${value === option.value ? \"text-white\" : \"text-gray-400 hover:text-gray-300\"}\n `}\n\t\t\t\t>\n\t\t\t\t\t{option.icon}\n\t\t\t\t\t<span className=\"capitalize\">{option.label}</span>\n\t\t\t\t</button>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n\n// ============================================================================\n// Main DevControls Component\n// ============================================================================\n\nfunction DevControls({ defaultProps }: DevControlsProps) {\n\tconst [isOpen, setIsOpen] = useState(() => {\n\t\tif (typeof window === \"undefined\") return false;\n\t\treturn localStorage.getItem(\"dev-controls-open\") === \"true\";\n\t});\n\tconst [isAnimating, setIsAnimating] = useState(false);\n\tconst [shouldRender, setShouldRender] = useState(isOpen);\n\tconst [displayMode, setDisplayMode] = useState<DisplayMode>(\"inline\");\n\tconst [theme, setTheme] = useState<Theme>(\"dark\");\n\tconst [showSafeArea, setShowSafeArea] = useState(false);\n\tconst [propsJson, setPropsJson] = useState(() =>\n\t\tJSON.stringify(defaultProps ?? {}, null, 2),\n\t);\n\tconst [jsonError, setJsonError] = useState<string | null>(null);\n\tconst debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst panelRef = useRef<HTMLDivElement>(null);\n\tconst containerRef = useRef<HTMLDivElement>(null);\n\n\t// Initialize from mock state\n\tuseEffect(() => {\n\t\tconst state = getMockState();\n\t\tsetDisplayMode(state.displayMode);\n\t\tsetTheme(state.theme);\n\t}, []);\n\n\t// Update safeArea when toggled\n\tuseEffect(() => {\n\t\tif (typeof window === \"undefined\") return;\n\t\tif (!window.openai) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: window.openai may not exist yet\n\t\t\t(window as any).openai = {};\n\t\t}\n\t\twindow.openai.safeArea = {\n\t\t\tinsets: {\n\t\t\t\ttop: 0,\n\t\t\t\tbottom: showSafeArea ? MOCK_SAFE_AREA_HEIGHT : 0,\n\t\t\t\tleft: 0,\n\t\t\t\tright: 0,\n\t\t\t},\n\t\t};\n\t\twindow.dispatchEvent(\n\t\t\tnew SetGlobalsEvent({ globals: { safeArea: window.openai.safeArea } }),\n\t\t);\n\t}, [showSafeArea]);\n\n\t// Persist open state\n\tuseEffect(() => {\n\t\tlocalStorage.setItem(\"dev-controls-open\", String(isOpen));\n\t}, [isOpen]);\n\n\tconst openPanel = useCallback(() => {\n\t\tsetShouldRender(true);\n\t\t// Small delay to ensure DOM is ready for animation\n\t\trequestAnimationFrame(() => {\n\t\t\tsetIsOpen(true);\n\t\t});\n\t}, []);\n\n\tconst closePanel = useCallback(() => {\n\t\tsetIsAnimating(true);\n\t\tsetIsOpen(false);\n\t\t// Wait for exit animation\n\t\tsetTimeout(() => {\n\t\t\tsetShouldRender(false);\n\t\t\tsetIsAnimating(false);\n\t\t}, 150);\n\t}, []);\n\n\tconst togglePanel = useCallback(() => {\n\t\tif (isOpen) {\n\t\t\tclosePanel();\n\t\t} else {\n\t\t\topenPanel();\n\t\t}\n\t}, [isOpen, openPanel, closePanel]);\n\n\t// Keyboard shortcuts\n\tuseEffect(() => {\n\t\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\t\t// Cmd/Ctrl + Shift + D to toggle\n\t\t\tif ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === \"d\") {\n\t\t\t\te.preventDefault();\n\t\t\t\ttogglePanel();\n\t\t\t}\n\t\t\t// Escape to close\n\t\t\tif (e.key === \"Escape\" && isOpen) {\n\t\t\t\tclosePanel();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"keydown\", handleKeyDown);\n\t\treturn () => window.removeEventListener(\"keydown\", handleKeyDown);\n\t}, [isOpen, togglePanel, closePanel]);\n\n\t// Click outside to close\n\tuseEffect(() => {\n\t\tif (!isOpen) return;\n\n\t\tconst handleClickOutside = (e: MouseEvent) => {\n\t\t\tif (\n\t\t\t\tcontainerRef.current &&\n\t\t\t\t!containerRef.current.contains(e.target as Node)\n\t\t\t) {\n\t\t\t\tclosePanel();\n\t\t\t}\n\t\t};\n\n\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\treturn () => document.removeEventListener(\"mousedown\", handleClickOutside);\n\t}, [isOpen, closePanel]);\n\n\tconst handleDisplayModeChange = useCallback((mode: DisplayMode) => {\n\t\tsetDisplayMode(mode);\n\t\tupdateMockDisplayMode(mode);\n\t}, []);\n\n\tconst handleThemeChange = useCallback((newTheme: Theme) => {\n\t\tsetTheme(newTheme);\n\t\tupdateMockTheme(newTheme);\n\t}, []);\n\n\tconst applyProps = useCallback((json: string) => {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(json);\n\t\t\tsetJsonError(null);\n\t\t\tupdateMockToolOutput(parsed);\n\t\t} catch {\n\t\t\tsetJsonError(\"Invalid JSON\");\n\t\t}\n\t}, []);\n\n\tconst handlePropsChange = useCallback(\n\t\t(json: string) => {\n\t\t\tsetPropsJson(json);\n\t\t\tif (debounceRef.current) {\n\t\t\t\tclearTimeout(debounceRef.current);\n\t\t\t}\n\t\t\tdebounceRef.current = setTimeout(() => {\n\t\t\t\tapplyProps(json);\n\t\t\t}, 500);\n\t\t},\n\t\t[applyProps],\n\t);\n\n\tconst handlePropsBlur = useCallback(() => {\n\t\tif (debounceRef.current) {\n\t\t\tclearTimeout(debounceRef.current);\n\t\t}\n\t\tapplyProps(propsJson);\n\t}, [applyProps, propsJson]);\n\n\tconst handleReset = useCallback(() => {\n\t\tconst defaultJson = JSON.stringify(defaultProps ?? {}, null, 2);\n\t\tsetPropsJson(defaultJson);\n\t\tsetJsonError(null);\n\t\tupdateMockToolOutput(defaultProps ?? {});\n\t\tsetDisplayMode(\"inline\");\n\t\tupdateMockDisplayMode(\"inline\");\n\t\tsetTheme(\"dark\");\n\t\tupdateMockTheme(\"dark\");\n\t}, [defaultProps]);\n\n\tconst displayModeOptions: SegmentOption<DisplayMode>[] = [\n\t\t{\n\t\t\tvalue: \"inline\",\n\t\t\tlabel: \"inline\",\n\t\t\ticon: <InlineIcon className=\"w-3.5 h-3.5\" />,\n\t\t},\n\t\t{ value: \"pip\", label: \"pip\", icon: <PipIcon className=\"w-3.5 h-3.5\" /> },\n\t\t{\n\t\t\tvalue: \"fullscreen\",\n\t\t\tlabel: \"full\",\n\t\t\ticon: <FullscreenIcon className=\"w-3.5 h-3.5\" />,\n\t\t},\n\t];\n\n\tconst themeOptions: SegmentOption<Theme>[] = [\n\t\t{\n\t\t\tvalue: \"light\",\n\t\t\tlabel: \"light\",\n\t\t\ticon: <SunIcon className=\"w-3.5 h-3.5\" />,\n\t\t},\n\t\t{\n\t\t\tvalue: \"dark\",\n\t\t\tlabel: \"dark\",\n\t\t\ticon: <MoonIcon className=\"w-3.5 h-3.5\" />,\n\t\t},\n\t];\n\n\treturn (\n\t\t<>\n\t\t\t{/* Inject animation styles */}\n\t\t\t{/** biome-ignore lint/security/noDangerouslySetInnerHtml: we need to inject styles into the DOM */}\n\t\t\t<style dangerouslySetInnerHTML={{ __html: panelAnimationStyles }} />\n\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\tclassName=\"fixed bottom-4 right-4 z-[9999] font-['Inter',_system-ui,_sans-serif]\"\n\t\t\t>\n\t\t\t\t{/* Toggle Button */}\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={togglePanel}\n\t\t\t\t\tclassName=\"group relative flex items-center justify-center w-11 h-11 rounded-xl transition-all duration-200 ease-out hover:scale-105 active:scale-95\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackground: \"rgba(14, 14, 16, 0.95)\",\n\t\t\t\t\t\tbackdropFilter: \"blur(16px)\",\n\t\t\t\t\t\tWebkitBackdropFilter: \"blur(16px)\",\n\t\t\t\t\t\tborder: \"1px solid rgba(255, 255, 255, 0.08)\",\n\t\t\t\t\t\tboxShadow: `\n 0 4px 12px rgba(0, 0, 0, 0.4),\n 0 0 0 1px rgba(255, 255, 255, 0.05),\n inset 0 1px 0 rgba(255, 255, 255, 0.04)\n `,\n\t\t\t\t\t}}\n\t\t\t\t\taria-label=\"Toggle Dev Controls\"\n\t\t\t\t\taria-expanded={isOpen}\n\t\t\t\t>\n\t\t\t\t\t<DevIcon\n\t\t\t\t\t\tclassName={`w-5 h-5 transition-all duration-200 ${\n\t\t\t\t\t\t\tisOpen\n\t\t\t\t\t\t\t\t? \"text-indigo-400 scale-110\"\n\t\t\t\t\t\t\t\t: \"text-gray-300 group-hover:text-white\"\n\t\t\t\t\t\t}`}\n\t\t\t\t\t/>\n\n\t\t\t\t\t{/* Subtle glow on hover */}\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"absolute inset-0 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none\"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tbackground:\n\t\t\t\t\t\t\t\t\"radial-gradient(circle at center, rgba(99, 102, 241, 0.15) 0%, transparent 70%)\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t</button>\n\n\t\t\t\t{/* Panel */}\n\t\t\t\t{shouldRender && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tref={panelRef}\n\t\t\t\t\t\tclassName={`absolute bottom-14 right-0 w-80 ${\n\t\t\t\t\t\t\tisOpen && !isAnimating ? \"dev-panel-enter\" : \"dev-panel-exit\"\n\t\t\t\t\t\t}`}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tmaxHeight: \"calc(100vh - 120px)\",\n\t\t\t\t\t\t\tbackground: \"rgba(14, 14, 16, 0.92)\",\n\t\t\t\t\t\t\tbackdropFilter: \"blur(24px)\",\n\t\t\t\t\t\t\tWebkitBackdropFilter: \"blur(24px)\",\n\t\t\t\t\t\t\tborder: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t\t\t\t\tborderRadius: \"16px\",\n\t\t\t\t\t\t\tboxShadow: `\n 0 25px 50px -12px rgba(0, 0, 0, 0.6),\n 0 0 0 1px rgba(255, 255, 255, 0.05),\n inset 0 1px 0 rgba(255, 255, 255, 0.04)\n `,\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{/* Header */}\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"flex items-center justify-between px-4 py-3\"\n\t\t\t\t\t\t\tstyle={{ borderBottom: \"1px solid rgba(255, 255, 255, 0.06)\" }}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<DevIcon className=\"w-4 h-4 text-gray-400\" />\n\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium text-white\">\n\t\t\t\t\t\t\t\t\tDev Controls\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t{/* Keyboard shortcut badge */}\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclassName=\"text-[10px] font-medium text-gray-500 px-1.5 py-0.5 rounded\"\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tbackground: \"rgba(255, 255, 255, 0.04)\",\n\t\t\t\t\t\t\t\t\t\tborder: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{typeof navigator !== \"undefined\" &&\n\t\t\t\t\t\t\t\t\tnavigator.platform?.includes(\"Mac\")\n\t\t\t\t\t\t\t\t\t\t? \"⌘⇧D\"\n\t\t\t\t\t\t\t\t\t\t: \"Ctrl+Shift+D\"}\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t{/* Close button */}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={closePanel}\n\t\t\t\t\t\t\t\t\tclassName=\"p-1 rounded-md text-gray-500 hover:text-gray-300 hover:bg-white/5 transition-colors\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<CloseIcon className=\"w-4 h-4\" />\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Content */}\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"p-4 space-y-5 overflow-y-auto\"\n\t\t\t\t\t\t\tstyle={{ maxHeight: \"calc(100vh - 200px)\" }}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{/* Display Mode */}\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\thtmlFor=\"display-mode\"\n\t\t\t\t\t\t\t\t\tclassName=\"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tDisplay Mode\n\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t<SegmentedControl\n\t\t\t\t\t\t\t\t\toptions={displayModeOptions}\n\t\t\t\t\t\t\t\t\tvalue={displayMode}\n\t\t\t\t\t\t\t\t\tonChange={handleDisplayModeChange}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Theme */}\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\thtmlFor=\"theme\"\n\t\t\t\t\t\t\t\t\tclassName=\"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tTheme\n\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t<SegmentedControl\n\t\t\t\t\t\t\t\t\toptions={themeOptions}\n\t\t\t\t\t\t\t\t\tvalue={theme}\n\t\t\t\t\t\t\t\t\tonChange={handleThemeChange}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Safe Area Mock */}\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\thtmlFor=\"safe-area\"\n\t\t\t\t\t\t\t\t\tclassName=\"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tSafe Area (Chat Input)\n\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => setShowSafeArea(!showSafeArea)}\n\t\t\t\t\t\t\t\t\tclassName={`w-full flex items-center justify-between px-3 py-2.5 rounded-lg text-xs font-medium transition-all duration-150 ${\n\t\t\t\t\t\t\t\t\t\tshowSafeArea ? \"text-emerald-400\" : \"text-gray-400\"\n\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tbackground: showSafeArea\n\t\t\t\t\t\t\t\t\t\t\t? \"rgba(34, 197, 94, 0.1)\"\n\t\t\t\t\t\t\t\t\t\t\t: \"rgba(255, 255, 255, 0.04)\",\n\t\t\t\t\t\t\t\t\t\tborder: showSafeArea\n\t\t\t\t\t\t\t\t\t\t\t? \"1px solid rgba(34, 197, 94, 0.3)\"\n\t\t\t\t\t\t\t\t\t\t\t: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span>Mock ChatGPT Input Bar</span>\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName={`px-2 py-0.5 rounded text-[10px] font-semibold ${\n\t\t\t\t\t\t\t\t\t\t\tshowSafeArea\n\t\t\t\t\t\t\t\t\t\t\t\t? \"bg-emerald-500/20 text-emerald-400\"\n\t\t\t\t\t\t\t\t\t\t\t\t: \"bg-gray-500/20 text-gray-500\"\n\t\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{showSafeArea ? \"ON\" : \"OFF\"}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t{showSafeArea && (\n\t\t\t\t\t\t\t\t\t<p className=\"text-[10px] text-gray-500 mt-1.5\">\n\t\t\t\t\t\t\t\t\t\tbottom: {MOCK_SAFE_AREA_HEIGHT}px\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Widget Props */}\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\thtmlFor=\"widget-props\"\n\t\t\t\t\t\t\t\t\tclassName=\"text-[11px] font-medium uppercase tracking-wider text-gray-500 block mb-2\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tWidget Props\n\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\t\t\tvalue={propsJson}\n\t\t\t\t\t\t\t\t\tonChange={(e) => handlePropsChange(e.target.value)}\n\t\t\t\t\t\t\t\t\tonBlur={handlePropsBlur}\n\t\t\t\t\t\t\t\t\tclassName=\"dev-json-editor w-full min-h-[160px] text-xs text-gray-200 p-3 rounded-lg resize-none focus:outline-none transition-colors\"\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tbackground: \"rgba(0, 0, 0, 0.3)\",\n\t\t\t\t\t\t\t\t\t\tborder: jsonError\n\t\t\t\t\t\t\t\t\t\t\t? \"1px solid rgba(239, 68, 68, 0.5)\"\n\t\t\t\t\t\t\t\t\t\t\t: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t\t\t\t\t\t\t\tfontFamily:\n\t\t\t\t\t\t\t\t\t\t\t\"'JetBrains Mono', 'SF Mono', 'Fira Code', monospace\",\n\t\t\t\t\t\t\t\t\t\tlineHeight: 1.6,\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonFocus={(e) => {\n\t\t\t\t\t\t\t\t\t\tif (!jsonError) {\n\t\t\t\t\t\t\t\t\t\t\te.target.style.borderColor = \"rgba(99, 102, 241, 0.5)\";\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonBlurCapture={(e) => {\n\t\t\t\t\t\t\t\t\t\tif (!jsonError) {\n\t\t\t\t\t\t\t\t\t\t\te.target.style.borderColor = \"rgba(255, 255, 255, 0.06)\";\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t{jsonError && (\n\t\t\t\t\t\t\t\t\t<p className=\"text-red-400 text-[11px] mt-1.5 flex items-center gap-1\">\n\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"Error\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"w-3 h-3\"\n\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\t\t\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<path d=\"M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM7 4.5h2v4H7v-4zm0 5h2v2H7v-2z\" />\n\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t{jsonError}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Reset Button */}\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={handleReset}\n\t\t\t\t\t\t\t\tclassName=\"w-full flex items-center justify-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-gray-400 transition-all duration-150 hover:text-gray-200\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tbackground: \"rgba(255, 255, 255, 0.04)\",\n\t\t\t\t\t\t\t\t\tborder: \"1px solid rgba(255, 255, 255, 0.06)\",\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tonMouseEnter={(e) => {\n\t\t\t\t\t\t\t\t\te.currentTarget.style.background =\n\t\t\t\t\t\t\t\t\t\t\"rgba(255, 255, 255, 0.08)\";\n\t\t\t\t\t\t\t\t\te.currentTarget.style.borderColor =\n\t\t\t\t\t\t\t\t\t\t\"rgba(255, 255, 255, 0.1)\";\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tonMouseLeave={(e) => {\n\t\t\t\t\t\t\t\t\te.currentTarget.style.background =\n\t\t\t\t\t\t\t\t\t\t\"rgba(255, 255, 255, 0.04)\";\n\t\t\t\t\t\t\t\t\te.currentTarget.style.borderColor =\n\t\t\t\t\t\t\t\t\t\t\"rgba(255, 255, 255, 0.06)\";\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<ResetIcon className=\"w-3.5 h-3.5\" />\n\t\t\t\t\t\t\t\tReset to Defaults\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Mock ChatGPT Input Bar */}\n\t\t\t{showSafeArea && (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"fixed bottom-0 left-0 right-0 z-[9998] flex items-center justify-center pointer-events-none\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\theight: `${MOCK_SAFE_AREA_HEIGHT}px`,\n\t\t\t\t\t\tbackground:\n\t\t\t\t\t\t\t\"linear-gradient(to top, #1a1a1a 0%, #1a1a1a 80%, transparent 100%)\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<div className=\"w-full max-w-2xl mx-4 px-4 py-3 rounded-2xl bg-[#2f2f2f] border border-[#424242] flex items-center gap-3\">\n\t\t\t\t\t\t<div className=\"w-8 h-8 rounded-full bg-[#424242] flex items-center justify-center\">\n\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\tclassName=\"w-4 h-4 text-gray-400\"\n\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t\t\td=\"M12 4v16m8-8H4\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex-1 text-gray-400 text-sm\">\n\t\t\t\t\t\t\tAsk me anything...\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"w-8 h-8 rounded-full bg-[#424242] flex items-center justify-center\">\n\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\tclassName=\"w-4 h-4 text-gray-400\"\n\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t\t\td=\"M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"absolute bottom-1 text-[10px] text-gray-600 font-mono\">\n\t\t\t\t\t\tMock SafeArea: bottom={MOCK_SAFE_AREA_HEIGHT}px\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</>\n\t);\n}\n","import {\n\ttype DisplayMode,\n\ttype OpenAIGlobals,\n\tSetGlobalsEvent,\n\ttype Theme,\n} from \"../hooks/@types\";\n\nconst DEFAULT_MOCK_CONFIG: Omit<OpenAIGlobals, \"setWidgetState\"> = {\n\ttheme: \"dark\",\n\tuserAgent: {\n\t\tdevice: { type: \"desktop\" },\n\t\tcapabilities: { hover: true, touch: false },\n\t},\n\tlocale: \"en\",\n\tmaxHeight: 800,\n\tdisplayMode: \"inline\",\n\tsafeArea: { insets: { top: 0, bottom: 0, left: 0, right: 0 } },\n\ttoolInput: {},\n\ttoolOutput: null,\n\ttoolResponseMetadata: null,\n\twidgetState: null,\n};\n\nlet mockState: Omit<OpenAIGlobals, \"setWidgetState\"> = {\n\t...DEFAULT_MOCK_CONFIG,\n};\n\nexport function initializeMockOpenAI(\n\tinitialToolOutput?: Record<string, unknown>,\n): void {\n\tif (typeof window === \"undefined\") return;\n\tif (window.openai) return; // Already initialized (real ChatGPT or previous mock)\n\n\tmockState = {\n\t\t...DEFAULT_MOCK_CONFIG,\n\t\ttoolOutput: initialToolOutput ?? null,\n\t};\n\n\twindow.openai = {\n\t\t...mockState,\n\t\t// Mock API functions\n\t\trequestDisplayMode: async ({ mode }) => {\n\t\t\tupdateMockGlobal(\"displayMode\", mode);\n\t\t\treturn { mode };\n\t\t},\n\t\tcallTool: async (name, args) => {\n\t\t\tconsole.log(`[DevMode] callTool: ${name}`, args);\n\t\t\treturn { result: JSON.stringify({ mock: true, tool: name, args }) };\n\t\t},\n\t\tsendFollowUpMessage: async ({ prompt }) => {\n\t\t\tconsole.log(`[DevMode] sendFollowUpMessage: ${prompt}`);\n\t\t},\n\t\topenExternal: ({ href }) => {\n\t\t\tconsole.log(`[DevMode] openExternal: ${href}`);\n\t\t\twindow.open(href, \"_blank\");\n\t\t},\n\t\tsetWidgetState: async (state) => {\n\t\t\tupdateMockGlobal(\"widgetState\", state);\n\t\t},\n\t};\n\n\t// Dispatch initial event so hooks pick up the values\n\twindow.dispatchEvent(new SetGlobalsEvent({ globals: mockState }));\n}\n\nexport function updateMockGlobal<K extends keyof OpenAIGlobals>(\n\tkey: K,\n\tvalue: OpenAIGlobals[K],\n): void {\n\tif (typeof window === \"undefined\" || !window.openai) return;\n\n\t(mockState as Record<string, unknown>)[key] = value;\n\t(window.openai as Record<string, unknown>)[key] = value;\n\n\twindow.dispatchEvent(new SetGlobalsEvent({ globals: { [key]: value } }));\n}\n\nexport function getMockState(): typeof mockState {\n\treturn { ...mockState };\n}\n\nexport function updateMockToolOutput(props: Record<string, unknown>): void {\n\tupdateMockGlobal(\"toolOutput\", props);\n}\n\nexport function updateMockDisplayMode(mode: DisplayMode): void {\n\tupdateMockGlobal(\"displayMode\", mode);\n}\n\nexport function updateMockTheme(theme: Theme): void {\n\tupdateMockGlobal(\"theme\", theme);\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport type { ToolCallResult } from \"../widgets/widget-client\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get a function to call other tools.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns A function to call tools with their name and arguments\n */\nexport function useCallTool(): (\n\tname: string,\n\targs: Record<string, unknown>,\n) => Promise<ToolCallResult> {\n\tconst client = useWidgetClient();\n\treturn useCallback(\n\t\t(name: string, args: Record<string, unknown>) =>\n\t\t\tclient.callTool(name, args),\n\t\t[client],\n\t);\n}\n","\"use client\";\n\nimport React, {\n\tcreateContext,\n\ttype ReactNode,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseState,\n\tuseSyncExternalStore,\n} from \"react\";\nimport {\n\tcreateWidgetClient,\n\ttype UnifiedWidgetClient,\n} from \"../widgets/widget-client\";\nimport type { DisplayMode, SafeArea, Theme, UnknownObject } from \"./@types\";\n\n/**\n * Context for the unified widget client.\n */\nconst WidgetClientContext = createContext<UnifiedWidgetClient | null>(null);\n\n/**\n * Provider props\n */\ninterface WidgetProviderProps {\n\tchildren: ReactNode;\n\t/** Optional loading component while connecting */\n\tloading?: ReactNode;\n\t/** Optional error component */\n\tonError?: (error: Error) => ReactNode;\n}\n\n/**\n * Provider component that initializes the correct widget client based on platform.\n * Wrap your widget component with this provider.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <WidgetProvider loading={<Spinner />}>\n * <MyWidget />\n * </WidgetProvider>\n * );\n * }\n * ```\n */\nexport function WidgetProvider({\n\tchildren,\n\tloading = null,\n\tonError,\n}: WidgetProviderProps) {\n\tconst [client, setClient] = useState<UnifiedWidgetClient | null>(null);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [isConnecting, setIsConnecting] = useState(true);\n\n\tuseEffect(() => {\n\t\tlet mounted = true;\n\t\tlet activeClient: UnifiedWidgetClient | null = null;\n\n\t\tasync function initClient() {\n\t\t\ttry {\n\t\t\t\tconst widgetClient = await createWidgetClient();\n\n\t\t\t\tawait widgetClient.connect();\n\n\t\t\t\tif (mounted) {\n\t\t\t\t\tactiveClient = widgetClient;\n\t\t\t\t\tsetClient(widgetClient);\n\t\t\t\t\tsetIsConnecting(false);\n\t\t\t\t} else {\n\t\t\t\t\t// Component unmounted during connect — clean up\n\t\t\t\t\twidgetClient.close();\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tif (mounted) {\n\t\t\t\t\tconsole.error(\"error\", err);\n\t\t\t\t\tsetError(err instanceof Error ? err : new Error(String(err)));\n\t\t\t\t\tsetIsConnecting(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tinitClient();\n\n\t\treturn () => {\n\t\t\tmounted = false;\n\t\t\tactiveClient?.close();\n\t\t};\n\t}, []);\n\n\t// Sync theme to <html> class so consumers can use html.dark { ... } or dark: variants\n\tuseEffect(() => {\n\t\tif (!client) return;\n\n\t\tconst syncTheme = (theme: Theme) => {\n\t\t\tdocument.documentElement.classList.toggle(\"dark\", theme === \"dark\");\n\t\t};\n\n\t\tsyncTheme(client.getTheme());\n\t\treturn client.onThemeChange(syncTheme);\n\t}, [client]);\n\n\tif (error && onError) {\n\t\treturn React.createElement(React.Fragment, null, onError(error));\n\t}\n\n\tif (isConnecting || !client) {\n\t\treturn React.createElement(React.Fragment, null, loading);\n\t}\n\n\treturn React.createElement(\n\t\tWidgetClientContext.Provider,\n\t\t{ value: client },\n\t\tchildren,\n\t);\n}\n\n/**\n * Keys that can be selected from the widget client.\n */\ntype WidgetKey =\n\t| \"toolOutput\"\n\t| \"theme\"\n\t| \"displayMode\"\n\t| \"locale\"\n\t| \"safeArea\"\n\t| \"maxHeight\"\n\t| \"toolResponseMetadata\"\n\t| \"widgetState\";\n\n/**\n * Value types for each widget key.\n */\ntype WidgetKeyValues = {\n\ttoolOutput: Record<string, unknown> | null;\n\ttheme: Theme;\n\tdisplayMode: DisplayMode;\n\tlocale: string;\n\tsafeArea: SafeArea | null;\n\tmaxHeight: number | null;\n\ttoolResponseMetadata: UnknownObject | null;\n\twidgetState: UnknownObject | null;\n};\n\n/**\n * Get the unified widget client instance.\n * Must be used within a WidgetProvider.\n *\n * @example\n * ```tsx\n * // Full client for actions\n * const client = useWidgetClient();\n * client.callTool(\"foo\", {});\n *\n * // Key selector for reactive values\n * const toolOutput = useWidgetClient(\"toolOutput\");\n * const theme = useWidgetClient(\"theme\");\n * ```\n */\nexport function useWidgetClient(): UnifiedWidgetClient;\nexport function useWidgetClient<K extends WidgetKey>(\n\tkey: K,\n): WidgetKeyValues[K];\nexport function useWidgetClient<K extends WidgetKey>(key?: K) {\n\tconst client = useContext(WidgetClientContext);\n\n\tif (!client) {\n\t\tthrow new Error(\"useWidgetClient must be used within a WidgetProvider\");\n\t}\n\n\t// Key selector - use useSyncExternalStore\n\tconst subscribe = useCallback(\n\t\t(onChange: () => void) => {\n\t\t\tif (key === \"toolOutput\") return client.onToolResult(() => onChange());\n\t\t\tif (key === \"theme\") return client.onThemeChange(() => onChange());\n\t\t\tif (key === \"displayMode\")\n\t\t\t\treturn client.onDisplayModeChange(() => onChange());\n\t\t\tif (key === \"safeArea\") return client.onSafeAreaChange(() => onChange());\n\t\t\tif (key === \"maxHeight\")\n\t\t\t\treturn client.onMaxHeightChange(() => onChange());\n\t\t\tif (key === \"toolResponseMetadata\")\n\t\t\t\treturn client.onToolResponseMetadataChange(() => onChange());\n\t\t\tif (key === \"widgetState\")\n\t\t\t\treturn client.onWidgetStateChange(() => onChange());\n\t\t\treturn () => {};\n\t\t},\n\t\t[client, key],\n\t);\n\n\tconst getSnapshot = useCallback(() => {\n\t\tif (key === \"toolOutput\") return client.getToolOutput();\n\t\tif (key === \"theme\") return client.getTheme();\n\t\tif (key === \"displayMode\") return client.getDisplayMode();\n\t\tif (key === \"locale\") return client.getLocale();\n\t\tif (key === \"safeArea\") return client.getSafeArea();\n\t\tif (key === \"maxHeight\") return client.getMaxHeight();\n\t\tif (key === \"toolResponseMetadata\") return client.getToolResponseMetadata();\n\t\tif (key === \"widgetState\") return client.getWidgetState();\n\t\treturn null;\n\t}, [client, key]);\n\n\tconst store = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n\t// No key - return full client\n\tif (!key) return client;\n\n\treturn store;\n}\n","import type {\n\tDisplayMode,\n\tSafeArea,\n\tTheme,\n\tUnknownObject,\n} from \"../hooks/@types\";\n\n/**\n * Result from calling a tool\n */\nexport type ToolCallResult = {\n\tcontent?: Array<{ type: string; text?: string }>;\n\tstructuredContent?: Record<string, unknown>;\n};\n\n/**\n * Tool result notification (what the host pushes to the widget)\n */\nexport type ToolResult = {\n\tcontent?: Array<{ type: string; text?: string }>;\n\tstructuredContent?: Record<string, unknown>;\n};\n\n/**\n * Host context - all values available from the host.\n */\nexport type HostContext = {\n\ttheme: Theme;\n\tlocale: string;\n\tdisplayMode: DisplayMode;\n\tmaxHeight: number | null;\n\tsafeArea: SafeArea | null;\n\ttoolOutput: UnknownObject | null;\n\ttoolResponseMetadata: UnknownObject | null;\n\twidgetState: UnknownObject | null;\n};\n\n/**\n * Store interface for useSyncExternalStore compatibility.\n */\nexport type HostContextStore<K extends keyof HostContext> = {\n\tsubscribe: (onStoreChange: () => void) => () => void;\n\tgetSnapshot: () => HostContext[K];\n};\n\n/**\n * Unified widget client interface that works on both OpenAI and MCP Apps.\n *\n * Platform-specific behavior:\n * - Display mode: OpenAI-only. MCP Apps returns \"inline\" and requestDisplayMode is a no-op.\n * - Follow-up messages: Unified API, different underlying implementations.\n */\nexport interface UnifiedWidgetClient {\n\t/**\n\t * Connect to the host. Must be called before using other methods.\n\t * On OpenAI, this is a no-op (already connected via window.openai).\n\t * On MCP Apps, this establishes the postMessage connection.\n\t */\n\tconnect(): Promise<void>;\n\n\t/**\n\t * Close the connection to the host and clean up resources.\n\t * On OpenAI, this is a no-op.\n\t * On MCP Apps, this closes the transport and removes event listeners.\n\t */\n\tclose(): Promise<void>;\n\n\t/**\n\t * Get the tool output (structured content returned by the tool handler).\n\t * This is the main data source for widget rendering.\n\t */\n\tgetToolOutput<T = Record<string, unknown>>(): T | null;\n\n\t/**\n\t * Register a callback for when tool results are received.\n\t * On OpenAI, this subscribes to toolOutput changes.\n\t * On MCP Apps, this sets app.ontoolresult.\n\t */\n\tonToolResult(callback: (result: ToolResult) => void): () => void;\n\n\t/**\n\t * Call another tool on the server.\n\t */\n\tcallTool(\n\t\tname: string,\n\t\targs: Record<string, unknown>,\n\t): Promise<ToolCallResult>;\n\n\t/**\n\t * Open an external URL.\n\t * On OpenAI: openai.openExternal({ href })\n\t * On MCP Apps: app.sendOpenLink(url)\n\t */\n\topenExternal(url: string): void;\n\n\t/**\n\t * Send a follow-up message to the AI.\n\t * On OpenAI: openai.sendFollowUpMessage({ prompt })\n\t * On MCP Apps: app.sendMessages([{ role: 'user', content: { type: 'text', text: prompt } }])\n\t */\n\tsendFollowUp(prompt: string): void;\n\n\t/**\n\t * Get the current theme.\n\t */\n\tgetTheme(): Theme;\n\n\t/**\n\t * Subscribe to theme changes.\n\t */\n\tonThemeChange(callback: (theme: Theme) => void): () => void;\n\n\t/**\n\t * Get the current locale.\n\t */\n\tgetLocale(): string;\n\n\t/**\n\t * Get the current display mode.\n\t * OpenAI-only: returns \"pip\" | \"inline\" | \"fullscreen\"\n\t * MCP Apps: always returns \"inline\"\n\t */\n\tgetDisplayMode(): DisplayMode;\n\n\t/**\n\t * Request a display mode change.\n\t * OpenAI-only: requests the mode from the host.\n\t * MCP Apps: no-op (returns current mode).\n\t */\n\trequestDisplayMode(mode: DisplayMode): Promise<DisplayMode>;\n\n\t/**\n\t * Subscribe to display mode changes.\n\t * OpenAI-only: subscribes to displayMode changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonDisplayModeChange(callback: (mode: DisplayMode) => void): () => void;\n\n\t/**\n\t * Get the safe area insets.\n\t * OpenAI-only: returns insets from window.openai.safeArea.\n\t * MCP Apps: returns null.\n\t */\n\tgetSafeArea(): SafeArea | null;\n\n\t/**\n\t * Subscribe to safe area changes.\n\t * OpenAI-only: subscribes to safeArea changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonSafeAreaChange(callback: (safeArea: SafeArea | null) => void): () => void;\n\n\t/**\n\t * Get the max height constraint.\n\t * OpenAI-only: returns maxHeight from window.openai.maxHeight.\n\t * MCP Apps: returns null.\n\t */\n\tgetMaxHeight(): number | null;\n\n\t/**\n\t * Subscribe to max height changes.\n\t * OpenAI-only: subscribes to maxHeight changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonMaxHeightChange(callback: (maxHeight: number | null) => void): () => void;\n\n\t/**\n\t * Get the tool response metadata.\n\t * OpenAI-only: returns metadata from window.openai.toolResponseMetadata.\n\t * MCP Apps: returns null.\n\t */\n\tgetToolResponseMetadata(): UnknownObject | null;\n\n\t/**\n\t * Subscribe to tool response metadata changes.\n\t * OpenAI-only: subscribes to toolResponseMetadata changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonToolResponseMetadataChange(\n\t\tcallback: (metadata: UnknownObject | null) => void,\n\t): () => void;\n\n\t/**\n\t * Get the widget state.\n\t * OpenAI-only: returns state from window.openai.widgetState.\n\t * MCP Apps: returns null.\n\t */\n\tgetWidgetState(): UnknownObject | null;\n\n\t/**\n\t * Subscribe to widget state changes.\n\t * OpenAI-only: subscribes to widgetState changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonWidgetStateChange(\n\t\tcallback: (state: UnknownObject | null) => void,\n\t): () => void;\n}\n\n/**\n * Creates a unified widget client for the current platform.\n */\nexport async function createWidgetClient(): Promise<UnifiedWidgetClient> {\n\tconst { detectPlatform } = await import(\"./platform\");\n\tconst platform = detectPlatform();\n\n\tif (platform === \"openai\") {\n\t\tconst { OpenAIWidgetClient } = await import(\"./openai-client\");\n\t\treturn new OpenAIWidgetClient();\n\t} else {\n\t\tconst { MCPAppsWidgetClient } = await import(\"./mcp-apps-client\");\n\t\treturn new MCPAppsWidgetClient();\n\t}\n}\n","\"use client\";\n\nimport type { DisplayMode } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get the current display mode.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns The current display mode (\"pip\" | \"inline\" | \"fullscreen\")\n */\nexport function useDisplayMode(): DisplayMode {\n\treturn useWidgetClient(\"displayMode\");\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\n\n/**\n * Check if running in ChatGPT app (OpenAI-only).\n * Returns false on MCP Apps.\n *\n * @returns Whether the widget is running in ChatGPT app\n */\nexport function useIsChatGptApp(): boolean {\n\treturn useSyncExternalStore(\n\t\t() => () => {},\n\t\t() => {\n\t\t\tif (typeof window === \"undefined\") return false;\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: __isChatGptApp is injected by ChatGPT\n\t\t\treturn (window as any).__isChatGptApp === true;\n\t\t},\n\t\t() => false,\n\t);\n}\n","\"use client\";\n\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get the current locale.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns The current locale string (e.g., \"en-US\")\n */\nexport function useLocale(): string {\n\treturn useWidgetClient(\"locale\");\n}\n","\"use client\";\n\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get the maximum height available for the widget (OpenAI-only).\n * Useful for responsive layouts that need to adapt to container constraints.\n * Returns null on MCP Apps.\n *\n * @returns The maximum height in pixels, or null if not available\n */\nexport function useMaxHeight(): number | null {\n\treturn useWidgetClient(\"maxHeight\");\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get a function to open external URLs.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns A function that opens external URLs\n */\nexport function useOpenExternal(): (url: string) => void {\n\tconst client = useWidgetClient();\n\treturn useCallback((url: string) => client.openExternal(url), [client]);\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport type { DisplayMode } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get a function to request display mode changes.\n * On MCP Apps, this may be a no-op depending on host support.\n *\n * @returns A function to request a specific display mode\n */\nexport function useRequestDisplayMode(): (\n\tmode: DisplayMode,\n) => Promise<DisplayMode> {\n\tconst client = useWidgetClient();\n\treturn useCallback(\n\t\t(mode: DisplayMode) => client.requestDisplayMode(mode),\n\t\t[client],\n\t);\n}\n","\"use client\";\n\nimport type { SafeArea } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get safe area insets (OpenAI-only).\n * Useful for ensuring UI elements don't get hidden behind the chat input.\n * Returns null on MCP Apps.\n *\n * @returns The safe area insets, or null if not available\n */\nexport function useSafeArea(): SafeArea | null {\n\treturn useWidgetClient(\"safeArea\");\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get a function to send follow-up messages to the AI.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns A function that sends a follow-up message\n */\nexport function useSendFollowUp(): (prompt: string) => void {\n\tconst client = useWidgetClient();\n\treturn useCallback((prompt: string) => client.sendFollowUp(prompt), [client]);\n}\n","\"use client\";\n\nimport type { Theme } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get the current theme.\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns The current theme (\"light\" | \"dark\")\n */\nexport function useTheme(): Theme {\n\treturn useWidgetClient(\"theme\");\n}\n","\"use client\";\n\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get the tool output (structured content returned by the tool handler).\n * Works on both OpenAI widgets and MCP Apps.\n *\n * @returns The tool output or null\n */\nexport function useToolOutput<T extends Record<string, unknown>>(): T | null {\n\treturn useWidgetClient(\"toolOutput\") as T | null;\n}\n","\"use client\";\n\nimport type { UnknownObject } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Get tool response metadata (OpenAI-only).\n * Contains identifiers like `openai/widgetSessionId` for correlating\n * multiple tool calls or logs for the same widget instance.\n * Returns null on MCP Apps.\n *\n * @returns The tool response metadata object or null if not available\n */\nexport function useToolResponseMetadata(): UnknownObject | null {\n\treturn useWidgetClient(\"toolResponseMetadata\");\n}\n","\"use client\";\n\nimport { type SetStateAction, useCallback, useEffect, useState } from \"react\";\nimport { detectPlatform } from \"../widgets/platform\";\nimport type { UnknownObject } from \"./@types\";\nimport { useWidgetClient } from \"./use-widget\";\n\n/**\n * Widget state that persists across widget lifecycles (OpenAI-only).\n * State is synchronized with the ChatGPT parent window and survives widget minimize/restore.\n * On MCP Apps, returns [null, no-op].\n *\n * @param defaultState - Initial state value or function to compute it\n * @returns A tuple of [state, setState] similar to useState\n */\nexport function useWidgetState<T extends UnknownObject>(\n\tdefaultState?: T | (() => T | null) | null,\n): readonly [T | null, (state: SetStateAction<T | null>) => void] {\n\tconst widgetStateFromWindow = useWidgetClient(\"widgetState\") as T | null;\n\n\tconst [widgetState, _setWidgetState] = useState<T | null>(() => {\n\t\tif (widgetStateFromWindow != null) {\n\t\t\treturn widgetStateFromWindow;\n\t\t}\n\t\treturn typeof defaultState === \"function\"\n\t\t\t? defaultState()\n\t\t\t: (defaultState ?? null);\n\t});\n\n\tuseEffect(() => {\n\t\t_setWidgetState(widgetStateFromWindow);\n\t}, [widgetStateFromWindow]);\n\n\tconst setWidgetState = useCallback((state: SetStateAction<T | null>) => {\n\t\t_setWidgetState((prevState) => {\n\t\t\tconst newState = typeof state === \"function\" ? state(prevState) : state;\n\n\t\t\tif (detectPlatform() === \"openai\" && newState != null) {\n\t\t\t\twindow.openai?.setWidgetState(newState);\n\t\t\t}\n\n\t\t\treturn newState;\n\t\t});\n\t}, []);\n\n\treturn [widgetState, setWidgetState] as const;\n}\n","export const LoadingWidget = () => {\n\treturn (\n\t\t<div className=\"flex flex-col items-center justify-center h-full min-h-[120px] gap-4\">\n\t\t\t{/* Animated dots */}\n\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t<div className=\"w-3 h-3 rounded-full bg-gradient-to-r from-blue-400 to-cyan-400 animate-bounce [animation-delay:-0.3s]\" />\n\t\t\t\t<div className=\"w-3 h-3 rounded-full bg-gradient-to-r from-cyan-400 to-teal-400 animate-bounce [animation-delay:-0.15s]\" />\n\t\t\t\t<div className=\"w-3 h-3 rounded-full bg-gradient-to-r from-teal-400 to-emerald-400 animate-bounce\" />\n\t\t\t</div>\n\n\t\t\t{/* Shimmer text */}\n\t\t\t<p className=\"text-sm font-medium text-transparent bg-clip-text bg-gradient-to-r from-slate-400 via-slate-200 to-slate-400 bg-[length:200%_100%] animate-[shimmer_1.5s_ease-in-out_infinite]\">\n\t\t\t\tLoading widget...\n\t\t\t</p>\n\n\t\t\t{/* Pulsing ring */}\n\t\t\t<div className=\"absolute inset-0 flex items-center justify-center pointer-events-none\">\n\t\t\t\t<div className=\"w-16 h-16 rounded-full border-2 border-blue-400/20 animate-ping\" />\n\t\t\t</div>\n\n\t\t\t<style>{`\n @keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n `}</style>\n\t\t</div>\n\t);\n};\n"],"mappings":"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","names":["Fragment","jsx","jsxs","InitializeNextJsInChatGpt","baseUrl","htmlElement","mutations","mutation","attrName","originalReplaceState","_s","unused","url","u","href","originalPushState","appOrigin","isInIframe","e","a","originalFetch","input","init","newUrl","useCallback","useEffect","useRef","useState","DEFAULT_MOCK_CONFIG","mockState","initializeMockOpenAI","initialToolOutput","mode","updateMockGlobal","name","args","prompt","href","state","SetGlobalsEvent","key","value","getMockState","updateMockToolOutput","props","updateMockDisplayMode","updateMockTheme","theme","Fragment","jsx","jsxs","MOCK_SAFE_AREA_HEIGHT","DevIcon","className","CloseIcon","InlineIcon","PipIcon","FullscreenIcon","SunIcon","MoonIcon","ResetIcon","panelAnimationStyles","DevModeProvider","defaultProps","widgetPaths","children","isInitialized","setIsInitialized","useState","isWidgetPage","setIsWidgetPage","useEffect","currentPath","isWidget","path","initializeMockOpenAI","WidgetPreviewFrame","DevControls","SegmentedControl","options","value","onChange","activeIndex","opt","option","isOpen","setIsOpen","isAnimating","setIsAnimating","shouldRender","setShouldRender","displayMode","setDisplayMode","theme","setTheme","showSafeArea","setShowSafeArea","propsJson","setPropsJson","jsonError","setJsonError","debounceRef","useRef","panelRef","containerRef","state","getMockState","SetGlobalsEvent","openPanel","useCallback","closePanel","togglePanel","handleKeyDown","e","handleClickOutside","handleDisplayModeChange","mode","updateMockDisplayMode","handleThemeChange","newTheme","updateMockTheme","applyProps","json","parsed","updateMockToolOutput","handlePropsChange","handlePropsBlur","handleReset","defaultJson","displayModeOptions","themeOptions","useCallback","React","createContext","useCallback","useContext","useEffect","useState","useSyncExternalStore","createWidgetClient","detectPlatform","OpenAIWidgetClient","MCPAppsWidgetClient","WidgetClientContext","createContext","WidgetProvider","children","loading","onError","client","setClient","useState","error","setError","isConnecting","setIsConnecting","useEffect","mounted","activeClient","initClient","widgetClient","createWidgetClient","err","syncTheme","theme","React","useWidgetClient","key","useContext","subscribe","useCallback","onChange","getSnapshot","store","useSyncExternalStore","useCallTool","client","useWidgetClient","useCallback","name","args","useDisplayMode","useWidgetClient","useSyncExternalStore","useIsChatGptApp","useLocale","useWidgetClient","useMaxHeight","useWidgetClient","useCallback","useOpenExternal","client","useWidgetClient","useCallback","url","useCallback","useRequestDisplayMode","client","useWidgetClient","useCallback","mode","useSafeArea","useWidgetClient","useCallback","useSendFollowUp","client","useWidgetClient","useCallback","prompt","useTheme","useWidgetClient","useToolOutput","useWidgetClient","useToolResponseMetadata","useWidgetClient","useCallback","useEffect","useState","useWidgetState","defaultState","widgetStateFromWindow","useWidgetClient","widgetState","_setWidgetState","useState","useEffect","setWidgetState","useCallback","state","prevState","newState","detectPlatform","jsx","jsxs","LoadingWidget"]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import{App as l,PostMessageTransport as s}from"@modelcontextprotocol/ext-apps";var n=class{app;toolResultCallback=null;themeChangeCallback=null;displayModeChangeCallback=null;hostContext;latestToolResult=null;constructor(){this.app=new l({name:"WaniWani Widget",version:"1.0.0"},{},{autoResize:!0}),this.app.ontoolresult=e=>{let t={content:e.content,structuredContent:e.structuredContent};this.latestToolResult=t,this.toolResultCallback?.(t)},this.app.onhostcontextchanged=e=>{this.hostContext={...this.hostContext,...e},e.theme&&this.themeChangeCallback?.(e.theme),e.displayMode&&this.displayModeChangeCallback?.(e.displayMode)}}async connect(){await this.app.connect(new s(window.parent,window.parent)),this.hostContext=this.app.getHostContext()}async close(){await this.app.close()}getToolOutput(){return this.latestToolResult?.structuredContent?this.latestToolResult.structuredContent:null}onToolResult(e){return this.toolResultCallback=e,()=>{this.toolResultCallback=null}}async callTool(e,t){let o=await this.app.callServerTool({name:e,arguments:t});return{content:o.content,structuredContent:o.structuredContent}}openExternal(e){this.app.sendOpenLink({url:e}).catch(t=>{console.error("Failed to open link:",t)})}sendFollowUp(e){this.app.sendMessage({role:"user",content:[{type:"text",text:e}]}).catch(t=>{console.error("Failed to send follow-up message:",t)})}getTheme(){return this.hostContext?.theme??"light"}onThemeChange(e){return this.themeChangeCallback=e,()=>{this.themeChangeCallback=null}}getLocale(){return this.hostContext?.locale??"en"}getDisplayMode(){return this.hostContext?.displayMode??"inline"}async requestDisplayMode(e){return(await this.app.requestDisplayMode({mode:e})).mode}onDisplayModeChange(e){return this.displayModeChangeCallback=e,()=>{this.displayModeChangeCallback=null}}getSafeArea(){return null}onSafeAreaChange(){return()=>{}}getMaxHeight(){return null}onMaxHeightChange(){return()=>{}}getToolResponseMetadata(){return null}onToolResponseMetadataChange(){return()=>{}}getWidgetState(){return null}onWidgetStateChange(){return()=>{}}};export{n as MCPAppsWidgetClient};
|
|
3
|
+
//# sourceMappingURL=mcp-apps-client-Z62VBB36.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp/react/widgets/mcp-apps-client.ts"],"sourcesContent":["import type { McpUiHostContext } from \"@modelcontextprotocol/ext-apps\";\nimport { App, PostMessageTransport } from \"@modelcontextprotocol/ext-apps\";\nimport type {\n\tDisplayMode,\n\tSafeArea,\n\tTheme,\n\tUnknownObject,\n} from \"../hooks/@types\";\nimport type {\n\tToolCallResult,\n\tToolResult,\n\tUnifiedWidgetClient,\n} from \"./widget-client\";\n\n/**\n * MCP Apps widget client implementation.\n * Uses the @modelcontextprotocol/ext-apps App class for communication.\n */\nexport class MCPAppsWidgetClient implements UnifiedWidgetClient {\n\tprivate app: App;\n\tprivate toolResultCallback: ((result: ToolResult) => void) | null = null;\n\tprivate themeChangeCallback: ((theme: Theme) => void) | null = null;\n\tprivate displayModeChangeCallback: ((mode: DisplayMode) => void) | null =\n\t\tnull;\n\tprivate hostContext: McpUiHostContext | undefined;\n\tprivate latestToolResult: ToolResult | null = null;\n\n\tconstructor() {\n\t\tthis.app = new App(\n\t\t\t{ name: \"WaniWani Widget\", version: \"1.0.0\" },\n\t\t\t{}, // capabilities\n\t\t\t{ autoResize: true },\n\t\t);\n\n\t\t// Set up notification handlers\n\t\tthis.app.ontoolresult = (params) => {\n\t\t\tconst result: ToolResult = {\n\t\t\t\tcontent: params.content,\n\t\t\t\tstructuredContent: params.structuredContent as\n\t\t\t\t\t| Record<string, unknown>\n\t\t\t\t\t| undefined,\n\t\t\t};\n\t\t\tthis.latestToolResult = result;\n\t\t\tthis.toolResultCallback?.(result);\n\t\t};\n\n\t\tthis.app.onhostcontextchanged = (params) => {\n\t\t\tthis.hostContext = { ...this.hostContext, ...params };\n\t\t\tif (params.theme) {\n\t\t\t\tthis.themeChangeCallback?.(params.theme as Theme);\n\t\t\t}\n\t\t\tif (params.displayMode) {\n\t\t\t\tthis.displayModeChangeCallback?.(params.displayMode as DisplayMode);\n\t\t\t}\n\t\t};\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tawait this.app.connect(\n\t\t\tnew PostMessageTransport(window.parent, window.parent),\n\t\t);\n\t\tthis.hostContext = this.app.getHostContext();\n\t}\n\n\tasync close(): Promise<void> {\n\t\tawait this.app.close();\n\t}\n\n\tgetToolOutput<T = Record<string, unknown>>(): T | null {\n\t\t// In MCP Apps, tool output comes through ontoolresult notification\n\t\t// Return the latest cached result\n\t\tif (this.latestToolResult?.structuredContent) {\n\t\t\treturn this.latestToolResult.structuredContent as T;\n\t\t}\n\t\treturn null;\n\t}\n\n\tonToolResult(callback: (result: ToolResult) => void): () => void {\n\t\tthis.toolResultCallback = callback;\n\t\treturn () => {\n\t\t\tthis.toolResultCallback = null;\n\t\t};\n\t}\n\n\tasync callTool(\n\t\tname: string,\n\t\targs: Record<string, unknown>,\n\t): Promise<ToolCallResult> {\n\t\tconst result = await this.app.callServerTool({\n\t\t\tname,\n\t\t\targuments: args,\n\t\t});\n\t\treturn {\n\t\t\tcontent: result.content,\n\t\t\tstructuredContent: result.structuredContent as\n\t\t\t\t| Record<string, unknown>\n\t\t\t\t| undefined,\n\t\t};\n\t}\n\n\topenExternal(url: string): void {\n\t\tthis.app.sendOpenLink({ url }).catch((err: unknown) => {\n\t\t\tconsole.error(\"Failed to open link:\", err);\n\t\t});\n\t}\n\n\tsendFollowUp(prompt: string): void {\n\t\tthis.app\n\t\t\t.sendMessage({\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: [{ type: \"text\", text: prompt }],\n\t\t\t})\n\t\t\t.catch((err: unknown) => {\n\t\t\t\tconsole.error(\"Failed to send follow-up message:\", err);\n\t\t\t});\n\t}\n\n\tgetTheme(): Theme {\n\t\treturn (this.hostContext?.theme as Theme) ?? \"light\";\n\t}\n\n\tonThemeChange(callback: (theme: Theme) => void): () => void {\n\t\tthis.themeChangeCallback = callback;\n\t\treturn () => {\n\t\t\tthis.themeChangeCallback = null;\n\t\t};\n\t}\n\n\tgetLocale(): string {\n\t\treturn (this.hostContext?.locale as string | undefined) ?? \"en\";\n\t}\n\n\tgetDisplayMode(): DisplayMode {\n\t\treturn (this.hostContext?.displayMode as DisplayMode) ?? \"inline\";\n\t}\n\n\tasync requestDisplayMode(mode: DisplayMode): Promise<DisplayMode> {\n\t\tconst result = await this.app.requestDisplayMode({ mode });\n\t\treturn result.mode as DisplayMode;\n\t}\n\n\tonDisplayModeChange(callback: (mode: DisplayMode) => void): () => void {\n\t\tthis.displayModeChangeCallback = callback;\n\t\treturn () => {\n\t\t\tthis.displayModeChangeCallback = null;\n\t\t};\n\t}\n\n\t// OPENAI specific methods\n\tgetSafeArea(): SafeArea | null {\n\t\treturn null;\n\t}\n\n\t// MCP Apps specific methods\n\tonSafeAreaChange(): () => void {\n\t\treturn () => {};\n\t}\n\n\t// OPENAI specific methods\n\tgetMaxHeight(): number | null {\n\t\treturn null;\n\t}\n\n\t// MCP Apps specific methods\n\tonMaxHeightChange(): () => void {\n\t\treturn () => {};\n\t}\n\n\t// MCP Apps specific methods\n\tgetToolResponseMetadata(): UnknownObject | null {\n\t\treturn null;\n\t}\n\n\t// MCP Apps specific methods\n\tonToolResponseMetadataChange(): () => void {\n\t\treturn () => {};\n\t}\n\n\t// MCP Apps specific methods\n\tgetWidgetState(): UnknownObject | null {\n\t\treturn null;\n\t}\n\n\t// MCP Apps specific methods\n\tonWidgetStateChange(): () => void {\n\t\treturn () => {};\n\t}\n}\n"],"mappings":";AACA,OAAS,OAAAA,EAAK,wBAAAC,MAA4B,iCAiBnC,IAAMC,EAAN,KAAyD,CACvD,IACA,mBAA4D,KAC5D,oBAAuD,KACvD,0BACP,KACO,YACA,iBAAsC,KAE9C,aAAc,CACb,KAAK,IAAM,IAAIF,EACd,CAAE,KAAM,kBAAmB,QAAS,OAAQ,EAC5C,CAAC,EACD,CAAE,WAAY,EAAK,CACpB,EAGA,KAAK,IAAI,aAAgBG,GAAW,CACnC,IAAMC,EAAqB,CAC1B,QAASD,EAAO,QAChB,kBAAmBA,EAAO,iBAG3B,EACA,KAAK,iBAAmBC,EACxB,KAAK,qBAAqBA,CAAM,CACjC,EAEA,KAAK,IAAI,qBAAwBD,GAAW,CAC3C,KAAK,YAAc,CAAE,GAAG,KAAK,YAAa,GAAGA,CAAO,EAChDA,EAAO,OACV,KAAK,sBAAsBA,EAAO,KAAc,EAE7CA,EAAO,aACV,KAAK,4BAA4BA,EAAO,WAA0B,CAEpE,CACD,CAEA,MAAM,SAAyB,CAC9B,MAAM,KAAK,IAAI,QACd,IAAIF,EAAqB,OAAO,OAAQ,OAAO,MAAM,CACtD,EACA,KAAK,YAAc,KAAK,IAAI,eAAe,CAC5C,CAEA,MAAM,OAAuB,CAC5B,MAAM,KAAK,IAAI,MAAM,CACtB,CAEA,eAAuD,CAGtD,OAAI,KAAK,kBAAkB,kBACnB,KAAK,iBAAiB,kBAEvB,IACR,CAEA,aAAaI,EAAoD,CAChE,YAAK,mBAAqBA,EACnB,IAAM,CACZ,KAAK,mBAAqB,IAC3B,CACD,CAEA,MAAM,SACLC,EACAC,EAC0B,CAC1B,IAAMH,EAAS,MAAM,KAAK,IAAI,eAAe,CAC5C,KAAAE,EACA,UAAWC,CACZ,CAAC,EACD,MAAO,CACN,QAASH,EAAO,QAChB,kBAAmBA,EAAO,iBAG3B,CACD,CAEA,aAAaI,EAAmB,CAC/B,KAAK,IAAI,aAAa,CAAE,IAAAA,CAAI,CAAC,EAAE,MAAOC,GAAiB,CACtD,QAAQ,MAAM,uBAAwBA,CAAG,CAC1C,CAAC,CACF,CAEA,aAAaC,EAAsB,CAClC,KAAK,IACH,YAAY,CACZ,KAAM,OACN,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAMA,CAAO,CAAC,CACzC,CAAC,EACA,MAAOD,GAAiB,CACxB,QAAQ,MAAM,oCAAqCA,CAAG,CACvD,CAAC,CACH,CAEA,UAAkB,CACjB,OAAQ,KAAK,aAAa,OAAmB,OAC9C,CAEA,cAAcJ,EAA8C,CAC3D,YAAK,oBAAsBA,EACpB,IAAM,CACZ,KAAK,oBAAsB,IAC5B,CACD,CAEA,WAAoB,CACnB,OAAQ,KAAK,aAAa,QAAiC,IAC5D,CAEA,gBAA8B,CAC7B,OAAQ,KAAK,aAAa,aAA+B,QAC1D,CAEA,MAAM,mBAAmBM,EAAyC,CAEjE,OADe,MAAM,KAAK,IAAI,mBAAmB,CAAE,KAAAA,CAAK,CAAC,GAC3C,IACf,CAEA,oBAAoBN,EAAmD,CACtE,YAAK,0BAA4BA,EAC1B,IAAM,CACZ,KAAK,0BAA4B,IAClC,CACD,CAGA,aAA+B,CAC9B,OAAO,IACR,CAGA,kBAA+B,CAC9B,MAAO,IAAM,CAAC,CACf,CAGA,cAA8B,CAC7B,OAAO,IACR,CAGA,mBAAgC,CAC/B,MAAO,IAAM,CAAC,CACf,CAGA,yBAAgD,CAC/C,OAAO,IACR,CAGA,8BAA2C,CAC1C,MAAO,IAAM,CAAC,CACf,CAGA,gBAAuC,CACtC,OAAO,IACR,CAGA,qBAAkC,CACjC,MAAO,IAAM,CAAC,CACf,CACD","names":["App","PostMessageTransport","MCPAppsWidgetClient","params","result","callback","name","args","url","err","prompt","mode"]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import{a as l}from"./chunk-ZUGQBRJF.js";var i=class{getGlobal(e,n){return typeof window>"u"?n:window.openai?.[e]??n}onGlobalChange(e,n,o){if(typeof window>"u")return()=>{};let a=s=>{let t=s.detail.globals[e];t!==void 0&&n(o?o(t):t)};return window.addEventListener(l,a,{passive:!0}),()=>window.removeEventListener(l,a)}async connect(){if(typeof window>"u"||!("openai"in window))throw new Error("OpenAI global not found. Are you running in ChatGPT?")}async close(){}getToolOutput(){return this.getGlobal("toolOutput",null)}onToolResult(e){return this.onGlobalChange("toolOutput",e,n=>({structuredContent:n??{}}))}async callTool(e,n){if(typeof window>"u"||!window.openai?.callTool)throw new Error("OpenAI callTool not available");return{content:[{type:"text",text:(await window.openai.callTool(e,n)).result}]}}openExternal(e){typeof window<"u"&&window.openai?.openExternal&&window.openai.openExternal({href:e})}sendFollowUp(e){typeof window<"u"&&window.openai?.sendFollowUpMessage&&window.openai.sendFollowUpMessage({prompt:e})}getTheme(){return this.getGlobal("theme","light")}onThemeChange(e){return this.onGlobalChange("theme",e)}getLocale(){return this.getGlobal("locale","en")}getDisplayMode(){return this.getGlobal("displayMode","inline")}async requestDisplayMode(e){return typeof window>"u"||!window.openai?.requestDisplayMode?"inline":(await window.openai.requestDisplayMode({mode:e})).mode}onDisplayModeChange(e){return this.onGlobalChange("displayMode",e)}getSafeArea(){return this.getGlobal("safeArea",null)}onSafeAreaChange(e){return this.onGlobalChange("safeArea",e,n=>n??null)}getMaxHeight(){return this.getGlobal("maxHeight",null)}onMaxHeightChange(e){return this.onGlobalChange("maxHeight",e,n=>n??null)}getToolResponseMetadata(){return this.getGlobal("toolResponseMetadata",null)}onToolResponseMetadataChange(e){return this.onGlobalChange("toolResponseMetadata",e,n=>n??null)}getWidgetState(){return this.getGlobal("widgetState",null)}onWidgetStateChange(e){return this.onGlobalChange("widgetState",e,n=>n??null)}};export{i as OpenAIWidgetClient};
|
|
3
|
+
//# sourceMappingURL=openai-client-MVV4KJUO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp/react/widgets/openai-client.ts"],"sourcesContent":["import type {\n\tDisplayMode,\n\tSafeArea,\n\tTheme,\n\tUnknownObject,\n} from \"../hooks/@types\";\nimport { SET_GLOBALS_EVENT_TYPE, type SetGlobalsEvent } from \"../hooks/@types\";\nimport type {\n\tToolCallResult,\n\tToolResult,\n\tUnifiedWidgetClient,\n} from \"./widget-client\";\n\ntype GlobalsKey = keyof SetGlobalsEvent[\"detail\"][\"globals\"];\n\n/**\n * OpenAI widget client implementation.\n * Uses window.openai global object injected by ChatGPT.\n */\nexport class OpenAIWidgetClient implements UnifiedWidgetClient {\n\tprivate getGlobal<T>(\n\t\tkey: keyof NonNullable<typeof window.openai>,\n\t\tfallback: T,\n\t): T {\n\t\tif (typeof window === \"undefined\") return fallback;\n\t\treturn (window.openai?.[key] as T) ?? fallback;\n\t}\n\n\tprivate onGlobalChange<\n\t\tK extends GlobalsKey,\n\t\tT = SetGlobalsEvent[\"detail\"][\"globals\"][K],\n\t>(\n\t\tkey: K,\n\t\tcallback: (value: T) => void,\n\t\ttransform?: (value: SetGlobalsEvent[\"detail\"][\"globals\"][K]) => T,\n\t): () => void {\n\t\tif (typeof window === \"undefined\") return () => {};\n\n\t\tconst handler = (event: SetGlobalsEvent) => {\n\t\t\tconst value = event.detail.globals[key];\n\t\t\tif (value !== undefined) {\n\t\t\t\tcallback(transform ? transform(value) : (value as T));\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(SET_GLOBALS_EVENT_TYPE, handler, { passive: true });\n\t\treturn () => window.removeEventListener(SET_GLOBALS_EVENT_TYPE, handler);\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (typeof window === \"undefined\" || !(\"openai\" in window)) {\n\t\t\tthrow new Error(\"OpenAI global not found. Are you running in ChatGPT?\");\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// No-op for OpenAI - connection is managed by the host\n\t}\n\n\tgetToolOutput<T = Record<string, unknown>>(): T | null {\n\t\treturn this.getGlobal<T | null>(\"toolOutput\", null);\n\t}\n\n\tonToolResult(callback: (result: ToolResult) => void): () => void {\n\t\treturn this.onGlobalChange(\"toolOutput\", callback, (v) => ({\n\t\t\tstructuredContent: v ?? {},\n\t\t}));\n\t}\n\n\tasync callTool(\n\t\tname: string,\n\t\targs: Record<string, unknown>,\n\t): Promise<ToolCallResult> {\n\t\tif (typeof window === \"undefined\" || !window.openai?.callTool) {\n\t\t\tthrow new Error(\"OpenAI callTool not available\");\n\t\t}\n\t\tconst response = await window.openai.callTool(name, args);\n\t\treturn { content: [{ type: \"text\", text: response.result }] };\n\t}\n\n\topenExternal(url: string): void {\n\t\tif (typeof window !== \"undefined\" && window.openai?.openExternal) {\n\t\t\twindow.openai.openExternal({ href: url });\n\t\t}\n\t}\n\n\tsendFollowUp(prompt: string): void {\n\t\tif (typeof window !== \"undefined\" && window.openai?.sendFollowUpMessage) {\n\t\t\twindow.openai.sendFollowUpMessage({ prompt });\n\t\t}\n\t}\n\n\tgetTheme(): Theme {\n\t\treturn this.getGlobal(\"theme\", \"light\" as Theme);\n\t}\n\n\tonThemeChange(callback: (theme: Theme) => void): () => void {\n\t\treturn this.onGlobalChange(\"theme\", callback);\n\t}\n\n\tgetLocale(): string {\n\t\treturn this.getGlobal(\"locale\", \"en\");\n\t}\n\n\tgetDisplayMode(): DisplayMode {\n\t\treturn this.getGlobal(\"displayMode\", \"inline\" as DisplayMode);\n\t}\n\n\tasync requestDisplayMode(mode: DisplayMode): Promise<DisplayMode> {\n\t\tif (typeof window === \"undefined\" || !window.openai?.requestDisplayMode) {\n\t\t\treturn \"inline\";\n\t\t}\n\t\tconst result = await window.openai.requestDisplayMode({ mode });\n\t\treturn result.mode;\n\t}\n\n\tonDisplayModeChange(callback: (mode: DisplayMode) => void): () => void {\n\t\treturn this.onGlobalChange(\"displayMode\", callback);\n\t}\n\n\tgetSafeArea(): SafeArea | null {\n\t\treturn this.getGlobal(\"safeArea\", null);\n\t}\n\n\tonSafeAreaChange(callback: (safeArea: SafeArea | null) => void): () => void {\n\t\treturn this.onGlobalChange(\"safeArea\", callback, (v) => v ?? null);\n\t}\n\n\tgetMaxHeight(): number | null {\n\t\treturn this.getGlobal(\"maxHeight\", null);\n\t}\n\n\tonMaxHeightChange(callback: (maxHeight: number | null) => void): () => void {\n\t\treturn this.onGlobalChange(\"maxHeight\", callback, (v) => v ?? null);\n\t}\n\n\tgetToolResponseMetadata(): UnknownObject | null {\n\t\treturn this.getGlobal(\"toolResponseMetadata\", null);\n\t}\n\n\tonToolResponseMetadataChange(\n\t\tcallback: (metadata: UnknownObject | null) => void,\n\t): () => void {\n\t\treturn this.onGlobalChange(\n\t\t\t\"toolResponseMetadata\",\n\t\t\tcallback,\n\t\t\t(v) => v ?? null,\n\t\t);\n\t}\n\n\tgetWidgetState(): UnknownObject | null {\n\t\treturn this.getGlobal<UnknownObject | null>(\"widgetState\", null);\n\t}\n\n\tonWidgetStateChange(\n\t\tcallback: (state: UnknownObject | null) => void,\n\t): () => void {\n\t\treturn this.onGlobalChange(\"widgetState\", callback, (v) => v ?? null);\n\t}\n}\n"],"mappings":";wCAmBO,IAAMA,EAAN,KAAwD,CACtD,UACPC,EACAC,EACI,CACJ,OAAI,OAAO,OAAW,IAAoBA,EAClC,OAAO,SAASD,CAAG,GAAWC,CACvC,CAEQ,eAIPD,EACAE,EACAC,EACa,CACb,GAAI,OAAO,OAAW,IAAa,MAAO,IAAM,CAAC,EAEjD,IAAMC,EAAWC,GAA2B,CAC3C,IAAMC,EAAQD,EAAM,OAAO,QAAQL,CAAG,EAClCM,IAAU,QACbJ,EAASC,EAAYA,EAAUG,CAAK,EAAKA,CAAW,CAEtD,EAEA,cAAO,iBAAiBC,EAAwBH,EAAS,CAAE,QAAS,EAAK,CAAC,EACnE,IAAM,OAAO,oBAAoBG,EAAwBH,CAAO,CACxE,CAEA,MAAM,SAAyB,CAC9B,GAAI,OAAO,OAAW,KAAe,EAAE,WAAY,QAClD,MAAM,IAAI,MAAM,sDAAsD,CAExE,CAEA,MAAM,OAAuB,CAE7B,CAEA,eAAuD,CACtD,OAAO,KAAK,UAAoB,aAAc,IAAI,CACnD,CAEA,aAAaF,EAAoD,CAChE,OAAO,KAAK,eAAe,aAAcA,EAAWM,IAAO,CAC1D,kBAAmBA,GAAK,CAAC,CAC1B,EAAE,CACH,CAEA,MAAM,SACLC,EACAC,EAC0B,CAC1B,GAAI,OAAO,OAAW,KAAe,CAAC,OAAO,QAAQ,SACpD,MAAM,IAAI,MAAM,+BAA+B,EAGhD,MAAO,CAAE,QAAS,CAAC,CAAE,KAAM,OAAQ,MADlB,MAAM,OAAO,OAAO,SAASD,EAAMC,CAAI,GACN,MAAO,CAAC,CAAE,CAC7D,CAEA,aAAaC,EAAmB,CAC3B,OAAO,OAAW,KAAe,OAAO,QAAQ,cACnD,OAAO,OAAO,aAAa,CAAE,KAAMA,CAAI,CAAC,CAE1C,CAEA,aAAaC,EAAsB,CAC9B,OAAO,OAAW,KAAe,OAAO,QAAQ,qBACnD,OAAO,OAAO,oBAAoB,CAAE,OAAAA,CAAO,CAAC,CAE9C,CAEA,UAAkB,CACjB,OAAO,KAAK,UAAU,QAAS,OAAgB,CAChD,CAEA,cAAcV,EAA8C,CAC3D,OAAO,KAAK,eAAe,QAASA,CAAQ,CAC7C,CAEA,WAAoB,CACnB,OAAO,KAAK,UAAU,SAAU,IAAI,CACrC,CAEA,gBAA8B,CAC7B,OAAO,KAAK,UAAU,cAAe,QAAuB,CAC7D,CAEA,MAAM,mBAAmBW,EAAyC,CACjE,OAAI,OAAO,OAAW,KAAe,CAAC,OAAO,QAAQ,mBAC7C,UAEO,MAAM,OAAO,OAAO,mBAAmB,CAAE,KAAAA,CAAK,CAAC,GAChD,IACf,CAEA,oBAAoBX,EAAmD,CACtE,OAAO,KAAK,eAAe,cAAeA,CAAQ,CACnD,CAEA,aAA+B,CAC9B,OAAO,KAAK,UAAU,WAAY,IAAI,CACvC,CAEA,iBAAiBA,EAA2D,CAC3E,OAAO,KAAK,eAAe,WAAYA,EAAWM,GAAMA,GAAK,IAAI,CAClE,CAEA,cAA8B,CAC7B,OAAO,KAAK,UAAU,YAAa,IAAI,CACxC,CAEA,kBAAkBN,EAA0D,CAC3E,OAAO,KAAK,eAAe,YAAaA,EAAWM,GAAMA,GAAK,IAAI,CACnE,CAEA,yBAAgD,CAC/C,OAAO,KAAK,UAAU,uBAAwB,IAAI,CACnD,CAEA,6BACCN,EACa,CACb,OAAO,KAAK,eACX,uBACAA,EACCM,GAAMA,GAAK,IACb,CACD,CAEA,gBAAuC,CACtC,OAAO,KAAK,UAAgC,cAAe,IAAI,CAChE,CAEA,oBACCN,EACa,CACb,OAAO,KAAK,eAAe,cAAeA,EAAWM,GAAMA,GAAK,IAAI,CACrE,CACD","names":["OpenAIWidgetClient","key","fallback","callback","transform","handler","event","value","SET_GLOBALS_EVENT_TYPE","v","name","args","url","prompt","mode"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waniwani/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "WaniWani SDK - MCP event tracking, widget framework, and tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"@modelcontextprotocol/sdk": ">=1.0.0",
|
|
50
|
-
"@modelcontextprotocol/ext-apps": "^1.0
|
|
50
|
+
"@modelcontextprotocol/ext-apps": "^1.1.0",
|
|
51
51
|
"react": ">=18.0.0",
|
|
52
52
|
"zod": ">=4.0.0",
|
|
53
53
|
"@ai-sdk/react": ">=1.0.0",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"@ai-sdk/mcp": "^1.0.0",
|
|
114
114
|
"@ai-sdk/react": "^3.0.73",
|
|
115
115
|
"@biomejs/biome": "^2.3.13",
|
|
116
|
-
"@modelcontextprotocol/ext-apps": "1.0
|
|
116
|
+
"@modelcontextprotocol/ext-apps": "^1.1.0",
|
|
117
117
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
118
118
|
"@streamdown/cjk": "^1.0.1",
|
|
119
119
|
"@streamdown/code": "^1.0.1",
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import{App as l,PostMessageTransport as s}from"@modelcontextprotocol/ext-apps";var n=class{app;toolResultCallback=null;themeChangeCallback=null;displayModeChangeCallback=null;hostContext;latestToolResult=null;constructor(){this.app=new l({name:"WaniWani Widget",version:"1.0.0"},{},{autoResize:!0}),this.app.ontoolresult=e=>{let t={content:e.content,structuredContent:e.structuredContent};this.latestToolResult=t,this.toolResultCallback?.(t)},this.app.onhostcontextchanged=e=>{this.hostContext={...this.hostContext,...e},e.theme&&this.themeChangeCallback?.(e.theme),e.displayMode&&this.displayModeChangeCallback?.(e.displayMode)}}async connect(){await this.app.connect(new s(window.parent,window.parent)),this.hostContext=this.app.getHostContext()}getToolOutput(){return this.latestToolResult?.structuredContent?this.latestToolResult.structuredContent:null}onToolResult(e){return this.toolResultCallback=e,()=>{this.toolResultCallback=null}}async callTool(e,t){let o=await this.app.callServerTool({name:e,arguments:t});return{content:o.content,structuredContent:o.structuredContent}}openExternal(e){this.app.sendOpenLink({url:e}).catch(t=>{console.error("Failed to open link:",t)})}sendFollowUp(e){this.app.sendMessage({role:"user",content:[{type:"text",text:e}]}).catch(t=>{console.error("Failed to send follow-up message:",t)})}getTheme(){return this.hostContext?.theme??"light"}onThemeChange(e){return this.themeChangeCallback=e,()=>{this.themeChangeCallback=null}}getLocale(){return this.hostContext?.locale??"en"}getDisplayMode(){return this.hostContext?.displayMode??"inline"}async requestDisplayMode(e){return(await this.app.requestDisplayMode({mode:e})).mode}onDisplayModeChange(e){return this.displayModeChangeCallback=e,()=>{this.displayModeChangeCallback=null}}getSafeArea(){return null}onSafeAreaChange(){return()=>{}}getMaxHeight(){return null}onMaxHeightChange(){return()=>{}}getToolResponseMetadata(){return null}onToolResponseMetadataChange(){return()=>{}}getWidgetState(){return null}onWidgetStateChange(){return()=>{}}};export{n as MCPAppsWidgetClient};
|
|
3
|
-
//# sourceMappingURL=mcp-apps-client-GILGJJZP.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp/react/widgets/mcp-apps-client.ts"],"sourcesContent":["import type { McpUiHostContext } from \"@modelcontextprotocol/ext-apps\";\nimport { App, PostMessageTransport } from \"@modelcontextprotocol/ext-apps\";\nimport type {\n\tDisplayMode,\n\tSafeArea,\n\tTheme,\n\tUnknownObject,\n} from \"../hooks/@types\";\nimport type {\n\tToolCallResult,\n\tToolResult,\n\tUnifiedWidgetClient,\n} from \"./widget-client\";\n\n/**\n * MCP Apps widget client implementation.\n * Uses the @modelcontextprotocol/ext-apps App class for communication.\n */\nexport class MCPAppsWidgetClient implements UnifiedWidgetClient {\n\tprivate app: App;\n\tprivate toolResultCallback: ((result: ToolResult) => void) | null = null;\n\tprivate themeChangeCallback: ((theme: Theme) => void) | null = null;\n\tprivate displayModeChangeCallback: ((mode: DisplayMode) => void) | null =\n\t\tnull;\n\tprivate hostContext: McpUiHostContext | undefined;\n\tprivate latestToolResult: ToolResult | null = null;\n\n\tconstructor() {\n\t\tthis.app = new App(\n\t\t\t{ name: \"WaniWani Widget\", version: \"1.0.0\" },\n\t\t\t{}, // capabilities\n\t\t\t{ autoResize: true },\n\t\t);\n\n\t\t// Set up notification handlers\n\t\tthis.app.ontoolresult = (params) => {\n\t\t\tconst result: ToolResult = {\n\t\t\t\tcontent: params.content,\n\t\t\t\tstructuredContent: params.structuredContent as\n\t\t\t\t\t| Record<string, unknown>\n\t\t\t\t\t| undefined,\n\t\t\t};\n\t\t\tthis.latestToolResult = result;\n\t\t\tthis.toolResultCallback?.(result);\n\t\t};\n\n\t\tthis.app.onhostcontextchanged = (params) => {\n\t\t\tthis.hostContext = { ...this.hostContext, ...params };\n\t\t\tif (params.theme) {\n\t\t\t\tthis.themeChangeCallback?.(params.theme as Theme);\n\t\t\t}\n\t\t\tif (params.displayMode) {\n\t\t\t\tthis.displayModeChangeCallback?.(params.displayMode as DisplayMode);\n\t\t\t}\n\t\t};\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tawait this.app.connect(\n\t\t\tnew PostMessageTransport(window.parent, window.parent),\n\t\t);\n\t\tthis.hostContext = this.app.getHostContext();\n\t}\n\n\tgetToolOutput<T = Record<string, unknown>>(): T | null {\n\t\t// In MCP Apps, tool output comes through ontoolresult notification\n\t\t// Return the latest cached result\n\t\tif (this.latestToolResult?.structuredContent) {\n\t\t\treturn this.latestToolResult.structuredContent as T;\n\t\t}\n\t\treturn null;\n\t}\n\n\tonToolResult(callback: (result: ToolResult) => void): () => void {\n\t\tthis.toolResultCallback = callback;\n\t\treturn () => {\n\t\t\tthis.toolResultCallback = null;\n\t\t};\n\t}\n\n\tasync callTool(\n\t\tname: string,\n\t\targs: Record<string, unknown>,\n\t): Promise<ToolCallResult> {\n\t\tconst result = await this.app.callServerTool({\n\t\t\tname,\n\t\t\targuments: args,\n\t\t});\n\t\treturn {\n\t\t\tcontent: result.content,\n\t\t\tstructuredContent: result.structuredContent as\n\t\t\t\t| Record<string, unknown>\n\t\t\t\t| undefined,\n\t\t};\n\t}\n\n\topenExternal(url: string): void {\n\t\tthis.app.sendOpenLink({ url }).catch((err: unknown) => {\n\t\t\tconsole.error(\"Failed to open link:\", err);\n\t\t});\n\t}\n\n\tsendFollowUp(prompt: string): void {\n\t\tthis.app\n\t\t\t.sendMessage({\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: [{ type: \"text\", text: prompt }],\n\t\t\t})\n\t\t\t.catch((err: unknown) => {\n\t\t\t\tconsole.error(\"Failed to send follow-up message:\", err);\n\t\t\t});\n\t}\n\n\tgetTheme(): Theme {\n\t\treturn (this.hostContext?.theme as Theme) ?? \"light\";\n\t}\n\n\tonThemeChange(callback: (theme: Theme) => void): () => void {\n\t\tthis.themeChangeCallback = callback;\n\t\treturn () => {\n\t\t\tthis.themeChangeCallback = null;\n\t\t};\n\t}\n\n\tgetLocale(): string {\n\t\treturn (this.hostContext?.locale as string | undefined) ?? \"en\";\n\t}\n\n\tgetDisplayMode(): DisplayMode {\n\t\treturn (this.hostContext?.displayMode as DisplayMode) ?? \"inline\";\n\t}\n\n\tasync requestDisplayMode(mode: DisplayMode): Promise<DisplayMode> {\n\t\tconst result = await this.app.requestDisplayMode({ mode });\n\t\treturn result.mode as DisplayMode;\n\t}\n\n\tonDisplayModeChange(callback: (mode: DisplayMode) => void): () => void {\n\t\tthis.displayModeChangeCallback = callback;\n\t\treturn () => {\n\t\t\tthis.displayModeChangeCallback = null;\n\t\t};\n\t}\n\n\t// OPENAI specific methods\n\tgetSafeArea(): SafeArea | null {\n\t\treturn null;\n\t}\n\n\t// MCP Apps specific methods\n\tonSafeAreaChange(): () => void {\n\t\treturn () => {};\n\t}\n\n\t// OPENAI specific methods\n\tgetMaxHeight(): number | null {\n\t\treturn null;\n\t}\n\n\t// MCP Apps specific methods\n\tonMaxHeightChange(): () => void {\n\t\treturn () => {};\n\t}\n\n\t// MCP Apps specific methods\n\tgetToolResponseMetadata(): UnknownObject | null {\n\t\treturn null;\n\t}\n\n\t// MCP Apps specific methods\n\tonToolResponseMetadataChange(): () => void {\n\t\treturn () => {};\n\t}\n\n\t// MCP Apps specific methods\n\tgetWidgetState(): UnknownObject | null {\n\t\treturn null;\n\t}\n\n\t// MCP Apps specific methods\n\tonWidgetStateChange(): () => void {\n\t\treturn () => {};\n\t}\n}\n"],"mappings":";AACA,OAAS,OAAAA,EAAK,wBAAAC,MAA4B,iCAiBnC,IAAMC,EAAN,KAAyD,CACvD,IACA,mBAA4D,KAC5D,oBAAuD,KACvD,0BACP,KACO,YACA,iBAAsC,KAE9C,aAAc,CACb,KAAK,IAAM,IAAIF,EACd,CAAE,KAAM,kBAAmB,QAAS,OAAQ,EAC5C,CAAC,EACD,CAAE,WAAY,EAAK,CACpB,EAGA,KAAK,IAAI,aAAgBG,GAAW,CACnC,IAAMC,EAAqB,CAC1B,QAASD,EAAO,QAChB,kBAAmBA,EAAO,iBAG3B,EACA,KAAK,iBAAmBC,EACxB,KAAK,qBAAqBA,CAAM,CACjC,EAEA,KAAK,IAAI,qBAAwBD,GAAW,CAC3C,KAAK,YAAc,CAAE,GAAG,KAAK,YAAa,GAAGA,CAAO,EAChDA,EAAO,OACV,KAAK,sBAAsBA,EAAO,KAAc,EAE7CA,EAAO,aACV,KAAK,4BAA4BA,EAAO,WAA0B,CAEpE,CACD,CAEA,MAAM,SAAyB,CAC9B,MAAM,KAAK,IAAI,QACd,IAAIF,EAAqB,OAAO,OAAQ,OAAO,MAAM,CACtD,EACA,KAAK,YAAc,KAAK,IAAI,eAAe,CAC5C,CAEA,eAAuD,CAGtD,OAAI,KAAK,kBAAkB,kBACnB,KAAK,iBAAiB,kBAEvB,IACR,CAEA,aAAaI,EAAoD,CAChE,YAAK,mBAAqBA,EACnB,IAAM,CACZ,KAAK,mBAAqB,IAC3B,CACD,CAEA,MAAM,SACLC,EACAC,EAC0B,CAC1B,IAAMH,EAAS,MAAM,KAAK,IAAI,eAAe,CAC5C,KAAAE,EACA,UAAWC,CACZ,CAAC,EACD,MAAO,CACN,QAASH,EAAO,QAChB,kBAAmBA,EAAO,iBAG3B,CACD,CAEA,aAAaI,EAAmB,CAC/B,KAAK,IAAI,aAAa,CAAE,IAAAA,CAAI,CAAC,EAAE,MAAOC,GAAiB,CACtD,QAAQ,MAAM,uBAAwBA,CAAG,CAC1C,CAAC,CACF,CAEA,aAAaC,EAAsB,CAClC,KAAK,IACH,YAAY,CACZ,KAAM,OACN,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAMA,CAAO,CAAC,CACzC,CAAC,EACA,MAAOD,GAAiB,CACxB,QAAQ,MAAM,oCAAqCA,CAAG,CACvD,CAAC,CACH,CAEA,UAAkB,CACjB,OAAQ,KAAK,aAAa,OAAmB,OAC9C,CAEA,cAAcJ,EAA8C,CAC3D,YAAK,oBAAsBA,EACpB,IAAM,CACZ,KAAK,oBAAsB,IAC5B,CACD,CAEA,WAAoB,CACnB,OAAQ,KAAK,aAAa,QAAiC,IAC5D,CAEA,gBAA8B,CAC7B,OAAQ,KAAK,aAAa,aAA+B,QAC1D,CAEA,MAAM,mBAAmBM,EAAyC,CAEjE,OADe,MAAM,KAAK,IAAI,mBAAmB,CAAE,KAAAA,CAAK,CAAC,GAC3C,IACf,CAEA,oBAAoBN,EAAmD,CACtE,YAAK,0BAA4BA,EAC1B,IAAM,CACZ,KAAK,0BAA4B,IAClC,CACD,CAGA,aAA+B,CAC9B,OAAO,IACR,CAGA,kBAA+B,CAC9B,MAAO,IAAM,CAAC,CACf,CAGA,cAA8B,CAC7B,OAAO,IACR,CAGA,mBAAgC,CAC/B,MAAO,IAAM,CAAC,CACf,CAGA,yBAAgD,CAC/C,OAAO,IACR,CAGA,8BAA2C,CAC1C,MAAO,IAAM,CAAC,CACf,CAGA,gBAAuC,CACtC,OAAO,IACR,CAGA,qBAAkC,CACjC,MAAO,IAAM,CAAC,CACf,CACD","names":["App","PostMessageTransport","MCPAppsWidgetClient","params","result","callback","name","args","url","err","prompt","mode"]}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import{a as l}from"./chunk-ZUGQBRJF.js";var i=class{getGlobal(e,n){return typeof window>"u"?n:window.openai?.[e]??n}onGlobalChange(e,n,o){if(typeof window>"u")return()=>{};let a=s=>{let t=s.detail.globals[e];t!==void 0&&n(o?o(t):t)};return window.addEventListener(l,a,{passive:!0}),()=>window.removeEventListener(l,a)}async connect(){if(typeof window>"u"||!("openai"in window))throw new Error("OpenAI global not found. Are you running in ChatGPT?")}getToolOutput(){return this.getGlobal("toolOutput",null)}onToolResult(e){return this.onGlobalChange("toolOutput",e,n=>({structuredContent:n??{}}))}async callTool(e,n){if(typeof window>"u"||!window.openai?.callTool)throw new Error("OpenAI callTool not available");return{content:[{type:"text",text:(await window.openai.callTool(e,n)).result}]}}openExternal(e){typeof window<"u"&&window.openai?.openExternal&&window.openai.openExternal({href:e})}sendFollowUp(e){typeof window<"u"&&window.openai?.sendFollowUpMessage&&window.openai.sendFollowUpMessage({prompt:e})}getTheme(){return this.getGlobal("theme","light")}onThemeChange(e){return this.onGlobalChange("theme",e)}getLocale(){return this.getGlobal("locale","en")}getDisplayMode(){return this.getGlobal("displayMode","inline")}async requestDisplayMode(e){return typeof window>"u"||!window.openai?.requestDisplayMode?"inline":(await window.openai.requestDisplayMode({mode:e})).mode}onDisplayModeChange(e){return this.onGlobalChange("displayMode",e)}getSafeArea(){return this.getGlobal("safeArea",null)}onSafeAreaChange(e){return this.onGlobalChange("safeArea",e,n=>n??null)}getMaxHeight(){return this.getGlobal("maxHeight",null)}onMaxHeightChange(e){return this.onGlobalChange("maxHeight",e,n=>n??null)}getToolResponseMetadata(){return this.getGlobal("toolResponseMetadata",null)}onToolResponseMetadataChange(e){return this.onGlobalChange("toolResponseMetadata",e,n=>n??null)}getWidgetState(){return this.getGlobal("widgetState",null)}onWidgetStateChange(e){return this.onGlobalChange("widgetState",e,n=>n??null)}};export{i as OpenAIWidgetClient};
|
|
3
|
-
//# sourceMappingURL=openai-client-3M6MDERR.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp/react/widgets/openai-client.ts"],"sourcesContent":["import type {\n\tDisplayMode,\n\tSafeArea,\n\tTheme,\n\tUnknownObject,\n} from \"../hooks/@types\";\nimport { SET_GLOBALS_EVENT_TYPE, type SetGlobalsEvent } from \"../hooks/@types\";\nimport type {\n\tToolCallResult,\n\tToolResult,\n\tUnifiedWidgetClient,\n} from \"./widget-client\";\n\ntype GlobalsKey = keyof SetGlobalsEvent[\"detail\"][\"globals\"];\n\n/**\n * OpenAI widget client implementation.\n * Uses window.openai global object injected by ChatGPT.\n */\nexport class OpenAIWidgetClient implements UnifiedWidgetClient {\n\tprivate getGlobal<T>(\n\t\tkey: keyof NonNullable<typeof window.openai>,\n\t\tfallback: T,\n\t): T {\n\t\tif (typeof window === \"undefined\") return fallback;\n\t\treturn (window.openai?.[key] as T) ?? fallback;\n\t}\n\n\tprivate onGlobalChange<\n\t\tK extends GlobalsKey,\n\t\tT = SetGlobalsEvent[\"detail\"][\"globals\"][K],\n\t>(\n\t\tkey: K,\n\t\tcallback: (value: T) => void,\n\t\ttransform?: (value: SetGlobalsEvent[\"detail\"][\"globals\"][K]) => T,\n\t): () => void {\n\t\tif (typeof window === \"undefined\") return () => {};\n\n\t\tconst handler = (event: SetGlobalsEvent) => {\n\t\t\tconst value = event.detail.globals[key];\n\t\t\tif (value !== undefined) {\n\t\t\t\tcallback(transform ? transform(value) : (value as T));\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(SET_GLOBALS_EVENT_TYPE, handler, { passive: true });\n\t\treturn () => window.removeEventListener(SET_GLOBALS_EVENT_TYPE, handler);\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (typeof window === \"undefined\" || !(\"openai\" in window)) {\n\t\t\tthrow new Error(\"OpenAI global not found. Are you running in ChatGPT?\");\n\t\t}\n\t}\n\n\tgetToolOutput<T = Record<string, unknown>>(): T | null {\n\t\treturn this.getGlobal<T | null>(\"toolOutput\", null);\n\t}\n\n\tonToolResult(callback: (result: ToolResult) => void): () => void {\n\t\treturn this.onGlobalChange(\"toolOutput\", callback, (v) => ({\n\t\t\tstructuredContent: v ?? {},\n\t\t}));\n\t}\n\n\tasync callTool(\n\t\tname: string,\n\t\targs: Record<string, unknown>,\n\t): Promise<ToolCallResult> {\n\t\tif (typeof window === \"undefined\" || !window.openai?.callTool) {\n\t\t\tthrow new Error(\"OpenAI callTool not available\");\n\t\t}\n\t\tconst response = await window.openai.callTool(name, args);\n\t\treturn { content: [{ type: \"text\", text: response.result }] };\n\t}\n\n\topenExternal(url: string): void {\n\t\tif (typeof window !== \"undefined\" && window.openai?.openExternal) {\n\t\t\twindow.openai.openExternal({ href: url });\n\t\t}\n\t}\n\n\tsendFollowUp(prompt: string): void {\n\t\tif (typeof window !== \"undefined\" && window.openai?.sendFollowUpMessage) {\n\t\t\twindow.openai.sendFollowUpMessage({ prompt });\n\t\t}\n\t}\n\n\tgetTheme(): Theme {\n\t\treturn this.getGlobal(\"theme\", \"light\" as Theme);\n\t}\n\n\tonThemeChange(callback: (theme: Theme) => void): () => void {\n\t\treturn this.onGlobalChange(\"theme\", callback);\n\t}\n\n\tgetLocale(): string {\n\t\treturn this.getGlobal(\"locale\", \"en\");\n\t}\n\n\tgetDisplayMode(): DisplayMode {\n\t\treturn this.getGlobal(\"displayMode\", \"inline\" as DisplayMode);\n\t}\n\n\tasync requestDisplayMode(mode: DisplayMode): Promise<DisplayMode> {\n\t\tif (typeof window === \"undefined\" || !window.openai?.requestDisplayMode) {\n\t\t\treturn \"inline\";\n\t\t}\n\t\tconst result = await window.openai.requestDisplayMode({ mode });\n\t\treturn result.mode;\n\t}\n\n\tonDisplayModeChange(callback: (mode: DisplayMode) => void): () => void {\n\t\treturn this.onGlobalChange(\"displayMode\", callback);\n\t}\n\n\tgetSafeArea(): SafeArea | null {\n\t\treturn this.getGlobal(\"safeArea\", null);\n\t}\n\n\tonSafeAreaChange(callback: (safeArea: SafeArea | null) => void): () => void {\n\t\treturn this.onGlobalChange(\"safeArea\", callback, (v) => v ?? null);\n\t}\n\n\tgetMaxHeight(): number | null {\n\t\treturn this.getGlobal(\"maxHeight\", null);\n\t}\n\n\tonMaxHeightChange(callback: (maxHeight: number | null) => void): () => void {\n\t\treturn this.onGlobalChange(\"maxHeight\", callback, (v) => v ?? null);\n\t}\n\n\tgetToolResponseMetadata(): UnknownObject | null {\n\t\treturn this.getGlobal(\"toolResponseMetadata\", null);\n\t}\n\n\tonToolResponseMetadataChange(\n\t\tcallback: (metadata: UnknownObject | null) => void,\n\t): () => void {\n\t\treturn this.onGlobalChange(\n\t\t\t\"toolResponseMetadata\",\n\t\t\tcallback,\n\t\t\t(v) => v ?? null,\n\t\t);\n\t}\n\n\tgetWidgetState(): UnknownObject | null {\n\t\treturn this.getGlobal<UnknownObject | null>(\"widgetState\", null);\n\t}\n\n\tonWidgetStateChange(\n\t\tcallback: (state: UnknownObject | null) => void,\n\t): () => void {\n\t\treturn this.onGlobalChange(\"widgetState\", callback, (v) => v ?? null);\n\t}\n}\n"],"mappings":";wCAmBO,IAAMA,EAAN,KAAwD,CACtD,UACPC,EACAC,EACI,CACJ,OAAI,OAAO,OAAW,IAAoBA,EAClC,OAAO,SAASD,CAAG,GAAWC,CACvC,CAEQ,eAIPD,EACAE,EACAC,EACa,CACb,GAAI,OAAO,OAAW,IAAa,MAAO,IAAM,CAAC,EAEjD,IAAMC,EAAWC,GAA2B,CAC3C,IAAMC,EAAQD,EAAM,OAAO,QAAQL,CAAG,EAClCM,IAAU,QACbJ,EAASC,EAAYA,EAAUG,CAAK,EAAKA,CAAW,CAEtD,EAEA,cAAO,iBAAiBC,EAAwBH,EAAS,CAAE,QAAS,EAAK,CAAC,EACnE,IAAM,OAAO,oBAAoBG,EAAwBH,CAAO,CACxE,CAEA,MAAM,SAAyB,CAC9B,GAAI,OAAO,OAAW,KAAe,EAAE,WAAY,QAClD,MAAM,IAAI,MAAM,sDAAsD,CAExE,CAEA,eAAuD,CACtD,OAAO,KAAK,UAAoB,aAAc,IAAI,CACnD,CAEA,aAAaF,EAAoD,CAChE,OAAO,KAAK,eAAe,aAAcA,EAAWM,IAAO,CAC1D,kBAAmBA,GAAK,CAAC,CAC1B,EAAE,CACH,CAEA,MAAM,SACLC,EACAC,EAC0B,CAC1B,GAAI,OAAO,OAAW,KAAe,CAAC,OAAO,QAAQ,SACpD,MAAM,IAAI,MAAM,+BAA+B,EAGhD,MAAO,CAAE,QAAS,CAAC,CAAE,KAAM,OAAQ,MADlB,MAAM,OAAO,OAAO,SAASD,EAAMC,CAAI,GACN,MAAO,CAAC,CAAE,CAC7D,CAEA,aAAaC,EAAmB,CAC3B,OAAO,OAAW,KAAe,OAAO,QAAQ,cACnD,OAAO,OAAO,aAAa,CAAE,KAAMA,CAAI,CAAC,CAE1C,CAEA,aAAaC,EAAsB,CAC9B,OAAO,OAAW,KAAe,OAAO,QAAQ,qBACnD,OAAO,OAAO,oBAAoB,CAAE,OAAAA,CAAO,CAAC,CAE9C,CAEA,UAAkB,CACjB,OAAO,KAAK,UAAU,QAAS,OAAgB,CAChD,CAEA,cAAcV,EAA8C,CAC3D,OAAO,KAAK,eAAe,QAASA,CAAQ,CAC7C,CAEA,WAAoB,CACnB,OAAO,KAAK,UAAU,SAAU,IAAI,CACrC,CAEA,gBAA8B,CAC7B,OAAO,KAAK,UAAU,cAAe,QAAuB,CAC7D,CAEA,MAAM,mBAAmBW,EAAyC,CACjE,OAAI,OAAO,OAAW,KAAe,CAAC,OAAO,QAAQ,mBAC7C,UAEO,MAAM,OAAO,OAAO,mBAAmB,CAAE,KAAAA,CAAK,CAAC,GAChD,IACf,CAEA,oBAAoBX,EAAmD,CACtE,OAAO,KAAK,eAAe,cAAeA,CAAQ,CACnD,CAEA,aAA+B,CAC9B,OAAO,KAAK,UAAU,WAAY,IAAI,CACvC,CAEA,iBAAiBA,EAA2D,CAC3E,OAAO,KAAK,eAAe,WAAYA,EAAWM,GAAMA,GAAK,IAAI,CAClE,CAEA,cAA8B,CAC7B,OAAO,KAAK,UAAU,YAAa,IAAI,CACxC,CAEA,kBAAkBN,EAA0D,CAC3E,OAAO,KAAK,eAAe,YAAaA,EAAWM,GAAMA,GAAK,IAAI,CACnE,CAEA,yBAAgD,CAC/C,OAAO,KAAK,UAAU,uBAAwB,IAAI,CACnD,CAEA,6BACCN,EACa,CACb,OAAO,KAAK,eACX,uBACAA,EACCM,GAAMA,GAAK,IACb,CACD,CAEA,gBAAuC,CACtC,OAAO,KAAK,UAAgC,cAAe,IAAI,CAChE,CAEA,oBACCN,EACa,CACb,OAAO,KAAK,eAAe,cAAeA,EAAWM,GAAMA,GAAK,IAAI,CACrE,CACD","names":["OpenAIWidgetClient","key","fallback","callback","transform","handler","event","value","SET_GLOBALS_EVENT_TYPE","v","name","args","url","prompt","mode"]}
|