@waniwani/sdk 0.1.5 → 0.1.7
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/chat/index.d.ts +5 -0
- package/dist/chat/index.js +1 -1
- package/dist/chat/index.js.map +1 -1
- package/dist/mcp/index.d.ts +16 -1
- package/dist/mcp/index.js +3 -3
- package/dist/mcp/index.js.map +1 -1
- package/package.json +1 -1
package/dist/chat/index.d.ts
CHANGED
|
@@ -41,6 +41,11 @@ interface SuggestionsConfig {
|
|
|
41
41
|
* Defaults to an empty array.
|
|
42
42
|
*/
|
|
43
43
|
initial?: string[];
|
|
44
|
+
/**
|
|
45
|
+
* Enable AI-generated suggestions after each response.
|
|
46
|
+
* Defaults to `true` when suggestions config is provided.
|
|
47
|
+
*/
|
|
48
|
+
dynamic?: boolean;
|
|
44
49
|
}
|
|
45
50
|
interface ChatBaseProps {
|
|
46
51
|
/** WaniWani project API key */
|
package/dist/chat/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{forwardRef as Eo,useCallback as ye,useEffect as lt,useImperativeHandle as Io,useRef as ut,useState as ct}from"react";import{ArrowDownIcon as ht}from"lucide-react";import{useCallback as bt}from"react";import{StickToBottom as Fe,useStickToBottomContext as xt}from"use-stick-to-bottom";import{clsx as dt}from"clsx";import{extendTailwindMerge as mt}from"tailwind-merge";var ft=mt({prefix:"ww"});function i(...e){return ft(dt(e))}import{jsx as gt}from"react/jsx-runtime";var J=({className:e,variant:t="default",size:o="default",type:r="button",...n})=>gt("button",{type:r,className:i("ww:inline-flex ww:cursor-pointer ww:items-center ww:justify-center ww:rounded-md ww:font-medium ww:transition-colors ww:disabled:pointer-events-none ww:disabled:opacity-50",t==="default"&&"ww:bg-primary ww:text-primary-foreground ww:hover:bg-primary/90",t==="outline"&&"ww:border ww:border-border ww:bg-background ww:hover:bg-accent ww:hover:text-accent-foreground",t==="ghost"&&"ww:hover:bg-accent ww:hover:text-accent-foreground",o==="default"&&"ww:h-9 ww:px-4 ww:py-2 ww:text-sm",o==="sm"&&"ww:h-8 ww:px-3 ww:text-xs",o==="icon"&&"ww:size-9",o==="icon-sm"&&"ww:size-7",e),...n});import{jsx as oe}from"react/jsx-runtime";var re=({className:e,...t})=>oe(Fe,{className:i("ww:relative ww:flex-1 ww:overflow-y-hidden",e),initial:"smooth",resize:"smooth",role:"log",...t}),ne=({className:e,...t})=>oe(Fe.Content,{className:i("ww:flex ww:flex-col ww:gap-8 ww:p-4",e),...t}),se=({className:e,...t})=>{let{isAtBottom:o,scrollToBottom:r}=xt(),n=bt(()=>{r()},[r]);return!o&&oe(J,{className:i("ww:absolute ww:bottom-4 ww:left-[50%] ww:translate-x-[-50%] ww:rounded-full",e),onClick:n,size:"icon",variant:"outline",...t,children:oe(ht,{className:"ww:size-4"})})};import{ArrowUpIcon as yt,LoaderIcon as Ct,PaperclipIcon as vt,SquareIcon as Tt,XIcon as Pt}from"lucide-react";import{nanoid as Mt}from"nanoid";import{createContext as kt,useCallback as q,useContext as St,useEffect as Pe,useMemo as Et,useRef as Me,useState as Ue}from"react";import{jsx as O,jsxs as _e}from"react/jsx-runtime";var It=async e=>{try{let o=await(await fetch(e)).blob();return new Promise(r=>{let n=new FileReader;n.onloadend=()=>r(n.result),n.onerror=()=>r(null),n.readAsDataURL(o)})}catch{return null}},Oe=kt(null),ze=()=>{let e=St(Oe);if(!e)throw new Error("usePromptInputAttachments must be used within a PromptInput");return e},ae=({className:e,accept:t,multiple:o,globalDrop:r,maxFiles:n,maxFileSize:s,onSubmit:a,children:w,...m})=>{let c=Me(null),l=Me(null),[p,y]=Ue([]),C=Me(p);Pe(()=>{C.current=p},[p]);let E=q(()=>{c.current?.click()},[]),P=q(b=>{let v=[...b];if(v.length===0)return;let M=g=>s?g.size<=s:!0,N=v.filter(M);y(g=>{let I=typeof n=="number"?Math.max(0,n-g.length):void 0,R=typeof I=="number"?N.slice(0,I):N;return[...g,...R.map(k=>({filename:k.name,id:Mt(),mediaType:k.type,type:"file",url:URL.createObjectURL(k)}))]})},[n,s]),f=q(b=>{y(v=>{let M=v.find(N=>N.id===b);return M?.url&&URL.revokeObjectURL(M.url),v.filter(N=>N.id!==b)})},[]),u=q(()=>{y(b=>{for(let v of b)v.url&&URL.revokeObjectURL(v.url);return[]})},[]);Pe(()=>()=>{for(let b of C.current)b.url&&URL.revokeObjectURL(b.url)},[]);let d=q(b=>{b.currentTarget.files&&P(b.currentTarget.files),b.currentTarget.value=""},[P]);Pe(()=>{if(!r)return;let b=M=>{M.dataTransfer?.types?.includes("Files")&&M.preventDefault()},v=M=>{M.dataTransfer?.types?.includes("Files")&&M.preventDefault(),M.dataTransfer?.files&&M.dataTransfer.files.length>0&&P(M.dataTransfer.files)};return document.addEventListener("dragover",b),document.addEventListener("drop",v),()=>{document.removeEventListener("dragover",b),document.removeEventListener("drop",v)}},[P,r]);let x=q(async b=>{b.preventDefault();let v=b.currentTarget,N=new FormData(v).get("message")||"";v.reset();let g=await Promise.all(p.map(async({id:I,...R})=>{if(R.url?.startsWith("blob:")){let k=await It(R.url);return{...R,url:k??R.url}}return R}));try{let I=a({files:g,text:N},b);I instanceof Promise&&await I,u()}catch{}},[p,a,u]),D=Et(()=>({add:P,clear:u,files:p,openFileDialog:E,remove:f}),[p,P,f,u,E]);return _e(Oe.Provider,{value:D,children:[O("input",{accept:t,"aria-label":"Upload files",className:"ww:hidden",multiple:o,onChange:d,ref:c,title:"Upload files",type:"file"}),O("form",{className:i("ww:flex ww:w-full ww:flex-col ww:rounded-lg ww:border ww:border-border ww:bg-background",e),onSubmit:x,ref:l,...m,children:w})]})};var ie=({onChange:e,onKeyDown:t,className:o,placeholder:r="What would you like to know?",...n})=>{let s=ze(),[a,w]=Ue(!1),m=q(l=>{if(t?.(l),!l.defaultPrevented){if(l.key==="Enter"){if(a||l.nativeEvent.isComposing||l.shiftKey)return;l.preventDefault();let{form:p}=l.currentTarget;if(p?.querySelector('button[type="submit"]')?.disabled)return;p?.requestSubmit()}if(l.key==="Backspace"&&l.currentTarget.value===""&&s.files.length>0){l.preventDefault();let p=s.files.at(-1);p&&s.remove(p.id)}}},[t,a,s]),c=q(l=>{let p=l.clipboardData?.items;if(!p)return;let y=[];for(let C of p)if(C.kind==="file"){let E=C.getAsFile();E&&y.push(E)}y.length>0&&(l.preventDefault(),s.add(y))},[s]);return O("textarea",{className:i("ww:field-sizing-content ww:max-h-48 ww:min-h-16 ww:w-full ww:resize-none ww:border-0 ww:bg-transparent ww:px-3 ww:py-3 ww:text-sm ww:outline-none ww:placeholder:text-muted-foreground",o),name:"message",onCompositionEnd:()=>w(!1),onCompositionStart:()=>w(!0),onKeyDown:m,onPaste:c,placeholder:r,onChange:e,...n})},we=({className:e,status:t,onStop:o,onClick:r,children:n,...s})=>{let a=t==="submitted"||t==="streaming",w=O(yt,{className:"ww:size-4"});t==="submitted"?w=O(Ct,{className:"ww:size-4 ww:animate-spin"}):t==="streaming"&&(w=O(Tt,{className:"ww:size-4"}));let m=q(c=>{if(a&&o){c.preventDefault(),o();return}r?.(c)},[a,o,r]);return O(J,{"aria-label":a?"Stop":"Submit",className:i("ww:bg-foreground ww:text-background ww:hover:bg-foreground",e),onClick:m,size:"icon-sm",type:a&&o?"button":"submit",variant:"ghost",...s,children:n??w})},le=({className:e,children:t,...o})=>{let r=ze();return r.files.length>0?_e(J,{className:i("ww:group ww:relative",e),onClick:()=>r.clear(),size:"icon-sm",type:"button",variant:"ghost","aria-label":"Remove all attachments",...o,children:[O("span",{className:"ww:flex ww:size-5 ww:items-center ww:justify-center ww:rounded-full ww:bg-primary ww:text-[10px] ww:font-medium ww:text-primary-foreground ww:transition-opacity ww:group-hover:opacity-0",children:r.files.length}),O(Pt,{className:"ww:absolute ww:size-4 ww:opacity-0 ww:transition-opacity ww:group-hover:opacity-100"})]}):O(J,{className:i(e),onClick:()=>r.openFileDialog(),size:"icon-sm",type:"button",variant:"ghost",...o,children:t??O(vt,{className:"ww:size-4"})})};import{FileIcon as Rt}from"lucide-react";import{jsx as Y,jsxs as Lt}from"react/jsx-runtime";var We=({files:e,className:t,...o})=>e.length===0?null:Y("div",{className:i("ww:flex ww:flex-wrap ww:gap-1.5",t),...o,children:e.map((r,n)=>Y(Nt,{file:r},n))});function Nt({file:e}){return e.mediaType?.startsWith("image/")&&e.url?Y("img",{src:e.url,alt:e.filename??"attachment",className:"ww:h-16 ww:max-w-32 ww:rounded ww:object-cover"}):Lt("span",{className:"ww:inline-flex ww:items-center ww:gap-1.5 ww:rounded ww:bg-background/20 ww:px-2 ww:py-1 ww:text-xs",children:[Y(Rt,{className:"ww:size-3 ww:shrink-0"}),Y("span",{className:"ww:max-w-24 ww:truncate",children:e.filename??"file"})]})}import{jsx as $e}from"react/jsx-runtime";var ke=({className:e,size:t=5,...o})=>$e("div",{className:i("ww:flex ww:items-center ww:gap-1",e),...o,children:[0,1,2].map(r=>$e("div",{className:"ww:rounded-full ww:bg-muted-foreground/60",style:{width:t,height:t,animation:"ww-pulse 1.4s ease-in-out infinite",animationDelay:`${r*.2}s`}},r))});import{cjk as At}from"@streamdown/cjk";import{code as Ht}from"@streamdown/code";import{memo as Bt}from"react";import{Streamdown as Dt}from"streamdown";import{jsx as Se}from"react/jsx-runtime";var ue=({className:e,from:t,...o})=>Se("div",{className:i("ww:group ww:flex ww:w-full ww:max-w-[95%] ww:flex-col ww:gap-2",t==="user"?"is-user ww:ml-auto ww:justify-end":"is-assistant",e),...o}),ce=({children:e,className:t,...o})=>Se("div",{className:i("ww:flex ww:w-fit ww:min-w-0 ww:max-w-full ww:flex-col ww:gap-2 ww:overflow-hidden ww:text-base","ww:group-[.is-user]:ml-auto ww:group-[.is-user]:rounded-lg ww:group-[.is-user]:bg-user-bubble ww:group-[.is-user]:px-4 ww:group-[.is-user]:py-3 ww:group-[.is-user]:text-primary-foreground","ww:group-[.is-assistant]:text-foreground",t),...o,children:e}),Ft={cjk:At,code:Ht},pe=Bt(({className:e,...t})=>Se(Dt,{className:i("ww:size-full ww:[&>*:first-child]:mt-0 ww:[&>*:last-child]:mb-0",e),plugins:Ft,...t}),(e,t)=>e.children===t.children);pe.displayName="MessageResponse";import{jsx as Ut}from"react/jsx-runtime";function je({className:e,text:t,...o}){return t?Ut("pre",{className:i("ww:mb-2 ww:overflow-x-auto ww:whitespace-pre-wrap ww:break-words ww:text-xs ww:font-mono ww:text-muted-foreground",e),...o,children:t}):null}import{BracesIcon as Ot,CheckIcon as zt,ChevronDownIcon as _t,ChevronRightIcon as Wt,ClipboardCopyIcon as $t,ServerIcon as xr}from"lucide-react";import{createContext as jt,useCallback as qt,useContext as Ve,useEffect as Vt,useMemo as Jt,useRef as Kt,useState as Ee}from"react";import{Fragment as qe,jsx as T,jsxs as K}from"react/jsx-runtime";function Gt(e,t=80){if(e==null)return String(e);if(typeof e!="object")return String(e).slice(0,t);if(Array.isArray(e))return`Array(${e.length})`;let o=JSON.stringify(e);if(o.length<=t)return o;let r=Object.entries(e),n=[],s=t-2;for(let[a,w]of r){if(s<=8)break;let m=a.length>4?`${a.slice(0,4)}\u2026`:a,c;typeof w=="string"?c=w.length>2?`'${w.slice(0,1)}\u2026`:`'${w}'`:Array.isArray(w)?c=`Array(${w.length})`:typeof w=="object"&&w!==null?c="{\u2026}":c=String(w);let l=`${m}\u2009${c}`;n.push(l),s-=l.length+3}return`{${n.join(", ")}}`}function Xt({text:e,className:t}){let[o,r]=Ee(!1),n=Kt(null);Vt(()=>()=>{n.current&&clearTimeout(n.current)},[]);let s=qt(async a=>{a.stopPropagation();try{await navigator.clipboard.writeText(e),r(!0),n.current&&clearTimeout(n.current),n.current=setTimeout(()=>r(!1),2e3)}catch{}},[e]);return T(J,{variant:"ghost",size:"sm",onClick:s,className:i("ww:h-auto ww:gap-1 ww:px-1.5 ww:py-0.5 ww:text-xs ww:text-muted-foreground ww:hover:text-foreground",t),children:o?K(qe,{children:[T(zt,{className:"ww:size-3.5"}),T("span",{children:"Copied"})]}):K(qe,{children:[T($t,{className:"ww:size-3.5"}),T("span",{children:"Copy"})]})})}function Je({data:e,label:t,className:o,...r}){let[n,s]=Ee(!1),a=Jt(()=>JSON.stringify(e,null,2),[e]),w=Gt(e);return K("div",{className:i("ww:rounded-lg ww:bg-tool-card",o),...r,children:[K("div",{className:"ww:flex ww:items-center ww:justify-between ww:px-3 ww:pt-2.5 ww:pb-1.5",children:[T("span",{className:"ww:text-xs ww:font-medium ww:text-muted-foreground",children:t}),T(Xt,{text:a})]}),K("button",{type:"button",onClick:()=>s(m=>!m),className:"ww:flex ww:w-full ww:items-start ww:gap-2 ww:px-3 ww:pb-3 ww:text-left",children:[T(Wt,{className:i("ww:mt-0.5 ww:size-3.5 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-150",n&&"ww:rotate-90")}),n?T("pre",{className:"ww:overflow-x-auto ww:text-xs ww:font-mono ww:text-foreground ww:whitespace-pre-wrap ww:break-all",children:T("code",{children:a})}):T("span",{className:"ww:truncate ww:text-xs ww:font-mono ww:text-foreground/80",children:w})]})]})}var Ie=jt({open:!1,toggle:()=>{}});function Ke({className:e,defaultOpen:t=!1,children:o,...r}){let[n,s]=Ee(t);return T(Ie.Provider,{value:{open:n,toggle:()=>s(a=>!a)},children:T("div",{className:i("ww:mb-4 ww:w-full",e),"data-state":n?"open":"closed",...r,children:o})})}function Ge({className:e,title:t,state:o,...r}){let{open:n,toggle:s}=Ve(Ie),a=o==="input-available"||o==="input-streaming";return K("button",{type:"button",onClick:s,className:i("ww:flex ww:w-full ww:items-center ww:justify-between ww:gap-3 ww:py-1.5",e),"aria-expanded":n,...r,children:[K("div",{className:"ww:flex ww:min-w-0 ww:items-center ww:gap-2",children:[T(Ot,{className:"ww:size-4 ww:shrink-0 ww:text-muted-foreground"}),T("span",{className:"ww:truncate ww:text-sm ww:font-medium",children:t}),a&&T("span",{className:"ww:size-2 ww:shrink-0 ww:rounded-full ww:bg-primary ww:animate-pulse"})]}),T(_t,{className:i("ww:size-4 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-200",n&&"ww:rotate-180")})]})}function Xe({className:e,children:t,...o}){let{open:r}=Ve(Ie);return T("div",{className:i("ww:grid ww:transition-[grid-template-rows,opacity] ww:duration-200 ww:ease-out",r?"ww:grid-rows-[1fr] ww:opacity-100":"ww:grid-rows-[0fr] ww:opacity-0"),children:T("div",{className:"ww:min-h-0 ww:overflow-hidden",children:T("div",{className:i("ww:mt-2 ww:space-y-3 ww:rounded-lg ww:border ww:border-border ww:bg-background ww:p-3",e),...o,children:t})})})}function Ye({className:e,input:t,...o}){return T(Je,{data:t,label:"Request",className:e,...o})}function Ze(e){if(typeof e!="object"||e===null)return;let t=e._meta;if(typeof t!="object"||t===null)return;let o=t.ui;if(!(typeof o!="object"||o===null))return o}function Qe(e){let t=Ze(e)?.resourceUri;return typeof t=="string"?t:void 0}function et(e){return Ze(e)?.autoHeight===!0}function tt({className:e,output:t,errorText:o,...r}){return t||o?o?K("div",{className:i("ww:space-y-2",e),...r,children:[T("h4",{className:"ww:text-xs ww:font-medium ww:uppercase ww:tracking-wide ww:text-muted-foreground",children:"Error"}),T("div",{className:"ww:rounded-lg ww:bg-destructive/10 ww:p-3 ww:text-xs ww:text-destructive",children:o})]}):T(Je,{data:t,label:"Response",className:e,...r}):null}import{useCallback as Yt,useEffect as ot,useMemo as Zt,useRef as W,useState as rt}from"react";import{jsx as so}from"react/jsx-runtime";var Qt="/api/mcp/resource",eo=500,to=0,oo="2026-01-26",ro=300,no=3e3,Re=3;function Ne({resourceUri:e,toolInput:t,toolResult:o,resourceEndpoint:r=Qt,isDark:n=!1,className:s,autoHeight:a=!0,onOpenLink:w,onFollowUp:m}){let c=W(null),l=W(t),p=W(o),y=W({width:0,height:0}),C=W(null),E=W(!1),P=W(0),[f,u]=rt(to),[d,x]=rt(void 0),D=W(w),b=W(m);l.current=t,p.current=o,D.current=w,b.current=m;let v=Yt(g=>a?Math.max(g,0):Math.min(Math.max(g,50),eo),[a]),M=Zt(()=>`${r}?uri=${encodeURIComponent(e)}`,[r,e]),N=W(n);return N.current=n,ot(()=>{if(!E.current)return;let g=c.current;g?.contentWindow&&g.contentWindow.postMessage({jsonrpc:"2.0",method:"ui/notifications/host-context-changed",params:{theme:n?"dark":"light"}},"*")},[n]),ot(()=>{let g=c.current;if(!g)return;let I=!1,R=!1,k=(...S)=>console.debug("[McpAppFrame]",...S);k("effect mounted, waiting for handshake");let L=setTimeout(()=>{if(I||R)return;if(P.current>=Re){k("handshake failed after",Re,"retries, giving up");return}P.current+=1,k("handshake timeout, reloading iframe (retry",P.current,"of",Re,")");let S=new URL(g.src);S.searchParams.set("_retry",String(P.current)),g.src=S.toString()},no),h=S=>{k("\u2192 send",S.method??`response:${S.id}`,S),g.contentWindow?.postMessage(S,"*")},A=S=>{if(I||S.source!==g.contentWindow)return;let B=S.data;if(!B||typeof B!="object"||B.jsonrpc!=="2.0")return;let j=B.method,F=B.id;if(k("\u2190 recv",j??`response:${F}`,B),j==="ui/initialize"&&F!=null){R=!0,clearTimeout(L),k("handshake started"),h({jsonrpc:"2.0",id:F,result:{protocolVersion:B.params?.protocolVersion??oo,hostInfo:{name:"WaniWani Chat",version:"1.0.0"},hostCapabilities:{openLinks:{},message:{}},hostContext:{theme:N.current?"dark":"light",displayMode:"inline"}}});return}if(j==="ui/notifications/initialized"){k("handshake complete, sending tool data"),E.current=!0;let z=l.current,_=p.current;h({jsonrpc:"2.0",method:"ui/notifications/tool-input",params:{arguments:z}});let V=_.content??[{type:"text",text:JSON.stringify(_)}];h({jsonrpc:"2.0",method:"ui/notifications/tool-result",params:{content:V,structuredContent:_.structuredContent}});return}if(j==="ui/notifications/size-changed"){let z=B.params,_=typeof z?.height=="number"?z.height:void 0,V=typeof z?.width=="number"?z.width:void 0,G=y.current,Ce=_!==void 0&&_!==G.height,ve=V!==void 0&&V!==G.width;if(k("size-changed",{newHeight:_,newWidth:V,lastHeight:G.height,lastWidth:G.width,heightChanged:Ce,widthChanged:ve}),!Ce&&!ve)return;if(Ce&&_!==void 0){G.height=_;let Te=v(_),De=g.getBoundingClientRect().height;if(C.current&&(C.current.cancel(),C.current=null),u(Te),g.animate&&Math.abs(De-Te)>2){let te=g.animate([{height:`${De}px`},{height:`${Te}px`}],{duration:ro,easing:"ease-out",fill:"forwards"});C.current=te,te.onfinish=()=>{C.current===te&&(te.cancel(),C.current=null)}}}ve&&a&&V!==void 0&&(G.width=V,x(V));return}if(j==="ui/open-link"&&F!=null){let z=B.params?.url;typeof z=="string"&&(D.current?D.current(z):window.open(z,"_blank","noopener,noreferrer")),h({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/message"&&F!=null){b.current&&B.params&&b.current(B.params),h({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/request-display-mode"&&F!=null){h({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/resource-teardown"&&F!=null){h({jsonrpc:"2.0",id:F,result:{}});return}j==="ping"&&F!=null&&h({jsonrpc:"2.0",id:F,result:{}})};return window.addEventListener("message",A),()=>{k("effect cleanup (disposed)"),I=!0,clearTimeout(L),window.removeEventListener("message",A)}},[a,v]),so("iframe",{ref:c,src:M,sandbox:"allow-scripts allow-forms allow-same-origin",className:i("ww:rounded-md ww:border ww:border-border",s),style:{height:f,minWidth:d?`min(${d}px, 100%)`:void 0,width:"100%",border:"none",colorScheme:"auto"},title:"MCP App"})}import{Component as ao}from"react";import{jsx as nt,jsxs as io}from"react/jsx-runtime";var de=class extends ao{state={hasError:!1};static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(t){console.warn("[WaniWani] Widget failed to render:",t.message)}render(){return this.state.hasError?io("div",{className:"ww:flex ww:items-center ww:justify-between ww:rounded-md ww:border ww:border-border ww:bg-muted/50 ww:px-4 ww:py-3 ww:text-sm ww:text-muted-foreground",children:[nt("span",{children:"Widget failed to load"}),nt("button",{type:"button",onClick:()=>this.setState({hasError:!1}),className:"ww:text-xs ww:font-medium ww:text-primary ww:hover:underline",children:"Retry"})]}):this.props.children}};import{Fragment as lo,jsx as H,jsxs as X}from"react/jsx-runtime";function wo(e){return e.replace(/[-_]/g," ").replace(/^\w/,t=>t.toUpperCase())}function me({messages:e,status:t,welcomeMessage:o,resourceEndpoint:r,isDark:n,onFollowUp:s}){let a=t==="submitted"||t==="streaming",w=e[e.length-1],m=e.length>0,c=a&&(!m||w.role==="user");return X(lo,{children:[o&&H(ue,{from:"assistant",children:H(ce,{children:H(pe,{children:o})})}),e.map(l=>{let p=l.parts.filter(u=>u.type==="text"),y=l.parts.filter(u=>u.type==="reasoning"),C=l.parts.filter(u=>u.type==="file"),E=l.parts.filter(u=>"toolCallId"in u),P=l===w&&l.role==="assistant",f=p.length>0;return X(ue,{from:l.role,children:[y.map((u,d)=>H(je,{text:u.text},`reasoning-${l.id}-${d}`)),E.map(u=>{let d="output"in u?u.output:void 0,x=d!==void 0?Qe(d):void 0,D=d!==void 0?et(d):!1;return X("div",{children:[X(Ke,{defaultOpen:u.state==="output-available",children:[H(Ge,{title:u.title??wo(u.toolName),state:u.state}),X(Xe,{children:[H(Ye,{input:u.input}),d!==void 0&&H(tt,{output:d,errorText:"errorText"in u?u.errorText:void 0})]})]}),x&&d!==void 0&&H(de,{children:H(Ne,{resourceUri:x,toolInput:u.input??{},toolResult:{content:d.content,structuredContent:d.structuredContent},resourceEndpoint:r,isDark:n,autoHeight:D,onFollowUp:s})})]},u.toolCallId)}),X(ce,{children:[C.length>0&&H(We,{files:C}),f?p.map((u,d)=>H(pe,{children:u.type==="text"?u.text:""},`${l.id}-${d}`)):P&&a&&H(ke,{})]})]},l.id)}),c&&H(ue,{from:"assistant",children:H(ce,{children:H(ke,{})})})]})}import{jsx as Le}from"react/jsx-runtime";function fe({suggestions:e,isLoading:t,onSelect:o,className:r,...n}){return e.length===0&&!t?null:Le("div",{className:i("ww:flex ww:flex-wrap ww:gap-2 ww:px-3 ww:py-2",r),...n,children:t?[0,1,2].map(s=>Le("div",{className:"ww:h-7 ww:rounded-full ww:bg-accent ww:animate-pulse",style:{width:`${60+s*20}px`}},s)):e.map((s,a)=>Le("button",{type:"button",onClick:()=>o(s),className:i("ww:rounded-full ww:border ww:border-border ww:bg-background ww:px-3 ww:py-1 ww:text-xs","ww:text-foreground ww:hover:bg-accent ww:hover:border-primary/30","ww:transition-all ww:duration-200 ww:ease-out ww:cursor-pointer","ww:animate-[ww-fade-in_0.2s_ease-out_both]"),style:{animationDelay:`${a*50}ms`},children:s},s))})}import{useChat as uo}from"@ai-sdk/react";import{DefaultChatTransport as co}from"ai";import{useCallback as st,useRef as po,useState as mo}from"react";function ge(e){let{api:t="https://app.waniwani.ai/api/chat",headers:o,body:r,onMessageSent:n,onResponseReceived:s}=e,a=po(new co({api:t,headers:{...o},body:r})),{messages:w,sendMessage:m,status:c}=uo({transport:a.current,onFinish(){s?.()},onError(d){console.warn("[WaniWani] Chat error:",d.message)}}),[l,p]=mo(""),y=st(d=>{let x=!!d.text?.trim(),D=!!d.files?.length;(x||D)&&(m({text:d.text||"",files:d.files}),n?.(d.text||""),p(""))},[m,n]),C=st(d=>{p(d.target.value)},[]),E=c==="submitted"||c==="streaming",P=w[w.length-1],f=w.length>0,u=E&&(!f||P.role==="user");return{messages:w,status:c,text:l,setText:p,handleSubmit:y,handleTextChange:C,isLoading:E,showLoaderBubble:u,lastMessage:P,hasMessages:f,sendMessage:m}}import{useCallback as fo,useEffect as at,useRef as go,useState as ho}from"react";function bo(e){for(let t of e.parts){let o=t;if(o.type==="data"||o.type==="data-suggestions"){let r=o.data;if(r&&Array.isArray(r.suggestions))return r.suggestions}}return null}function xo(e){return typeof e=="object"&&e!==null&&"initial"in e}function he(e){let{messages:t,status:o,config:r}=e,[n,s]=ho((xo(r)&&r.initial?r.initial:[])??[]),a=go(o),w=!!r,m=fo(()=>{s([])},[]),c=t[t.length-1];return at(()=>{c?.role==="user"&&m()},[c,m]),at(()=>{let l=a.current;if(a.current=o,l==="streaming"&&o==="ready"&&w){let p=[...t].reverse().find(C=>C.role==="assistant");if(!p)return;console.log("[WaniWani] Assistant parts:",p.parts);let y=bo(p);console.log("[WaniWani] Extracted suggestions:",y),y&&s(y)}},[o,w,t]),{suggestions:n,isLoading:!1,clear:m}}import{useEffect as yo,useRef as Co,useState as vo}from"react";var To=50,Po=30,Mo=2e3,it=500;function be(e,t=!0){let[o,r]=vo(""),n=Co(void 0);return yo(()=>{if(!t){r("");return}let s=0,a=!1,w=!1,m=()=>{w||(a?(s--,r(e.slice(0,s)),s<=0?(a=!1,n.current=setTimeout(m,it)):n.current=setTimeout(m,Po)):(s++,r(e.slice(0,s)),s>=e.length?(a=!0,n.current=setTimeout(m,Mo)):n.current=setTimeout(m,To)))};return n.current=setTimeout(m,it),()=>{w=!0,clearTimeout(n.current)}},[e,t]),o}var wt={primaryColor:"#6366f1",primaryForeground:"#1f2937",backgroundColor:"#ffffff",textColor:"#1f2937",mutedColor:"#6b7280",borderColor:"#e5e7eb",assistantBubbleColor:"#f3f4f6",userBubbleColor:"#f4f4f4",inputBackgroundColor:"#f9fafb",borderRadius:16,messageBorderRadius:12,fontFamily:"system-ui, -apple-system, 'Segoe UI', sans-serif",headerBackgroundColor:"#ffffff",headerTextColor:"#1f2937",statusColor:"#22c55e",toolCardColor:"#f4f4f5"},ko={backgroundColor:"#212121",headerBackgroundColor:"#1e1e1e",headerTextColor:"#ececec",textColor:"#ececec",primaryForeground:"#ffffff",mutedColor:"#8e8ea0",borderColor:"#444444",assistantBubbleColor:"#2f2f2f",userBubbleColor:"#303030",inputBackgroundColor:"#2f2f2f",primaryColor:"#6366f1",statusColor:"#22c55e",toolCardColor:"#262626"},So={primaryColor:["--ww-primary","--ww-color-primary"],primaryForeground:["--ww-primary-fg","--ww-color-primary-foreground"],backgroundColor:["--ww-bg","--ww-color-background"],textColor:["--ww-text","--ww-color-foreground","--ww-color-accent-foreground"],mutedColor:["--ww-muted","--ww-color-muted-foreground"],borderColor:["--ww-border","--ww-color-border"],assistantBubbleColor:["--ww-assistant-bubble","--ww-color-accent"],userBubbleColor:["--ww-user-bubble"],inputBackgroundColor:["--ww-input-bg","--ww-color-input"],borderRadius:["--ww-radius"],messageBorderRadius:["--ww-msg-radius"],fontFamily:["--ww-font"],headerBackgroundColor:["--ww-header-bg"],headerTextColor:["--ww-header-text"],statusColor:["--ww-status"],toolCardColor:["--ww-tool-card","--ww-color-tool-card"]};function Z(e){return{...wt,...e}}function xe(e){let t=e.backgroundColor.replace("#",""),o=parseInt(t.substring(0,2),16),r=parseInt(t.substring(2,4),16),n=parseInt(t.substring(4,6),16);return(o*299+r*587+n*114)/1e3<128}function Q(e){let t={};for(let[o,r]of Object.entries(So)){let n=e[o],s=typeof n=="number"?`${n}px`:String(n);for(let a of r)t[a]=s}return t}import{jsx as $,jsxs as Ae}from"react/jsx-runtime";var He=Eo(function(t,o){let{theme:r,width:n=600,expandedHeight:s=400,allowAttachments:a=!1,welcomeMessage:w,placeholder:m="Ask me anything...",triggerEvent:c="triggerDemoRequest",resourceEndpoint:l,api:p}=t,y=l??(p?`${p}/resource`:void 0),C=Z(r),E=Q(C),P=xe(C),f=ge(t),u=he({messages:f.messages,status:f.status,config:t.suggestions}),d=ye(h=>{let A=h.content.map(S=>S.text??"").join("").trim();A&&f.handleSubmit({text:A,files:[]})},[f.handleSubmit]),x=ye(h=>{u.clear(),f.handleSubmit({text:h,files:[]})},[u.clear,f.handleSubmit]),D=be(m,!f.text),[b,v]=ct(!1),[M,N]=ct(!1),g=ut(null),I=ut(void 0),R=ye(()=>{let h=g.current;if(!h)return;h.scrollIntoView({behavior:"smooth",block:"center"});let A=h.querySelector("textarea");A&&setTimeout(()=>A.focus(),300),v(!0),N(!0),clearTimeout(I.current),I.current=setTimeout(()=>N(!1),2e3)},[]);Io(o,()=>({sendMessage:h=>{f.handleSubmit({text:h,files:[]}),R()},focus:R}),[f.handleSubmit,R]),lt(()=>{if(!c)return;let h=A=>{let S=A.detail,B=typeof S?.message=="string"?S.message:void 0;B&&f.handleSubmit({text:B,files:[]}),R()};return window.addEventListener(c,h),()=>window.removeEventListener(c,h)},[c,f.handleSubmit,R]);let k=b;lt(()=>{if(!b)return;let h=A=>{g.current&&!g.current.contains(A.target)&&v(!1)};return document.addEventListener("mousedown",h),()=>document.removeEventListener("mousedown",h)},[b]);let L=ye(()=>{v(!0)},[]);return Ae("div",{ref:g,style:{...E,width:n},"data-waniwani-chat":"","data-waniwani-layout":"bar",...P?{"data-waniwani-dark":""}:{},className:"ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground",children:[$("div",{className:i("ww:overflow-hidden ww:bg-background/80 ww:backdrop-blur-xl ww:transition-all ww:duration-300 ww:ease-out",k?"ww:opacity-100 ww:translate-y-0":"ww:opacity-0 ww:translate-y-2 ww:pointer-events-none ww:max-h-0"),style:{...k?{maxHeight:s}:void 0,maskImage:"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)",maskComposite:"intersect",WebkitMaskImage:"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)",WebkitMaskComposite:"source-in"},children:Ae(re,{className:"ww:flex-1",style:{height:s},children:[$(ne,{children:$(me,{messages:f.messages,status:f.status,welcomeMessage:w,resourceEndpoint:y,isDark:P,onFollowUp:d})}),$(se,{})]})}),$(fe,{suggestions:u.suggestions,isLoading:u.isLoading,onSelect:x}),$("div",{className:"ww:shrink-0",children:$(ae,{onSubmit:f.handleSubmit,globalDrop:a,multiple:a,className:i("ww:rounded-[var(--ww-radius)] ww:shadow-sm ww:transition-all ww:duration-300 ww:ease-out",M&&"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background"),children:Ae("div",{className:"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2",children:[a&&$(le,{}),$(ie,{onChange:f.handleTextChange,value:f.text,placeholder:D,onFocus:L,className:"ww:min-h-0 ww:py-1.5 ww:px-2"}),$(we,{status:f.status})]})})})]})});import{forwardRef as Ro,useCallback as Be,useEffect as No,useImperativeHandle as Lo,useRef as pt,useState as Ao}from"react";import{jsx as U,jsxs as ee}from"react/jsx-runtime";var Ho=Ro(function(t,o){let{theme:r,title:n="Assistant",subtitle:s,showStatus:a=!0,width:w=500,height:m=600,allowAttachments:c=!1,welcomeMessage:l,placeholder:p="Ask me anything...",triggerEvent:y="triggerDemoRequest",resourceEndpoint:C,api:E}=t,P=C??(E?`${E}/resource`:void 0),f=Z(r),u=Q(f),d=xe(f),x=ge(t),D=be(p,!x.text),[b,v]=Ao(!1),M=pt(null),N=pt(void 0),g=Be(()=>{let L=M.current;if(!L)return;L.scrollIntoView({behavior:"smooth",block:"center"});let h=L.querySelector("textarea");h&&setTimeout(()=>h.focus(),300),v(!0),clearTimeout(N.current),N.current=setTimeout(()=>v(!1),2e3)},[]),I=he({messages:x.messages,status:x.status,config:t.suggestions}),R=Be(L=>{let h=L.content.map(A=>A.text??"").join("").trim();h&&x.handleSubmit({text:h,files:[]})},[x.handleSubmit]),k=Be(L=>{I.clear(),x.handleSubmit({text:L,files:[]})},[I.clear,x.handleSubmit]);return Lo(o,()=>({sendMessage:L=>{x.handleSubmit({text:L,files:[]}),g()},focus:g}),[x.handleSubmit,g]),No(()=>{if(!y)return;let L=h=>{let A=h.detail,S=typeof A?.message=="string"?A.message:void 0;S&&x.handleSubmit({text:S,files:[]}),g()};return window.addEventListener(y,L),()=>window.removeEventListener(y,L)},[y,x.handleSubmit,g]),ee("div",{ref:M,style:{...u,width:w,height:m},"data-waniwani-chat":"","data-waniwani-layout":"card",...d?{"data-waniwani-dark":""}:{},className:i("ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground ww:bg-background ww:rounded-[var(--ww-radius)] ww:border ww:border-border ww:shadow-md ww:overflow-hidden ww:transition-shadow ww:duration-300",b&&"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background"),children:[ee("div",{className:"ww:shrink-0 ww:flex ww:items-center ww:gap-3 ww:px-4 ww:py-2 ww:border-b ww:border-border",style:{backgroundColor:f.headerBackgroundColor,color:f.headerTextColor},children:[a&&U("span",{className:"ww:size-2.5 ww:rounded-full ww:bg-status"}),ee("div",{className:"ww:flex-1 ww:min-w-0",children:[U("div",{className:"ww:text-xs ww:font-semibold ww:truncate",children:n}),s&&U("div",{className:"ww:text-[11px] ww:text-muted-foreground ww:truncate",children:s})]})]}),ee(re,{className:"ww:flex-1 ww:min-h-0 ww:bg-background",children:[U(ne,{children:U(me,{messages:x.messages,status:x.status,welcomeMessage:l,resourceEndpoint:P,isDark:d,onFollowUp:R})}),U(se,{})]}),U(fe,{suggestions:I.suggestions,isLoading:I.isLoading,onSelect:k,className:"ww:border-t ww:border-border"}),U("div",{className:"ww:shrink-0 ww:border-t ww:border-border ww:bg-background",children:U(ae,{onSubmit:x.handleSubmit,globalDrop:c,multiple:c,className:i("ww:rounded-none ww:border-0"),children:ee("div",{className:"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2",children:[c&&U(le,{}),U(ie,{onChange:x.handleTextChange,value:x.text,placeholder:D,className:"ww:min-h-0 ww:py-1.5 ww:px-2"}),U(we,{status:x.status})]})})})]})});export{He as ChatBar,Ho as ChatCard,He as ChatWidget,ko as DARK_THEME,wt as DEFAULT_THEME,Ne as McpAppFrame,Z as mergeTheme,Q as themeToCSSProperties};
|
|
2
|
+
import{forwardRef as Eo,useCallback as ye,useEffect as ut,useImperativeHandle as Io,useRef as ct,useState as pt}from"react";import{ArrowDownIcon as bt}from"lucide-react";import{useCallback as xt}from"react";import{StickToBottom as Fe,useStickToBottomContext as yt}from"use-stick-to-bottom";import{clsx as mt}from"clsx";import{extendTailwindMerge as ft}from"tailwind-merge";var gt=ft({prefix:"ww"});function i(...e){return gt(mt(e))}import{jsx as ht}from"react/jsx-runtime";var J=({className:e,variant:t="default",size:o="default",type:r="button",...n})=>ht("button",{type:r,className:i("ww:inline-flex ww:cursor-pointer ww:items-center ww:justify-center ww:rounded-md ww:font-medium ww:transition-colors ww:disabled:pointer-events-none ww:disabled:opacity-50",t==="default"&&"ww:bg-primary ww:text-primary-foreground ww:hover:bg-primary/90",t==="outline"&&"ww:border ww:border-border ww:bg-background ww:hover:bg-accent ww:hover:text-accent-foreground",t==="ghost"&&"ww:hover:bg-accent ww:hover:text-accent-foreground",o==="default"&&"ww:h-9 ww:px-4 ww:py-2 ww:text-sm",o==="sm"&&"ww:h-8 ww:px-3 ww:text-xs",o==="icon"&&"ww:size-9",o==="icon-sm"&&"ww:size-7",e),...n});import{jsx as oe}from"react/jsx-runtime";var re=({className:e,...t})=>oe(Fe,{className:i("ww:relative ww:flex-1 ww:overflow-y-hidden",e),initial:"smooth",resize:"smooth",role:"log",...t}),ne=({className:e,...t})=>oe(Fe.Content,{className:i("ww:flex ww:flex-col ww:gap-8 ww:p-4",e),...t}),se=({className:e,...t})=>{let{isAtBottom:o,scrollToBottom:r}=yt(),n=xt(()=>{r()},[r]);return!o&&oe(J,{className:i("ww:absolute ww:bottom-4 ww:left-[50%] ww:translate-x-[-50%] ww:rounded-full",e),onClick:n,size:"icon",variant:"outline",...t,children:oe(bt,{className:"ww:size-4"})})};import{ArrowUpIcon as Ct,LoaderIcon as vt,PaperclipIcon as Tt,SquareIcon as Pt,XIcon as Mt}from"lucide-react";import{nanoid as kt}from"nanoid";import{createContext as St,useCallback as q,useContext as Et,useEffect as Pe,useMemo as It,useRef as Me,useState as Ue}from"react";import{jsx as O,jsxs as _e}from"react/jsx-runtime";var Rt=async e=>{try{let o=await(await fetch(e)).blob();return new Promise(r=>{let n=new FileReader;n.onloadend=()=>r(n.result),n.onerror=()=>r(null),n.readAsDataURL(o)})}catch{return null}},Oe=St(null),ze=()=>{let e=Et(Oe);if(!e)throw new Error("usePromptInputAttachments must be used within a PromptInput");return e},ae=({className:e,accept:t,multiple:o,globalDrop:r,maxFiles:n,maxFileSize:s,onSubmit:a,children:w,...m})=>{let c=Me(null),l=Me(null),[p,y]=Ue([]),C=Me(p);Pe(()=>{C.current=p},[p]);let E=q(()=>{c.current?.click()},[]),P=q(b=>{let v=[...b];if(v.length===0)return;let M=g=>s?g.size<=s:!0,N=v.filter(M);y(g=>{let I=typeof n=="number"?Math.max(0,n-g.length):void 0,R=typeof I=="number"?N.slice(0,I):N;return[...g,...R.map(k=>({filename:k.name,id:kt(),mediaType:k.type,type:"file",url:URL.createObjectURL(k)}))]})},[n,s]),f=q(b=>{y(v=>{let M=v.find(N=>N.id===b);return M?.url&&URL.revokeObjectURL(M.url),v.filter(N=>N.id!==b)})},[]),u=q(()=>{y(b=>{for(let v of b)v.url&&URL.revokeObjectURL(v.url);return[]})},[]);Pe(()=>()=>{for(let b of C.current)b.url&&URL.revokeObjectURL(b.url)},[]);let d=q(b=>{b.currentTarget.files&&P(b.currentTarget.files),b.currentTarget.value=""},[P]);Pe(()=>{if(!r)return;let b=M=>{M.dataTransfer?.types?.includes("Files")&&M.preventDefault()},v=M=>{M.dataTransfer?.types?.includes("Files")&&M.preventDefault(),M.dataTransfer?.files&&M.dataTransfer.files.length>0&&P(M.dataTransfer.files)};return document.addEventListener("dragover",b),document.addEventListener("drop",v),()=>{document.removeEventListener("dragover",b),document.removeEventListener("drop",v)}},[P,r]);let x=q(async b=>{b.preventDefault();let v=b.currentTarget,N=new FormData(v).get("message")||"";v.reset();let g=await Promise.all(p.map(async({id:I,...R})=>{if(R.url?.startsWith("blob:")){let k=await Rt(R.url);return{...R,url:k??R.url}}return R}));try{let I=a({files:g,text:N},b);I instanceof Promise&&await I,u()}catch{}},[p,a,u]),D=It(()=>({add:P,clear:u,files:p,openFileDialog:E,remove:f}),[p,P,f,u,E]);return _e(Oe.Provider,{value:D,children:[O("input",{accept:t,"aria-label":"Upload files",className:"ww:hidden",multiple:o,onChange:d,ref:c,title:"Upload files",type:"file"}),O("form",{className:i("ww:flex ww:w-full ww:flex-col ww:rounded-lg ww:border ww:border-border ww:bg-background",e),onSubmit:x,ref:l,...m,children:w})]})};var ie=({onChange:e,onKeyDown:t,className:o,placeholder:r="What would you like to know?",...n})=>{let s=ze(),[a,w]=Ue(!1),m=q(l=>{if(t?.(l),!l.defaultPrevented){if(l.key==="Enter"){if(a||l.nativeEvent.isComposing||l.shiftKey)return;l.preventDefault();let{form:p}=l.currentTarget;if(p?.querySelector('button[type="submit"]')?.disabled)return;p?.requestSubmit()}if(l.key==="Backspace"&&l.currentTarget.value===""&&s.files.length>0){l.preventDefault();let p=s.files.at(-1);p&&s.remove(p.id)}}},[t,a,s]),c=q(l=>{let p=l.clipboardData?.items;if(!p)return;let y=[];for(let C of p)if(C.kind==="file"){let E=C.getAsFile();E&&y.push(E)}y.length>0&&(l.preventDefault(),s.add(y))},[s]);return O("textarea",{className:i("ww:field-sizing-content ww:max-h-48 ww:min-h-16 ww:w-full ww:resize-none ww:border-0 ww:bg-transparent ww:px-3 ww:py-3 ww:text-sm ww:outline-none ww:placeholder:text-muted-foreground",o),name:"message",onCompositionEnd:()=>w(!1),onCompositionStart:()=>w(!0),onKeyDown:m,onPaste:c,placeholder:r,onChange:e,...n})},we=({className:e,status:t,onStop:o,onClick:r,children:n,...s})=>{let a=t==="submitted"||t==="streaming",w=O(Ct,{className:"ww:size-4"});t==="submitted"?w=O(vt,{className:"ww:size-4 ww:animate-spin"}):t==="streaming"&&(w=O(Pt,{className:"ww:size-4"}));let m=q(c=>{if(a&&o){c.preventDefault(),o();return}r?.(c)},[a,o,r]);return O(J,{"aria-label":a?"Stop":"Submit",className:i("ww:bg-foreground ww:text-background ww:hover:bg-foreground",e),onClick:m,size:"icon-sm",type:a&&o?"button":"submit",variant:"ghost",...s,children:n??w})},le=({className:e,children:t,...o})=>{let r=ze();return r.files.length>0?_e(J,{className:i("ww:group ww:relative",e),onClick:()=>r.clear(),size:"icon-sm",type:"button",variant:"ghost","aria-label":"Remove all attachments",...o,children:[O("span",{className:"ww:flex ww:size-5 ww:items-center ww:justify-center ww:rounded-full ww:bg-primary ww:text-[10px] ww:font-medium ww:text-primary-foreground ww:transition-opacity ww:group-hover:opacity-0",children:r.files.length}),O(Mt,{className:"ww:absolute ww:size-4 ww:opacity-0 ww:transition-opacity ww:group-hover:opacity-100"})]}):O(J,{className:i(e),onClick:()=>r.openFileDialog(),size:"icon-sm",type:"button",variant:"ghost",...o,children:t??O(Tt,{className:"ww:size-4"})})};import{FileIcon as Nt}from"lucide-react";import{jsx as Y,jsxs as At}from"react/jsx-runtime";var We=({files:e,className:t,...o})=>e.length===0?null:Y("div",{className:i("ww:flex ww:flex-wrap ww:gap-1.5",t),...o,children:e.map((r,n)=>Y(Lt,{file:r},n))});function Lt({file:e}){return e.mediaType?.startsWith("image/")&&e.url?Y("img",{src:e.url,alt:e.filename??"attachment",className:"ww:h-16 ww:max-w-32 ww:rounded ww:object-cover"}):At("span",{className:"ww:inline-flex ww:items-center ww:gap-1.5 ww:rounded ww:bg-background/20 ww:px-2 ww:py-1 ww:text-xs",children:[Y(Nt,{className:"ww:size-3 ww:shrink-0"}),Y("span",{className:"ww:max-w-24 ww:truncate",children:e.filename??"file"})]})}import{jsx as $e}from"react/jsx-runtime";var ke=({className:e,size:t=5,...o})=>$e("div",{className:i("ww:flex ww:items-center ww:gap-1",e),...o,children:[0,1,2].map(r=>$e("div",{className:"ww:rounded-full ww:bg-muted-foreground/60",style:{width:t,height:t,animation:"ww-pulse 1.4s ease-in-out infinite",animationDelay:`${r*.2}s`}},r))});import{cjk as Ht}from"@streamdown/cjk";import{code as Bt}from"@streamdown/code";import{memo as Dt}from"react";import{Streamdown as Ft}from"streamdown";import{jsx as Se}from"react/jsx-runtime";var ue=({className:e,from:t,...o})=>Se("div",{className:i("ww:group ww:flex ww:w-full ww:max-w-[95%] ww:flex-col ww:gap-2",t==="user"?"is-user ww:ml-auto ww:justify-end":"is-assistant",e),...o}),ce=({children:e,className:t,...o})=>Se("div",{className:i("ww:flex ww:w-fit ww:min-w-0 ww:max-w-full ww:flex-col ww:gap-2 ww:overflow-hidden ww:text-base","ww:group-[.is-user]:ml-auto ww:group-[.is-user]:rounded-lg ww:group-[.is-user]:bg-user-bubble ww:group-[.is-user]:px-4 ww:group-[.is-user]:py-3 ww:group-[.is-user]:text-primary-foreground","ww:group-[.is-assistant]:text-foreground",t),...o,children:e}),Ut={cjk:Ht,code:Bt},pe=Dt(({className:e,...t})=>Se(Ft,{className:i("ww:size-full ww:[&>*:first-child]:mt-0 ww:[&>*:last-child]:mb-0",e),plugins:Ut,...t}),(e,t)=>e.children===t.children);pe.displayName="MessageResponse";import{jsx as Ot}from"react/jsx-runtime";function je({className:e,text:t,...o}){return t?Ot("pre",{className:i("ww:mb-2 ww:overflow-x-auto ww:whitespace-pre-wrap ww:break-words ww:text-xs ww:font-mono ww:text-muted-foreground",e),...o,children:t}):null}import{BracesIcon as zt,CheckIcon as _t,ChevronDownIcon as Wt,ChevronRightIcon as $t,ClipboardCopyIcon as jt,ServerIcon as xr}from"lucide-react";import{createContext as qt,useCallback as Vt,useContext as Ve,useEffect as Jt,useMemo as Kt,useRef as Gt,useState as Ee}from"react";import{Fragment as qe,jsx as T,jsxs as K}from"react/jsx-runtime";function Xt(e,t=80){if(e==null)return String(e);if(typeof e!="object")return String(e).slice(0,t);if(Array.isArray(e))return`Array(${e.length})`;let o=JSON.stringify(e);if(o.length<=t)return o;let r=Object.entries(e),n=[],s=t-2;for(let[a,w]of r){if(s<=8)break;let m=a.length>4?`${a.slice(0,4)}\u2026`:a,c;typeof w=="string"?c=w.length>2?`'${w.slice(0,1)}\u2026`:`'${w}'`:Array.isArray(w)?c=`Array(${w.length})`:typeof w=="object"&&w!==null?c="{\u2026}":c=String(w);let l=`${m}\u2009${c}`;n.push(l),s-=l.length+3}return`{${n.join(", ")}}`}function Yt({text:e,className:t}){let[o,r]=Ee(!1),n=Gt(null);Jt(()=>()=>{n.current&&clearTimeout(n.current)},[]);let s=Vt(async a=>{a.stopPropagation();try{await navigator.clipboard.writeText(e),r(!0),n.current&&clearTimeout(n.current),n.current=setTimeout(()=>r(!1),2e3)}catch{}},[e]);return T(J,{variant:"ghost",size:"sm",onClick:s,className:i("ww:h-auto ww:gap-1 ww:px-1.5 ww:py-0.5 ww:text-xs ww:text-muted-foreground ww:hover:text-foreground",t),children:o?K(qe,{children:[T(_t,{className:"ww:size-3.5"}),T("span",{children:"Copied"})]}):K(qe,{children:[T(jt,{className:"ww:size-3.5"}),T("span",{children:"Copy"})]})})}function Je({data:e,label:t,className:o,...r}){let[n,s]=Ee(!1),a=Kt(()=>JSON.stringify(e,null,2),[e]),w=Xt(e);return K("div",{className:i("ww:rounded-lg ww:bg-tool-card",o),...r,children:[K("div",{className:"ww:flex ww:items-center ww:justify-between ww:px-3 ww:pt-2.5 ww:pb-1.5",children:[T("span",{className:"ww:text-xs ww:font-medium ww:text-muted-foreground",children:t}),T(Yt,{text:a})]}),K("button",{type:"button",onClick:()=>s(m=>!m),className:"ww:flex ww:w-full ww:items-start ww:gap-2 ww:px-3 ww:pb-3 ww:text-left",children:[T($t,{className:i("ww:mt-0.5 ww:size-3.5 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-150",n&&"ww:rotate-90")}),n?T("pre",{className:"ww:overflow-x-auto ww:text-xs ww:font-mono ww:text-foreground ww:whitespace-pre-wrap ww:break-all",children:T("code",{children:a})}):T("span",{className:"ww:truncate ww:text-xs ww:font-mono ww:text-foreground/80",children:w})]})]})}var Ie=qt({open:!1,toggle:()=>{}});function Ke({className:e,defaultOpen:t=!1,children:o,...r}){let[n,s]=Ee(t);return T(Ie.Provider,{value:{open:n,toggle:()=>s(a=>!a)},children:T("div",{className:i("ww:mb-4 ww:w-full",e),"data-state":n?"open":"closed",...r,children:o})})}function Ge({className:e,title:t,state:o,...r}){let{open:n,toggle:s}=Ve(Ie),a=o==="input-available"||o==="input-streaming";return K("button",{type:"button",onClick:s,className:i("ww:flex ww:w-full ww:items-center ww:justify-between ww:gap-3 ww:py-1.5",e),"aria-expanded":n,...r,children:[K("div",{className:"ww:flex ww:min-w-0 ww:items-center ww:gap-2",children:[T(zt,{className:"ww:size-4 ww:shrink-0 ww:text-muted-foreground"}),T("span",{className:"ww:truncate ww:text-sm ww:font-medium",children:t}),a&&T("span",{className:"ww:size-2 ww:shrink-0 ww:rounded-full ww:bg-primary ww:animate-pulse"})]}),T(Wt,{className:i("ww:size-4 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-200",n&&"ww:rotate-180")})]})}function Xe({className:e,children:t,...o}){let{open:r}=Ve(Ie);return T("div",{className:i("ww:grid ww:transition-[grid-template-rows,opacity] ww:duration-200 ww:ease-out",r?"ww:grid-rows-[1fr] ww:opacity-100":"ww:grid-rows-[0fr] ww:opacity-0"),children:T("div",{className:"ww:min-h-0 ww:overflow-hidden",children:T("div",{className:i("ww:mt-2 ww:space-y-3 ww:rounded-lg ww:border ww:border-border ww:bg-background ww:p-3",e),...o,children:t})})})}function Ye({className:e,input:t,...o}){return T(Je,{data:t,label:"Request",className:e,...o})}function Ze(e){if(typeof e!="object"||e===null)return;let t=e._meta;if(typeof t!="object"||t===null)return;let o=t.ui;if(!(typeof o!="object"||o===null))return o}function Qe(e){let t=Ze(e)?.resourceUri;return typeof t=="string"?t:void 0}function et(e){return Ze(e)?.autoHeight===!0}function tt({className:e,output:t,errorText:o,...r}){return t||o?o?K("div",{className:i("ww:space-y-2",e),...r,children:[T("h4",{className:"ww:text-xs ww:font-medium ww:uppercase ww:tracking-wide ww:text-muted-foreground",children:"Error"}),T("div",{className:"ww:rounded-lg ww:bg-destructive/10 ww:p-3 ww:text-xs ww:text-destructive",children:o})]}):T(Je,{data:t,label:"Response",className:e,...r}):null}import{useCallback as Zt,useEffect as ot,useMemo as Qt,useRef as W,useState as rt}from"react";import{jsx as ao}from"react/jsx-runtime";var eo="/api/mcp/resource",to=500,oo=0,ro="2026-01-26",no=300,so=3e3,Re=3;function Ne({resourceUri:e,toolInput:t,toolResult:o,resourceEndpoint:r=eo,isDark:n=!1,className:s,autoHeight:a=!0,onOpenLink:w,onFollowUp:m}){let c=W(null),l=W(t),p=W(o),y=W({width:0,height:0}),C=W(null),E=W(!1),P=W(0),[f,u]=rt(oo),[d,x]=rt(void 0),D=W(w),b=W(m);l.current=t,p.current=o,D.current=w,b.current=m;let v=Zt(g=>a?Math.max(g,0):Math.min(Math.max(g,50),to),[a]),M=Qt(()=>`${r}?uri=${encodeURIComponent(e)}`,[r,e]),N=W(n);return N.current=n,ot(()=>{if(!E.current)return;let g=c.current;g?.contentWindow&&g.contentWindow.postMessage({jsonrpc:"2.0",method:"ui/notifications/host-context-changed",params:{theme:n?"dark":"light"}},"*")},[n]),ot(()=>{let g=c.current;if(!g)return;let I=!1,R=!1,k=(...S)=>console.debug("[McpAppFrame]",...S);k("effect mounted, waiting for handshake");let L=setTimeout(()=>{if(I||R)return;if(P.current>=Re){k("handshake failed after",Re,"retries, giving up");return}P.current+=1,k("handshake timeout, reloading iframe (retry",P.current,"of",Re,")");let S=new URL(g.src);S.searchParams.set("_retry",String(P.current)),g.src=S.toString()},so),h=S=>{k("\u2192 send",S.method??`response:${S.id}`,S),g.contentWindow?.postMessage(S,"*")},A=S=>{if(I||S.source!==g.contentWindow)return;let B=S.data;if(!B||typeof B!="object"||B.jsonrpc!=="2.0")return;let j=B.method,F=B.id;if(k("\u2190 recv",j??`response:${F}`,B),j==="ui/initialize"&&F!=null){R=!0,clearTimeout(L),k("handshake started"),h({jsonrpc:"2.0",id:F,result:{protocolVersion:B.params?.protocolVersion??ro,hostInfo:{name:"WaniWani Chat",version:"1.0.0"},hostCapabilities:{openLinks:{},message:{}},hostContext:{theme:N.current?"dark":"light",displayMode:"inline"}}});return}if(j==="ui/notifications/initialized"){k("handshake complete, sending tool data"),E.current=!0;let z=l.current,_=p.current;h({jsonrpc:"2.0",method:"ui/notifications/tool-input",params:{arguments:z}});let V=_.content??[{type:"text",text:JSON.stringify(_)}];h({jsonrpc:"2.0",method:"ui/notifications/tool-result",params:{content:V,structuredContent:_.structuredContent}});return}if(j==="ui/notifications/size-changed"){let z=B.params,_=typeof z?.height=="number"?z.height:void 0,V=typeof z?.width=="number"?z.width:void 0,G=y.current,Ce=_!==void 0&&_!==G.height,ve=V!==void 0&&V!==G.width;if(k("size-changed",{newHeight:_,newWidth:V,lastHeight:G.height,lastWidth:G.width,heightChanged:Ce,widthChanged:ve}),!Ce&&!ve)return;if(Ce&&_!==void 0){G.height=_;let Te=v(_),De=g.getBoundingClientRect().height;if(C.current&&(C.current.cancel(),C.current=null),u(Te),g.animate&&Math.abs(De-Te)>2){let te=g.animate([{height:`${De}px`},{height:`${Te}px`}],{duration:no,easing:"ease-out",fill:"forwards"});C.current=te,te.onfinish=()=>{C.current===te&&(te.cancel(),C.current=null)}}}ve&&a&&V!==void 0&&(G.width=V,x(V));return}if(j==="ui/open-link"&&F!=null){let z=B.params?.url;typeof z=="string"&&(D.current?D.current(z):window.open(z,"_blank","noopener,noreferrer")),h({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/message"&&F!=null){b.current&&B.params&&b.current(B.params),h({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/request-display-mode"&&F!=null){h({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/resource-teardown"&&F!=null){h({jsonrpc:"2.0",id:F,result:{}});return}j==="ping"&&F!=null&&h({jsonrpc:"2.0",id:F,result:{}})};return window.addEventListener("message",A),()=>{k("effect cleanup (disposed)"),I=!0,clearTimeout(L),window.removeEventListener("message",A)}},[a,v]),ao("iframe",{ref:c,src:M,sandbox:"allow-scripts allow-forms allow-same-origin",className:i("ww:rounded-md ww:border ww:border-border",s),style:{height:f,minWidth:d?`min(${d}px, 100%)`:void 0,width:"100%",border:"none",colorScheme:"auto"},title:"MCP App"})}import{Component as io}from"react";import{jsx as nt,jsxs as wo}from"react/jsx-runtime";var de=class extends io{state={hasError:!1};static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(t){console.warn("[WaniWani] Widget failed to render:",t.message)}render(){return this.state.hasError?wo("div",{className:"ww:flex ww:items-center ww:justify-between ww:rounded-md ww:border ww:border-border ww:bg-muted/50 ww:px-4 ww:py-3 ww:text-sm ww:text-muted-foreground",children:[nt("span",{children:"Widget failed to load"}),nt("button",{type:"button",onClick:()=>this.setState({hasError:!1}),className:"ww:text-xs ww:font-medium ww:text-primary ww:hover:underline",children:"Retry"})]}):this.props.children}};import{Fragment as uo,jsx as H,jsxs as X}from"react/jsx-runtime";function lo(e){return e.replace(/[-_]/g," ").replace(/^\w/,t=>t.toUpperCase())}function me({messages:e,status:t,welcomeMessage:o,resourceEndpoint:r,isDark:n,onFollowUp:s}){let a=t==="submitted"||t==="streaming",w=e[e.length-1],m=e.length>0,c=a&&(!m||w.role==="user");return X(uo,{children:[o&&H(ue,{from:"assistant",children:H(ce,{children:H(pe,{children:o})})}),e.map(l=>{let p=l.parts.filter(u=>u.type==="text"),y=l.parts.filter(u=>u.type==="reasoning"),C=l.parts.filter(u=>u.type==="file"),E=l.parts.filter(u=>"toolCallId"in u),P=l===w&&l.role==="assistant",f=p.length>0;return X(ue,{from:l.role,children:[y.map((u,d)=>H(je,{text:u.text},`reasoning-${l.id}-${d}`)),E.map(u=>{let d="output"in u?u.output:void 0,x=d!==void 0?Qe(d):void 0,D=d!==void 0?et(d):!1;return X("div",{children:[X(Ke,{defaultOpen:u.state==="output-available",children:[H(Ge,{title:u.title??lo(u.toolName),state:u.state}),X(Xe,{children:[H(Ye,{input:u.input}),d!==void 0&&H(tt,{output:d,errorText:"errorText"in u?u.errorText:void 0})]})]}),x&&d!==void 0&&H(de,{children:H(Ne,{resourceUri:x,toolInput:u.input??{},toolResult:{content:d.content,structuredContent:d.structuredContent},resourceEndpoint:r,isDark:n,autoHeight:D,onFollowUp:s})})]},u.toolCallId)}),X(ce,{children:[C.length>0&&H(We,{files:C}),f?p.map((u,d)=>H(pe,{children:u.type==="text"?u.text:""},`${l.id}-${d}`)):P&&a&&H(ke,{})]})]},l.id)}),c&&H(ue,{from:"assistant",children:H(ce,{children:H(ke,{})})})]})}import{jsx as Le}from"react/jsx-runtime";function fe({suggestions:e,isLoading:t,onSelect:o,className:r,...n}){return e.length===0&&!t?null:Le("div",{className:i("ww:flex ww:flex-wrap ww:gap-2 ww:px-3 ww:py-2",r),...n,children:t?[0,1,2].map(s=>Le("div",{className:"ww:h-7 ww:rounded-full ww:bg-accent ww:animate-pulse",style:{width:`${60+s*20}px`}},s)):e.map((s,a)=>Le("button",{type:"button",onClick:()=>o(s),className:i("ww:rounded-full ww:border ww:border-border ww:bg-background ww:px-3 ww:py-1 ww:text-xs","ww:text-foreground ww:hover:bg-accent ww:hover:border-primary/30","ww:transition-all ww:duration-200 ww:ease-out ww:cursor-pointer","ww:animate-[ww-fade-in_0.2s_ease-out_both]"),style:{animationDelay:`${a*50}ms`},children:s},s))})}import{useChat as co}from"@ai-sdk/react";import{DefaultChatTransport as po}from"ai";import{useCallback as st,useRef as mo,useState as fo}from"react";function ge(e){let{api:t="https://app.waniwani.ai/api/chat",headers:o,body:r,onMessageSent:n,onResponseReceived:s}=e,a=mo(new po({api:t,headers:{...o},body:r})),{messages:w,sendMessage:m,status:c}=co({transport:a.current,onFinish(){s?.()},onError(d){console.warn("[WaniWani] Chat error:",d.message)}}),[l,p]=fo(""),y=st(d=>{let x=!!d.text?.trim(),D=!!d.files?.length;(x||D)&&(m({text:d.text||"",files:d.files}),n?.(d.text||""),p(""))},[m,n]),C=st(d=>{p(d.target.value)},[]),E=c==="submitted"||c==="streaming",P=w[w.length-1],f=w.length>0,u=E&&(!f||P.role==="user");return{messages:w,status:c,text:l,setText:p,handleSubmit:y,handleTextChange:C,isLoading:E,showLoaderBubble:u,lastMessage:P,hasMessages:f,sendMessage:m}}import{useCallback as go,useEffect as at,useRef as ho,useState as bo}from"react";function xo(e){for(let t of e.parts){let o=t;if(o.type==="data"||o.type==="data-suggestions"){let r=o.data;if(r&&Array.isArray(r.suggestions))return r.suggestions}}return null}function it(e){return typeof e=="object"&&e!==null&&"initial"in e}function he(e){let{messages:t,status:o,config:r}=e,[n,s]=bo((it(r)&&r.initial?r.initial:[])??[]),a=ho(o),w=r===!0||it(r)&&r.dynamic!==!1,m=go(()=>{s([])},[]),c=t[t.length-1];return at(()=>{c?.role==="user"&&m()},[c,m]),at(()=>{let l=a.current;if(a.current=o,l==="streaming"&&o==="ready"&&w){let p=[...t].reverse().find(C=>C.role==="assistant");if(!p)return;console.log("[WaniWani] Assistant parts:",p.parts);let y=xo(p);console.log("[WaniWani] Extracted suggestions:",y),y&&s(y)}},[o,w,t]),{suggestions:n,isLoading:!1,clear:m}}import{useEffect as yo,useRef as Co,useState as vo}from"react";var To=50,Po=30,Mo=2e3,wt=500;function be(e,t=!0){let[o,r]=vo(""),n=Co(void 0);return yo(()=>{if(!t){r("");return}let s=0,a=!1,w=!1,m=()=>{w||(a?(s--,r(e.slice(0,s)),s<=0?(a=!1,n.current=setTimeout(m,wt)):n.current=setTimeout(m,Po)):(s++,r(e.slice(0,s)),s>=e.length?(a=!0,n.current=setTimeout(m,Mo)):n.current=setTimeout(m,To)))};return n.current=setTimeout(m,wt),()=>{w=!0,clearTimeout(n.current)}},[e,t]),o}var lt={primaryColor:"#6366f1",primaryForeground:"#1f2937",backgroundColor:"#ffffff",textColor:"#1f2937",mutedColor:"#6b7280",borderColor:"#e5e7eb",assistantBubbleColor:"#f3f4f6",userBubbleColor:"#f4f4f4",inputBackgroundColor:"#f9fafb",borderRadius:16,messageBorderRadius:12,fontFamily:"system-ui, -apple-system, 'Segoe UI', sans-serif",headerBackgroundColor:"#ffffff",headerTextColor:"#1f2937",statusColor:"#22c55e",toolCardColor:"#f4f4f5"},ko={backgroundColor:"#212121",headerBackgroundColor:"#1e1e1e",headerTextColor:"#ececec",textColor:"#ececec",primaryForeground:"#ffffff",mutedColor:"#8e8ea0",borderColor:"#444444",assistantBubbleColor:"#2f2f2f",userBubbleColor:"#303030",inputBackgroundColor:"#2f2f2f",primaryColor:"#6366f1",statusColor:"#22c55e",toolCardColor:"#262626"},So={primaryColor:["--ww-primary","--ww-color-primary"],primaryForeground:["--ww-primary-fg","--ww-color-primary-foreground"],backgroundColor:["--ww-bg","--ww-color-background"],textColor:["--ww-text","--ww-color-foreground","--ww-color-accent-foreground"],mutedColor:["--ww-muted","--ww-color-muted-foreground"],borderColor:["--ww-border","--ww-color-border"],assistantBubbleColor:["--ww-assistant-bubble","--ww-color-accent"],userBubbleColor:["--ww-user-bubble"],inputBackgroundColor:["--ww-input-bg","--ww-color-input"],borderRadius:["--ww-radius"],messageBorderRadius:["--ww-msg-radius"],fontFamily:["--ww-font"],headerBackgroundColor:["--ww-header-bg"],headerTextColor:["--ww-header-text"],statusColor:["--ww-status"],toolCardColor:["--ww-tool-card","--ww-color-tool-card"]};function Z(e){return{...lt,...e}}function xe(e){let t=e.backgroundColor.replace("#",""),o=parseInt(t.substring(0,2),16),r=parseInt(t.substring(2,4),16),n=parseInt(t.substring(4,6),16);return(o*299+r*587+n*114)/1e3<128}function Q(e){let t={};for(let[o,r]of Object.entries(So)){let n=e[o],s=typeof n=="number"?`${n}px`:String(n);for(let a of r)t[a]=s}return t}import{jsx as $,jsxs as Ae}from"react/jsx-runtime";var He=Eo(function(t,o){let{theme:r,width:n=600,expandedHeight:s=400,allowAttachments:a=!1,welcomeMessage:w,placeholder:m="Ask me anything...",triggerEvent:c="triggerDemoRequest",resourceEndpoint:l,api:p}=t,y=l??(p?`${p}/resource`:void 0),C=Z(r),E=Q(C),P=xe(C),f=ge(t),u=he({messages:f.messages,status:f.status,config:t.suggestions}),d=ye(h=>{let A=h.content.map(S=>S.text??"").join("").trim();A&&f.handleSubmit({text:A,files:[]})},[f.handleSubmit]),x=ye(h=>{u.clear(),f.handleSubmit({text:h,files:[]})},[u.clear,f.handleSubmit]),D=be(m,!f.text),[b,v]=pt(!1),[M,N]=pt(!1),g=ct(null),I=ct(void 0),R=ye(()=>{let h=g.current;if(!h)return;h.scrollIntoView({behavior:"smooth",block:"center"});let A=h.querySelector("textarea");A&&setTimeout(()=>A.focus(),300),v(!0),N(!0),clearTimeout(I.current),I.current=setTimeout(()=>N(!1),2e3)},[]);Io(o,()=>({sendMessage:h=>{f.handleSubmit({text:h,files:[]}),R()},focus:R}),[f.handleSubmit,R]),ut(()=>{if(!c)return;let h=A=>{let S=A.detail,B=typeof S?.message=="string"?S.message:void 0;B&&f.handleSubmit({text:B,files:[]}),R()};return window.addEventListener(c,h),()=>window.removeEventListener(c,h)},[c,f.handleSubmit,R]);let k=b;ut(()=>{if(!b)return;let h=A=>{g.current&&!g.current.contains(A.target)&&v(!1)};return document.addEventListener("mousedown",h),()=>document.removeEventListener("mousedown",h)},[b]);let L=ye(()=>{v(!0)},[]);return Ae("div",{ref:g,style:{...E,width:n},"data-waniwani-chat":"","data-waniwani-layout":"bar",...P?{"data-waniwani-dark":""}:{},className:"ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground",children:[$("div",{className:i("ww:overflow-hidden ww:bg-background/80 ww:backdrop-blur-xl ww:transition-all ww:duration-300 ww:ease-out",k?"ww:opacity-100 ww:translate-y-0":"ww:opacity-0 ww:translate-y-2 ww:pointer-events-none ww:max-h-0"),style:{...k?{maxHeight:s}:void 0,maskImage:"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)",maskComposite:"intersect",WebkitMaskImage:"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)",WebkitMaskComposite:"source-in"},children:Ae(re,{className:"ww:flex-1",style:{height:s},children:[$(ne,{children:$(me,{messages:f.messages,status:f.status,welcomeMessage:w,resourceEndpoint:y,isDark:P,onFollowUp:d})}),$(se,{})]})}),$(fe,{suggestions:u.suggestions,isLoading:u.isLoading,onSelect:x}),$("div",{className:"ww:shrink-0",children:$(ae,{onSubmit:f.handleSubmit,globalDrop:a,multiple:a,className:i("ww:rounded-[var(--ww-radius)] ww:shadow-sm ww:transition-all ww:duration-300 ww:ease-out",M&&"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background"),children:Ae("div",{className:"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2",children:[a&&$(le,{}),$(ie,{onChange:f.handleTextChange,value:f.text,placeholder:D,onFocus:L,className:"ww:min-h-0 ww:py-1.5 ww:px-2"}),$(we,{status:f.status})]})})})]})});import{forwardRef as Ro,useCallback as Be,useEffect as No,useImperativeHandle as Lo,useRef as dt,useState as Ao}from"react";import{jsx as U,jsxs as ee}from"react/jsx-runtime";var Ho=Ro(function(t,o){let{theme:r,title:n="Assistant",subtitle:s,showStatus:a=!0,width:w=500,height:m=600,allowAttachments:c=!1,welcomeMessage:l,placeholder:p="Ask me anything...",triggerEvent:y="triggerDemoRequest",resourceEndpoint:C,api:E}=t,P=C??(E?`${E}/resource`:void 0),f=Z(r),u=Q(f),d=xe(f),x=ge(t),D=be(p,!x.text),[b,v]=Ao(!1),M=dt(null),N=dt(void 0),g=Be(()=>{let L=M.current;if(!L)return;L.scrollIntoView({behavior:"smooth",block:"center"});let h=L.querySelector("textarea");h&&setTimeout(()=>h.focus(),300),v(!0),clearTimeout(N.current),N.current=setTimeout(()=>v(!1),2e3)},[]),I=he({messages:x.messages,status:x.status,config:t.suggestions}),R=Be(L=>{let h=L.content.map(A=>A.text??"").join("").trim();h&&x.handleSubmit({text:h,files:[]})},[x.handleSubmit]),k=Be(L=>{I.clear(),x.handleSubmit({text:L,files:[]})},[I.clear,x.handleSubmit]);return Lo(o,()=>({sendMessage:L=>{x.handleSubmit({text:L,files:[]}),g()},focus:g}),[x.handleSubmit,g]),No(()=>{if(!y)return;let L=h=>{let A=h.detail,S=typeof A?.message=="string"?A.message:void 0;S&&x.handleSubmit({text:S,files:[]}),g()};return window.addEventListener(y,L),()=>window.removeEventListener(y,L)},[y,x.handleSubmit,g]),ee("div",{ref:M,style:{...u,width:w,height:m},"data-waniwani-chat":"","data-waniwani-layout":"card",...d?{"data-waniwani-dark":""}:{},className:i("ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground ww:bg-background ww:rounded-[var(--ww-radius)] ww:border ww:border-border ww:shadow-md ww:overflow-hidden ww:transition-shadow ww:duration-300",b&&"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background"),children:[ee("div",{className:"ww:shrink-0 ww:flex ww:items-center ww:gap-3 ww:px-4 ww:py-2 ww:border-b ww:border-border",style:{backgroundColor:f.headerBackgroundColor,color:f.headerTextColor},children:[a&&U("span",{className:"ww:size-2.5 ww:rounded-full ww:bg-status"}),ee("div",{className:"ww:flex-1 ww:min-w-0",children:[U("div",{className:"ww:text-xs ww:font-semibold ww:truncate",children:n}),s&&U("div",{className:"ww:text-[11px] ww:text-muted-foreground ww:truncate",children:s})]})]}),ee(re,{className:"ww:flex-1 ww:min-h-0 ww:bg-background",children:[U(ne,{children:U(me,{messages:x.messages,status:x.status,welcomeMessage:l,resourceEndpoint:P,isDark:d,onFollowUp:R})}),U(se,{})]}),U(fe,{suggestions:I.suggestions,isLoading:I.isLoading,onSelect:k,className:"ww:border-t ww:border-border"}),U("div",{className:"ww:shrink-0 ww:border-t ww:border-border ww:bg-background",children:U(ae,{onSubmit:x.handleSubmit,globalDrop:c,multiple:c,className:i("ww:rounded-none ww:border-0"),children:ee("div",{className:"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2",children:[c&&U(le,{}),U(ie,{onChange:x.handleTextChange,value:x.text,placeholder:D,className:"ww:min-h-0 ww:py-1.5 ww:px-2"}),U(we,{status:x.status})]})})})]})});export{He as ChatBar,Ho as ChatCard,He as ChatWidget,ko as DARK_THEME,lt as DEFAULT_THEME,Ne as McpAppFrame,Z as mergeTheme,Q as themeToCSSProperties};
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/chat/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/chat/web/layouts/chat-bar.tsx","../../src/chat/web/ai-elements/conversation.tsx","../../src/chat/web/lib/utils.ts","../../src/chat/web/ui/button.tsx","../../src/chat/web/ai-elements/prompt-input.tsx","../../src/chat/web/ai-elements/attachments.tsx","../../src/chat/web/ai-elements/loader.tsx","../../src/chat/web/ai-elements/message.tsx","../../src/chat/web/ai-elements/reasoning.tsx","../../src/chat/web/ai-elements/tool.tsx","../../src/chat/web/components/mcp-app-frame.tsx","../../src/chat/web/components/widget-error-boundary.tsx","../../src/chat/web/components/message-list.tsx","../../src/chat/web/components/suggestions.tsx","../../src/chat/web/hooks/use-chat-engine.ts","../../src/chat/web/hooks/use-suggestions.ts","../../src/chat/web/hooks/use-typing-placeholder.ts","../../src/chat/web/theme.ts","../../src/chat/web/layouts/chat-card.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tforwardRef,\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport type { ChatBarProps, ChatHandle } from \"../@types\";\nimport {\n\tConversation,\n\tConversationContent,\n\tConversationScrollButton,\n} from \"../ai-elements/conversation\";\nimport {\n\tPromptInput,\n\tPromptInputAddAttachments,\n\tPromptInputSubmit,\n\tPromptInputTextarea,\n} from \"../ai-elements/prompt-input\";\nimport { MessageList } from \"../components/message-list\";\nimport { Suggestions } from \"../components/suggestions\";\nimport { useChatEngine } from \"../hooks/use-chat-engine\";\nimport { useSuggestions } from \"../hooks/use-suggestions\";\nimport { useTypingPlaceholder } from \"../hooks/use-typing-placeholder\";\nimport { cn } from \"../lib/utils\";\nimport { isDarkTheme, mergeTheme, themeToCSSProperties } from \"../theme\";\n\nexport const ChatBar = forwardRef<ChatHandle, ChatBarProps>(\n\tfunction ChatBar(props, ref) {\n\t\tconst {\n\t\t\ttheme: userTheme,\n\t\t\twidth = 600,\n\t\t\texpandedHeight = 400,\n\t\t\tallowAttachments = false,\n\t\t\twelcomeMessage,\n\t\t\tplaceholder = \"Ask me anything...\",\n\t\t\ttriggerEvent = \"triggerDemoRequest\",\n\t\t\tresourceEndpoint,\n\t\t\tapi,\n\t\t} = props;\n\n\t\tconst effectiveResourceEndpoint =\n\t\t\tresourceEndpoint ?? (api ? `${api}/resource` : undefined);\n\n\t\tconst resolvedTheme = mergeTheme(userTheme);\n\t\tconst cssVars = themeToCSSProperties(resolvedTheme);\n\t\tconst isDark = isDarkTheme(resolvedTheme);\n\n\t\tconst engine = useChatEngine(props);\n\n\t\tconst suggestionsState = useSuggestions({\n\t\t\tmessages: engine.messages,\n\t\t\tstatus: engine.status,\n\t\t\tconfig: props.suggestions,\n\t\t});\n\n\t\tconst handleWidgetMessage = useCallback(\n\t\t\t(message: {\n\t\t\t\trole: string;\n\t\t\t\tcontent: Array<{ type: string; text?: string }>;\n\t\t\t}) => {\n\t\t\t\tconst text = message.content\n\t\t\t\t\t.map((c) => c.text ?? \"\")\n\t\t\t\t\t.join(\"\")\n\t\t\t\t\t.trim();\n\t\t\t\tif (text) engine.handleSubmit({ text, files: [] });\n\t\t\t},\n\t\t\t[engine.handleSubmit],\n\t\t);\n\n\t\tconst handleSuggestionSelect = useCallback(\n\t\t\t(suggestion: string) => {\n\t\t\t\tsuggestionsState.clear();\n\t\t\t\tengine.handleSubmit({ text: suggestion, files: [] });\n\t\t\t},\n\t\t\t[suggestionsState.clear, engine.handleSubmit],\n\t\t);\n\n\t\tconst animatedPlaceholder = useTypingPlaceholder(placeholder, !engine.text);\n\n\t\tconst [isFocused, setIsFocused] = useState(false);\n\t\tconst [isHighlighted, setIsHighlighted] = useState(false);\n\t\tconst containerRef = useRef<HTMLDivElement>(null);\n\t\tconst highlightTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\t\tconst focusInput = useCallback(() => {\n\t\t\tconst container = containerRef.current;\n\t\t\tif (!container) return;\n\t\t\tcontainer.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\t\tconst textarea = container.querySelector(\"textarea\");\n\t\t\tif (textarea) {\n\t\t\t\tsetTimeout(() => textarea.focus(), 300);\n\t\t\t}\n\t\t\tsetIsFocused(true);\n\t\t\tsetIsHighlighted(true);\n\t\t\tclearTimeout(highlightTimerRef.current);\n\t\t\thighlightTimerRef.current = setTimeout(\n\t\t\t\t() => setIsHighlighted(false),\n\t\t\t\t2000,\n\t\t\t);\n\t\t}, []);\n\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tsendMessage: (text: string) => {\n\t\t\t\t\tengine.handleSubmit({ text, files: [] });\n\t\t\t\t\tfocusInput();\n\t\t\t\t},\n\t\t\t\tfocus: focusInput,\n\t\t\t}),\n\t\t\t[engine.handleSubmit, focusInput],\n\t\t);\n\n\t\t// Listen for custom trigger event (e.g. \"triggerDemoRequest\")\n\t\tuseEffect(() => {\n\t\t\tif (!triggerEvent) return;\n\t\t\tconst handler = (e: Event) => {\n\t\t\t\tconst detail = (e as CustomEvent).detail;\n\t\t\t\tconst message =\n\t\t\t\t\ttypeof detail?.message === \"string\" ? detail.message : undefined;\n\t\t\t\tif (message) {\n\t\t\t\t\tengine.handleSubmit({ text: message, files: [] });\n\t\t\t\t}\n\t\t\t\tfocusInput();\n\t\t\t};\n\t\t\twindow.addEventListener(triggerEvent, handler);\n\t\t\treturn () => window.removeEventListener(triggerEvent, handler);\n\t\t}, [triggerEvent, engine.handleSubmit, focusInput]);\n\t\tconst isExpanded = isFocused;\n\n\t\t// Close on outside click\n\t\tuseEffect(() => {\n\t\t\tif (!isFocused) return;\n\t\t\tconst handleClickOutside = (e: MouseEvent) => {\n\t\t\t\tif (\n\t\t\t\t\tcontainerRef.current &&\n\t\t\t\t\t!containerRef.current.contains(e.target as Node)\n\t\t\t\t) {\n\t\t\t\t\tsetIsFocused(false);\n\t\t\t\t}\n\t\t\t};\n\t\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\t\treturn () =>\n\t\t\t\tdocument.removeEventListener(\"mousedown\", handleClickOutside);\n\t\t}, [isFocused]);\n\n\t\tconst handleFocus = useCallback(() => {\n\t\t\tsetIsFocused(true);\n\t\t}, []);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\tstyle={{ ...cssVars, width }}\n\t\t\t\tdata-waniwani-chat=\"\"\n\t\t\t\tdata-waniwani-layout=\"bar\"\n\t\t\t\t{...(isDark ? { \"data-waniwani-dark\": \"\" } : {})}\n\t\t\t\tclassName=\"ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground\"\n\t\t\t>\n\t\t\t\t{/* Messages panel — fades up on expand */}\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:overflow-hidden ww:bg-background/80 ww:backdrop-blur-xl ww:transition-all ww:duration-300 ww:ease-out\",\n\t\t\t\t\t\tisExpanded\n\t\t\t\t\t\t\t? \"ww:opacity-100 ww:translate-y-0\"\n\t\t\t\t\t\t\t: \"ww:opacity-0 ww:translate-y-2 ww:pointer-events-none ww:max-h-0\",\n\t\t\t\t\t)}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\t...(isExpanded ? { maxHeight: expandedHeight } : undefined),\n\t\t\t\t\t\tmaskImage:\n\t\t\t\t\t\t\t\"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)\",\n\t\t\t\t\t\tmaskComposite: \"intersect\",\n\t\t\t\t\t\tWebkitMaskImage:\n\t\t\t\t\t\t\t\"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)\",\n\t\t\t\t\t\tWebkitMaskComposite: \"source-in\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<Conversation\n\t\t\t\t\t\tclassName=\"ww:flex-1\"\n\t\t\t\t\t\tstyle={{ height: expandedHeight }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ConversationContent>\n\t\t\t\t\t\t\t<MessageList\n\t\t\t\t\t\t\t\tmessages={engine.messages}\n\t\t\t\t\t\t\t\tstatus={engine.status}\n\t\t\t\t\t\t\t\twelcomeMessage={welcomeMessage}\n\t\t\t\t\t\t\t\tresourceEndpoint={effectiveResourceEndpoint}\n\t\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\t\tonFollowUp={handleWidgetMessage}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</ConversationContent>\n\t\t\t\t\t\t<ConversationScrollButton />\n\t\t\t\t\t</Conversation>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Suggestions */}\n\t\t\t\t<Suggestions\n\t\t\t\t\tsuggestions={suggestionsState.suggestions}\n\t\t\t\t\tisLoading={suggestionsState.isLoading}\n\t\t\t\t\tonSelect={handleSuggestionSelect}\n\t\t\t\t/>\n\n\t\t\t\t{/* Input bar — always visible */}\n\t\t\t\t<div className=\"ww:shrink-0\">\n\t\t\t\t\t<PromptInput\n\t\t\t\t\t\tonSubmit={engine.handleSubmit}\n\t\t\t\t\t\tglobalDrop={allowAttachments}\n\t\t\t\t\t\tmultiple={allowAttachments}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"ww:rounded-[var(--ww-radius)] ww:shadow-sm ww:transition-all ww:duration-300 ww:ease-out\",\n\t\t\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\t\t\"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2\">\n\t\t\t\t\t\t\t{allowAttachments && <PromptInputAddAttachments />}\n\t\t\t\t\t\t\t<PromptInputTextarea\n\t\t\t\t\t\t\t\tonChange={engine.handleTextChange}\n\t\t\t\t\t\t\t\tvalue={engine.text}\n\t\t\t\t\t\t\t\tplaceholder={animatedPlaceholder}\n\t\t\t\t\t\t\t\tonFocus={handleFocus}\n\t\t\t\t\t\t\t\tclassName=\"ww:min-h-0 ww:py-1.5 ww:px-2\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<PromptInputSubmit status={engine.status} />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</PromptInput>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t},\n);\n","\"use client\";\n\nimport { ArrowDownIcon } from \"lucide-react\";\nimport type { ComponentProps } from \"react\";\nimport { useCallback } from \"react\";\nimport { StickToBottom, useStickToBottomContext } from \"use-stick-to-bottom\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\nexport type ConversationProps = ComponentProps<typeof StickToBottom>;\n\nexport const Conversation = ({ className, ...props }: ConversationProps) => (\n\t<StickToBottom\n\t\tclassName={cn(\"ww:relative ww:flex-1 ww:overflow-y-hidden\", className)}\n\t\tinitial=\"smooth\"\n\t\tresize=\"smooth\"\n\t\trole=\"log\"\n\t\t{...props}\n\t/>\n);\n\nexport type ConversationContentProps = ComponentProps<\n\ttypeof StickToBottom.Content\n>;\n\nexport const ConversationContent = ({\n\tclassName,\n\t...props\n}: ConversationContentProps) => (\n\t<StickToBottom.Content\n\t\tclassName={cn(\"ww:flex ww:flex-col ww:gap-8 ww:p-4\", className)}\n\t\t{...props}\n\t/>\n);\n\nexport type ConversationScrollButtonProps = ComponentProps<typeof Button>;\n\nexport const ConversationScrollButton = ({\n\tclassName,\n\t...props\n}: ConversationScrollButtonProps) => {\n\tconst { isAtBottom, scrollToBottom } = useStickToBottomContext();\n\n\tconst handleScrollToBottom = useCallback(() => {\n\t\tscrollToBottom();\n\t}, [scrollToBottom]);\n\n\treturn (\n\t\t!isAtBottom && (\n\t\t\t<Button\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:absolute ww:bottom-4 ww:left-[50%] ww:translate-x-[-50%] ww:rounded-full\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonClick={handleScrollToBottom}\n\t\t\t\tsize=\"icon\"\n\t\t\t\tvariant=\"outline\"\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<ArrowDownIcon className=\"ww:size-4\" />\n\t\t\t</Button>\n\t\t)\n\t);\n};\n","import { type ClassValue, clsx } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\nconst twMerge = extendTailwindMerge({ prefix: \"ww\" });\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type ButtonProps = ComponentProps<\"button\"> & {\n\tvariant?: \"default\" | \"outline\" | \"ghost\";\n\tsize?: \"default\" | \"sm\" | \"icon\" | \"icon-sm\";\n};\n\nexport const Button = ({\n\tclassName,\n\tvariant = \"default\",\n\tsize = \"default\",\n\ttype = \"button\",\n\t...props\n}: ButtonProps) => (\n\t<button\n\t\ttype={type}\n\t\tclassName={cn(\n\t\t\t\"ww:inline-flex ww:cursor-pointer ww:items-center ww:justify-center ww:rounded-md ww:font-medium ww:transition-colors ww:disabled:pointer-events-none ww:disabled:opacity-50\",\n\t\t\tvariant === \"default\" &&\n\t\t\t\t\"ww:bg-primary ww:text-primary-foreground ww:hover:bg-primary/90\",\n\t\t\tvariant === \"outline\" &&\n\t\t\t\t\"ww:border ww:border-border ww:bg-background ww:hover:bg-accent ww:hover:text-accent-foreground\",\n\t\t\tvariant === \"ghost\" &&\n\t\t\t\t\"ww:hover:bg-accent ww:hover:text-accent-foreground\",\n\t\t\tsize === \"default\" && \"ww:h-9 ww:px-4 ww:py-2 ww:text-sm\",\n\t\t\tsize === \"sm\" && \"ww:h-8 ww:px-3 ww:text-xs\",\n\t\t\tsize === \"icon\" && \"ww:size-9\",\n\t\t\tsize === \"icon-sm\" && \"ww:size-7\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n","\"use client\";\n\nimport type { ChatStatus, FileUIPart } from \"ai\";\nimport {\n\tArrowUpIcon,\n\tLoaderIcon,\n\tPaperclipIcon,\n\tSquareIcon,\n\tXIcon,\n} from \"lucide-react\";\nimport { nanoid } from \"nanoid\";\nimport type {\n\tChangeEvent,\n\tClipboardEventHandler,\n\tComponentProps,\n\tFormEvent,\n\tFormEventHandler,\n\tHTMLAttributes,\n\tKeyboardEventHandler,\n} from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst convertBlobUrlToDataUrl = async (url: string): Promise<string | null> => {\n\ttry {\n\t\tconst response = await fetch(url);\n\t\tconst blob = await response.blob();\n\t\treturn new Promise((resolve) => {\n\t\t\tconst reader = new FileReader();\n\t\t\treader.onloadend = () => resolve(reader.result as string);\n\t\t\treader.onerror = () => resolve(null);\n\t\t\treader.readAsDataURL(blob);\n\t\t});\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// ============================================================================\n// Attachments Context\n// ============================================================================\n\nexport interface AttachmentsContext {\n\tfiles: (FileUIPart & { id: string })[];\n\tadd: (files: File[] | FileList) => void;\n\tremove: (id: string) => void;\n\tclear: () => void;\n\topenFileDialog: () => void;\n}\n\nconst LocalAttachmentsContext = createContext<AttachmentsContext | null>(null);\n\nexport const usePromptInputAttachments = () => {\n\tconst context = useContext(LocalAttachmentsContext);\n\tif (!context) {\n\t\tthrow new Error(\n\t\t\t\"usePromptInputAttachments must be used within a PromptInput\",\n\t\t);\n\t}\n\treturn context;\n};\n\n// ============================================================================\n// PromptInput Message Type\n// ============================================================================\n\nexport interface PromptInputMessage {\n\ttext: string;\n\tfiles: FileUIPart[];\n}\n\n// ============================================================================\n// PromptInput\n// ============================================================================\n\nexport type PromptInputProps = Omit<\n\tHTMLAttributes<HTMLFormElement>,\n\t\"onSubmit\"\n> & {\n\taccept?: string;\n\tmultiple?: boolean;\n\tglobalDrop?: boolean;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tonSubmit: (\n\t\tmessage: PromptInputMessage,\n\t\tevent: FormEvent<HTMLFormElement>,\n\t) => void | Promise<void>;\n};\n\nexport const PromptInput = ({\n\tclassName,\n\taccept,\n\tmultiple,\n\tglobalDrop,\n\tmaxFiles,\n\tmaxFileSize,\n\tonSubmit,\n\tchildren,\n\t...props\n}: PromptInputProps) => {\n\tconst inputRef = useRef<HTMLInputElement | null>(null);\n\tconst formRef = useRef<HTMLFormElement | null>(null);\n\tconst [items, setItems] = useState<(FileUIPart & { id: string })[]>([]);\n\tconst filesRef = useRef(items);\n\n\tuseEffect(() => {\n\t\tfilesRef.current = items;\n\t}, [items]);\n\n\tconst openFileDialog = useCallback(() => {\n\t\tinputRef.current?.click();\n\t}, []);\n\n\tconst add = useCallback(\n\t\t(fileList: File[] | FileList) => {\n\t\t\tconst incoming = [...fileList];\n\t\t\tif (incoming.length === 0) return;\n\n\t\t\tconst withinSize = (f: File) =>\n\t\t\t\tmaxFileSize ? f.size <= maxFileSize : true;\n\t\t\tconst valid = incoming.filter(withinSize);\n\n\t\t\tsetItems((prev) => {\n\t\t\t\tconst capacity =\n\t\t\t\t\ttypeof maxFiles === \"number\"\n\t\t\t\t\t\t? Math.max(0, maxFiles - prev.length)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst capped =\n\t\t\t\t\ttypeof capacity === \"number\" ? valid.slice(0, capacity) : valid;\n\t\t\t\treturn [\n\t\t\t\t\t...prev,\n\t\t\t\t\t...capped.map((file) => ({\n\t\t\t\t\t\tfilename: file.name,\n\t\t\t\t\t\tid: nanoid(),\n\t\t\t\t\t\tmediaType: file.type,\n\t\t\t\t\t\ttype: \"file\" as const,\n\t\t\t\t\t\turl: URL.createObjectURL(file),\n\t\t\t\t\t})),\n\t\t\t\t];\n\t\t\t});\n\t\t},\n\t\t[maxFiles, maxFileSize],\n\t);\n\n\tconst remove = useCallback((id: string) => {\n\t\tsetItems((prev) => {\n\t\t\tconst found = prev.find((f) => f.id === id);\n\t\t\tif (found?.url) URL.revokeObjectURL(found.url);\n\t\t\treturn prev.filter((f) => f.id !== id);\n\t\t});\n\t}, []);\n\n\tconst clear = useCallback(() => {\n\t\tsetItems((prev) => {\n\t\t\tfor (const f of prev) {\n\t\t\t\tif (f.url) URL.revokeObjectURL(f.url);\n\t\t\t}\n\t\t\treturn [];\n\t\t});\n\t}, []);\n\n\t// Cleanup blob URLs on unmount\n\tuseEffect(\n\t\t() => () => {\n\t\t\tfor (const f of filesRef.current) {\n\t\t\t\tif (f.url) URL.revokeObjectURL(f.url);\n\t\t\t}\n\t\t},\n\t\t[],\n\t);\n\n\tconst handleChange = useCallback(\n\t\t(event: ChangeEvent<HTMLInputElement>) => {\n\t\t\tif (event.currentTarget.files) {\n\t\t\t\tadd(event.currentTarget.files);\n\t\t\t}\n\t\t\tevent.currentTarget.value = \"\";\n\t\t},\n\t\t[add],\n\t);\n\n\t// Global drop support\n\tuseEffect(() => {\n\t\tif (!globalDrop) return;\n\t\tconst onDragOver = (e: DragEvent) => {\n\t\t\tif (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n\t\t};\n\t\tconst onDrop = (e: DragEvent) => {\n\t\t\tif (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n\t\t\tif (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {\n\t\t\t\tadd(e.dataTransfer.files);\n\t\t\t}\n\t\t};\n\t\tdocument.addEventListener(\"dragover\", onDragOver);\n\t\tdocument.addEventListener(\"drop\", onDrop);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"dragover\", onDragOver);\n\t\t\tdocument.removeEventListener(\"drop\", onDrop);\n\t\t};\n\t}, [add, globalDrop]);\n\n\tconst handleSubmit: FormEventHandler<HTMLFormElement> = useCallback(\n\t\tasync (event) => {\n\t\t\tevent.preventDefault();\n\t\t\tconst form = event.currentTarget;\n\t\t\tconst formData = new FormData(form);\n\t\t\tconst text = (formData.get(\"message\") as string) || \"\";\n\n\t\t\tform.reset();\n\n\t\t\tconst convertedFiles: FileUIPart[] = await Promise.all(\n\t\t\t\titems.map(async ({ id: _id, ...item }) => {\n\t\t\t\t\tif (item.url?.startsWith(\"blob:\")) {\n\t\t\t\t\t\tconst dataUrl = await convertBlobUrlToDataUrl(item.url);\n\t\t\t\t\t\treturn { ...item, url: dataUrl ?? item.url };\n\t\t\t\t\t}\n\t\t\t\t\treturn item;\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\ttry {\n\t\t\t\tconst result = onSubmit({ files: convertedFiles, text }, event);\n\t\t\t\tif (result instanceof Promise) {\n\t\t\t\t\tawait result;\n\t\t\t\t}\n\t\t\t\tclear();\n\t\t\t} catch {\n\t\t\t\t// Don't clear on error\n\t\t\t}\n\t\t},\n\t\t[items, onSubmit, clear],\n\t);\n\n\tconst attachmentsCtx = useMemo<AttachmentsContext>(\n\t\t() => ({\n\t\t\tadd,\n\t\t\tclear,\n\t\t\tfiles: items,\n\t\t\topenFileDialog,\n\t\t\tremove,\n\t\t}),\n\t\t[items, add, remove, clear, openFileDialog],\n\t);\n\n\treturn (\n\t\t<LocalAttachmentsContext.Provider value={attachmentsCtx}>\n\t\t\t<input\n\t\t\t\taccept={accept}\n\t\t\t\taria-label=\"Upload files\"\n\t\t\t\tclassName=\"ww:hidden\"\n\t\t\t\tmultiple={multiple}\n\t\t\t\tonChange={handleChange}\n\t\t\t\tref={inputRef}\n\t\t\t\ttitle=\"Upload files\"\n\t\t\t\ttype=\"file\"\n\t\t\t/>\n\t\t\t<form\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:flex ww:w-full ww:flex-col ww:rounded-lg ww:border ww:border-border ww:bg-background\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\tref={formRef}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</form>\n\t\t</LocalAttachmentsContext.Provider>\n\t);\n};\n\n// ============================================================================\n// Layout Components\n// ============================================================================\n\nexport type PromptInputHeaderProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputHeader = ({\n\tclassName,\n\t...props\n}: PromptInputHeaderProps) => (\n\t<div\n\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-1 ww:px-3 ww:pt-3\", className)}\n\t\t{...props}\n\t/>\n);\n\nexport type PromptInputBodyProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputBody = ({\n\tclassName,\n\t...props\n}: PromptInputBodyProps) => (\n\t<div className={cn(\"ww:contents\", className)} {...props} />\n);\n\nexport type PromptInputFooterProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputFooter = ({\n\tclassName,\n\t...props\n}: PromptInputFooterProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:flex ww:items-center ww:justify-between ww:gap-1 ww:px-3 ww:pb-3\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputTools = ({\n\tclassName,\n\t...props\n}: PromptInputToolsProps) => (\n\t<div\n\t\tclassName={cn(\"ww:flex ww:min-w-0 ww:items-center ww:gap-1\", className)}\n\t\t{...props}\n\t/>\n);\n\n// ============================================================================\n// Textarea\n// ============================================================================\n\nexport type PromptInputTextareaProps = ComponentProps<\"textarea\">;\n\nexport const PromptInputTextarea = ({\n\tonChange,\n\tonKeyDown,\n\tclassName,\n\tplaceholder = \"What would you like to know?\",\n\t...props\n}: PromptInputTextareaProps) => {\n\tconst attachments = usePromptInputAttachments();\n\tconst [isComposing, setIsComposing] = useState(false);\n\n\tconst handleKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = useCallback(\n\t\t(e) => {\n\t\t\tonKeyDown?.(e);\n\t\t\tif (e.defaultPrevented) return;\n\n\t\t\tif (e.key === \"Enter\") {\n\t\t\t\tif (isComposing || e.nativeEvent.isComposing) return;\n\t\t\t\tif (e.shiftKey) return;\n\t\t\t\te.preventDefault();\n\n\t\t\t\tconst { form } = e.currentTarget;\n\t\t\t\tconst submitButton = form?.querySelector(\n\t\t\t\t\t'button[type=\"submit\"]',\n\t\t\t\t) as HTMLButtonElement | null;\n\t\t\t\tif (submitButton?.disabled) return;\n\t\t\t\tform?.requestSubmit();\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\te.key === \"Backspace\" &&\n\t\t\t\te.currentTarget.value === \"\" &&\n\t\t\t\tattachments.files.length > 0\n\t\t\t) {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst lastAttachment = attachments.files.at(-1);\n\t\t\t\tif (lastAttachment) attachments.remove(lastAttachment.id);\n\t\t\t}\n\t\t},\n\t\t[onKeyDown, isComposing, attachments],\n\t);\n\n\tconst handlePaste: ClipboardEventHandler<HTMLTextAreaElement> = useCallback(\n\t\t(event) => {\n\t\t\tconst items = event.clipboardData?.items;\n\t\t\tif (!items) return;\n\n\t\t\tconst files: File[] = [];\n\t\t\tfor (const item of items) {\n\t\t\t\tif (item.kind === \"file\") {\n\t\t\t\t\tconst file = item.getAsFile();\n\t\t\t\t\tif (file) files.push(file);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (files.length > 0) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tattachments.add(files);\n\t\t\t}\n\t\t},\n\t\t[attachments],\n\t);\n\n\treturn (\n\t\t<textarea\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:field-sizing-content ww:max-h-48 ww:min-h-16 ww:w-full ww:resize-none ww:border-0 ww:bg-transparent ww:px-3 ww:py-3 ww:text-sm ww:outline-none ww:placeholder:text-muted-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tname=\"message\"\n\t\t\tonCompositionEnd={() => setIsComposing(false)}\n\t\t\tonCompositionStart={() => setIsComposing(true)}\n\t\t\tonKeyDown={handleKeyDown}\n\t\t\tonPaste={handlePaste}\n\t\t\tplaceholder={placeholder}\n\t\t\tonChange={onChange}\n\t\t\t{...props}\n\t\t/>\n\t);\n};\n\n// ============================================================================\n// Submit Button\n// ============================================================================\n\nexport type PromptInputSubmitProps = ComponentProps<typeof Button> & {\n\tstatus?: ChatStatus;\n\tonStop?: () => void;\n};\n\nexport const PromptInputSubmit = ({\n\tclassName,\n\tstatus,\n\tonStop,\n\tonClick,\n\tchildren,\n\t...props\n}: PromptInputSubmitProps) => {\n\tconst isGenerating = status === \"submitted\" || status === \"streaming\";\n\n\tlet Icon = <ArrowUpIcon className=\"ww:size-4\" />;\n\tif (status === \"submitted\") {\n\t\tIcon = <LoaderIcon className=\"ww:size-4 ww:animate-spin\" />;\n\t} else if (status === \"streaming\") {\n\t\tIcon = <SquareIcon className=\"ww:size-4\" />;\n\t}\n\n\tconst handleClick = useCallback(\n\t\t(e: React.MouseEvent<HTMLButtonElement>) => {\n\t\t\tif (isGenerating && onStop) {\n\t\t\t\te.preventDefault();\n\t\t\t\tonStop();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tonClick?.(e);\n\t\t},\n\t\t[isGenerating, onStop, onClick],\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\taria-label={isGenerating ? \"Stop\" : \"Submit\"}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:bg-foreground ww:text-background ww:hover:bg-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tonClick={handleClick}\n\t\t\tsize=\"icon-sm\"\n\t\t\ttype={isGenerating && onStop ? \"button\" : \"submit\"}\n\t\t\tvariant=\"ghost\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{children ?? Icon}\n\t\t</Button>\n\t);\n};\n\n// ============================================================================\n// Attachment Add Button (simple file picker, no Radix dropdown)\n// ============================================================================\n\nexport type PromptInputAddAttachmentsProps = ComponentProps<typeof Button>;\n\nexport const PromptInputAddAttachments = ({\n\tclassName,\n\tchildren,\n\t...props\n}: PromptInputAddAttachmentsProps) => {\n\tconst attachments = usePromptInputAttachments();\n\tconst hasFiles = attachments.files.length > 0;\n\n\tif (hasFiles) {\n\t\treturn (\n\t\t\t<Button\n\t\t\t\tclassName={cn(\"ww:group ww:relative\", className)}\n\t\t\t\tonClick={() => attachments.clear()}\n\t\t\t\tsize=\"icon-sm\"\n\t\t\t\ttype=\"button\"\n\t\t\t\tvariant=\"ghost\"\n\t\t\t\taria-label=\"Remove all attachments\"\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<span className=\"ww:flex ww:size-5 ww:items-center ww:justify-center ww:rounded-full ww:bg-primary ww:text-[10px] ww:font-medium ww:text-primary-foreground ww:transition-opacity ww:group-hover:opacity-0\">\n\t\t\t\t\t{attachments.files.length}\n\t\t\t\t</span>\n\t\t\t\t<XIcon className=\"ww:absolute ww:size-4 ww:opacity-0 ww:transition-opacity ww:group-hover:opacity-100\" />\n\t\t\t</Button>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Button\n\t\t\tclassName={cn(className)}\n\t\t\tonClick={() => attachments.openFileDialog()}\n\t\t\tsize=\"icon-sm\"\n\t\t\ttype=\"button\"\n\t\t\tvariant=\"ghost\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{children ?? <PaperclipIcon className=\"ww:size-4\" />}\n\t\t</Button>\n\t);\n};\n","\"use client\";\n\nimport type { FileUIPart } from \"ai\";\nimport { FileIcon } from \"lucide-react\";\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\n// ============================================================================\n// Attachments (inline list for chat bubbles)\n// ============================================================================\n\nexport type AttachmentsProps = HTMLAttributes<HTMLDivElement> & {\n\tfiles: FileUIPart[];\n};\n\nexport const Attachments = ({\n\tfiles,\n\tclassName,\n\t...props\n}: AttachmentsProps) => {\n\tif (files.length === 0) return null;\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-1.5\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{files.map((file, i) => (\n\t\t\t\t<AttachmentItem key={i} file={file} />\n\t\t\t))}\n\t\t</div>\n\t);\n};\n\n// ============================================================================\n// AttachmentItem\n// ============================================================================\n\nfunction AttachmentItem({ file }: { file: FileUIPart }) {\n\tconst isImage = file.mediaType?.startsWith(\"image/\");\n\n\tif (isImage && file.url) {\n\t\treturn (\n\t\t\t<img\n\t\t\t\tsrc={file.url}\n\t\t\t\talt={file.filename ?? \"attachment\"}\n\t\t\t\tclassName=\"ww:h-16 ww:max-w-32 ww:rounded ww:object-cover\"\n\t\t\t/>\n\t\t);\n\t}\n\n\treturn (\n\t\t<span className=\"ww:inline-flex ww:items-center ww:gap-1.5 ww:rounded ww:bg-background/20 ww:px-2 ww:py-1 ww:text-xs\">\n\t\t\t<FileIcon className=\"ww:size-3 ww:shrink-0\" />\n\t\t\t<span className=\"ww:max-w-24 ww:truncate\">{file.filename ?? \"file\"}</span>\n\t\t</span>\n\t);\n}\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type LoaderProps = HTMLAttributes<HTMLDivElement> & {\n\tsize?: number;\n};\n\nexport const Loader = ({ className, size = 5, ...props }: LoaderProps) => (\n\t<div className={cn(\"ww:flex ww:items-center ww:gap-1\", className)} {...props}>\n\t\t{[0, 1, 2].map((i) => (\n\t\t\t<div\n\t\t\t\tkey={i}\n\t\t\t\tclassName=\"ww:rounded-full ww:bg-muted-foreground/60\"\n\t\t\t\tstyle={{\n\t\t\t\t\twidth: size,\n\t\t\t\t\theight: size,\n\t\t\t\t\tanimation: \"ww-pulse 1.4s ease-in-out infinite\",\n\t\t\t\t\tanimationDelay: `${i * 0.2}s`,\n\t\t\t\t}}\n\t\t\t/>\n\t\t))}\n\t</div>\n);\n","\"use client\";\n\nimport { cjk } from \"@streamdown/cjk\";\nimport { code } from \"@streamdown/code\";\nimport type { UIMessage } from \"ai\";\nimport type { ComponentProps, HTMLAttributes } from \"react\";\nimport { memo } from \"react\";\nimport { Streamdown } from \"streamdown\";\nimport { cn } from \"../lib/utils\";\n\nexport type MessageProps = HTMLAttributes<HTMLDivElement> & {\n\tfrom: UIMessage[\"role\"];\n};\n\nexport const Message = ({ className, from, ...props }: MessageProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:group ww:flex ww:w-full ww:max-w-[95%] ww:flex-col ww:gap-2\",\n\t\t\tfrom === \"user\" ? \"is-user ww:ml-auto ww:justify-end\" : \"is-assistant\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type MessageContentProps = HTMLAttributes<HTMLDivElement>;\n\nexport const MessageContent = ({\n\tchildren,\n\tclassName,\n\t...props\n}: MessageContentProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:flex ww:w-fit ww:min-w-0 ww:max-w-full ww:flex-col ww:gap-2 ww:overflow-hidden ww:text-base\",\n\t\t\t\"ww:group-[.is-user]:ml-auto ww:group-[.is-user]:rounded-lg ww:group-[.is-user]:bg-user-bubble ww:group-[.is-user]:px-4 ww:group-[.is-user]:py-3 ww:group-[.is-user]:text-primary-foreground\",\n\t\t\t\"ww:group-[.is-assistant]:text-foreground\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t>\n\t\t{children}\n\t</div>\n);\n\nexport type MessageResponseProps = ComponentProps<typeof Streamdown>;\n\nconst streamdownPlugins = { cjk, code };\n\nexport const MessageResponse = memo(\n\t({ className, ...props }: MessageResponseProps) => (\n\t\t<Streamdown\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:size-full ww:[&>*:first-child]:mt-0 ww:[&>*:last-child]:mb-0\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tplugins={streamdownPlugins}\n\t\t\t{...props}\n\t\t/>\n\t),\n\t(prevProps, nextProps) => prevProps.children === nextProps.children,\n);\n\nMessageResponse.displayName = \"MessageResponse\";\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type ReasoningProps = HTMLAttributes<HTMLPreElement> & {\n\ttext: string;\n};\n\n/** Displays reasoning text inline with muted styling. */\nexport function Reasoning({ className, text, ...props }: ReasoningProps) {\n\tif (!text) return null;\n\n\treturn (\n\t\t<pre\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:mb-2 ww:overflow-x-auto ww:whitespace-pre-wrap ww:break-words ww:text-xs ww:font-mono ww:text-muted-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{text}\n\t\t</pre>\n\t);\n}\n","\"use client\";\n\nimport type { ToolUIPart } from \"ai\";\nimport {\n\tBracesIcon,\n\tCheckIcon,\n\tChevronDownIcon,\n\tChevronRightIcon,\n\tClipboardCopyIcon,\n\tServerIcon,\n} from \"lucide-react\";\nimport type { HTMLAttributes } from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\n/**\n * Produces an abbreviated single-line JSON preview for collapsed display.\n * Objects show truncated keys/values: `{ci… 'M…, pos… '2…, squa… 80}`\n * Arrays show their length: `Array(13)`\n */\nfunction truncateJSON(data: unknown, maxLength = 80): string {\n\tif (data === null || data === undefined) return String(data);\n\tif (typeof data !== \"object\") return String(data).slice(0, maxLength);\n\n\tif (Array.isArray(data)) {\n\t\treturn `Array(${data.length})`;\n\t}\n\n\tconst stringified = JSON.stringify(data);\n\tif (stringified.length <= maxLength) return stringified;\n\n\tconst entries = Object.entries(data as Record<string, unknown>);\n\tconst parts: string[] = [];\n\tlet remaining = maxLength - 2;\n\n\tfor (const [key, value] of entries) {\n\t\tif (remaining <= 8) break;\n\t\tconst keyAbbrev = key.length > 4 ? `${key.slice(0, 4)}\\u2026` : key;\n\t\tlet valStr: string;\n\t\tif (typeof value === \"string\") {\n\t\t\tvalStr = value.length > 2 ? `'${value.slice(0, 1)}\\u2026` : `'${value}'`;\n\t\t} else if (Array.isArray(value)) {\n\t\t\tvalStr = `Array(${value.length})`;\n\t\t} else if (typeof value === \"object\" && value !== null) {\n\t\t\tvalStr = `{\\u2026}`;\n\t\t} else {\n\t\t\tvalStr = String(value);\n\t\t}\n\t\tconst part = `${keyAbbrev}\\u2009${valStr}`;\n\t\tparts.push(part);\n\t\tremaining -= part.length + 3;\n\t}\n\n\treturn `{${parts.join(\", \")}}`;\n}\n\ninterface CopyButtonProps {\n\ttext: string;\n\tclassName?: string;\n}\n\n/** Ghost button that copies `text` to clipboard, showing \"Copied\" for 2s. */\nfunction CopyButton({ text, className }: CopyButtonProps) {\n\tconst [copied, setCopied] = useState(false);\n\tconst timeoutRef = useRef<ReturnType<typeof setTimeout>>(null);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (timeoutRef.current) {\n\t\t\t\tclearTimeout(timeoutRef.current);\n\t\t\t}\n\t\t};\n\t}, []);\n\n\tconst handleCopy = useCallback(\n\t\tasync (e: React.MouseEvent) => {\n\t\t\te.stopPropagation();\n\t\t\ttry {\n\t\t\t\tawait navigator.clipboard.writeText(text);\n\t\t\t\tsetCopied(true);\n\t\t\t\tif (timeoutRef.current) {\n\t\t\t\t\tclearTimeout(timeoutRef.current);\n\t\t\t\t}\n\t\t\t\ttimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n\t\t\t} catch {\n\t\t\t\t// Clipboard API not available\n\t\t\t}\n\t\t},\n\t\t[text],\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\tvariant=\"ghost\"\n\t\t\tsize=\"sm\"\n\t\t\tonClick={handleCopy}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:h-auto ww:gap-1 ww:px-1.5 ww:py-0.5 ww:text-xs ww:text-muted-foreground ww:hover:text-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t{copied ? (\n\t\t\t\t<>\n\t\t\t\t\t<CheckIcon className=\"ww:size-3.5\" />\n\t\t\t\t\t<span>Copied</span>\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<ClipboardCopyIcon className=\"ww:size-3.5\" />\n\t\t\t\t\t<span>Copy</span>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</Button>\n\t);\n}\n\ninterface CollapsibleJSONProps extends HTMLAttributes<HTMLDivElement> {\n\tdata: unknown;\n\tlabel: string;\n}\n\n/**\n * Labeled JSON section with a Copy button and a collapsible preview.\n * Collapsed: shows a truncated single-line abbreviation with a `>` chevron.\n * Expanded: rotates the chevron and shows full pretty-printed JSON.\n */\nfunction CollapsibleJSON({\n\tdata,\n\tlabel,\n\tclassName,\n\t...props\n}: CollapsibleJSONProps) {\n\tconst [expanded, setExpanded] = useState(false);\n\tconst fullJSON = useMemo(() => JSON.stringify(data, null, 2), [data]);\n\tconst preview = truncateJSON(data);\n\n\treturn (\n\t\t<div className={cn(\"ww:rounded-lg ww:bg-tool-card\", className)} {...props}>\n\t\t\t<div className=\"ww:flex ww:items-center ww:justify-between ww:px-3 ww:pt-2.5 ww:pb-1.5\">\n\t\t\t\t<span className=\"ww:text-xs ww:font-medium ww:text-muted-foreground\">\n\t\t\t\t\t{label}\n\t\t\t\t</span>\n\t\t\t\t<CopyButton text={fullJSON} />\n\t\t\t</div>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={() => setExpanded((v) => !v)}\n\t\t\t\tclassName=\"ww:flex ww:w-full ww:items-start ww:gap-2 ww:px-3 ww:pb-3 ww:text-left\"\n\t\t\t>\n\t\t\t\t<ChevronRightIcon\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:mt-0.5 ww:size-3.5 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-150\",\n\t\t\t\t\t\texpanded && \"ww:rotate-90\",\n\t\t\t\t\t)}\n\t\t\t\t/>\n\t\t\t\t{expanded ? (\n\t\t\t\t\t<pre className=\"ww:overflow-x-auto ww:text-xs ww:font-mono ww:text-foreground ww:whitespace-pre-wrap ww:break-all\">\n\t\t\t\t\t\t<code>{fullJSON}</code>\n\t\t\t\t\t</pre>\n\t\t\t\t) : (\n\t\t\t\t\t<span className=\"ww:truncate ww:text-xs ww:font-mono ww:text-foreground/80\">\n\t\t\t\t\t\t{preview}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</button>\n\t\t</div>\n\t);\n}\n\nconst ToolOpenContext = createContext<{\n\topen: boolean;\n\ttoggle: () => void;\n}>({ open: false, toggle: () => {} });\n\nexport type ToolProps = HTMLAttributes<HTMLDivElement> & {\n\tdefaultOpen?: boolean;\n};\n\n/**\n * Compound component root for a tool call display.\n * Provides open/closed state via context to ToolHeader and ToolContent.\n *\n * ```tsx\n * <Tool defaultOpen>\n * <ToolHeader title=\"Price estimate ready\" state=\"output-available\" />\n * <ToolContent>\n * <ToolServerInfo toolName=\"get_price_estimate\" serverName=\"Tuio v2\" />\n * <ToolInput input={args} />\n * <ToolOutput output={result} errorText={undefined} />\n * </ToolContent>\n * </Tool>\n * ```\n */\nexport function Tool({\n\tclassName,\n\tdefaultOpen = false,\n\tchildren,\n\t...props\n}: ToolProps) {\n\tconst [open, setOpen] = useState(defaultOpen);\n\treturn (\n\t\t<ToolOpenContext.Provider\n\t\t\tvalue={{ open, toggle: () => setOpen((o) => !o) }}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName={cn(\"ww:mb-4 ww:w-full\", className)}\n\t\t\t\tdata-state={open ? \"open\" : \"closed\"}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t</ToolOpenContext.Provider>\n\t);\n}\n\nexport type ToolHeaderProps = HTMLAttributes<HTMLButtonElement> & {\n\ttitle?: string;\n\tstate: ToolUIPart[\"state\"];\n};\n\n/** Clickable header that toggles the tool accordion. Shows a `{≡}` icon, title, and chevron. */\nexport function ToolHeader({\n\tclassName,\n\ttitle,\n\tstate,\n\t...props\n}: ToolHeaderProps) {\n\tconst { open, toggle } = useContext(ToolOpenContext);\n\tconst isRunning = state === \"input-available\" || state === \"input-streaming\";\n\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tonClick={toggle}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:flex ww:w-full ww:items-center ww:justify-between ww:gap-3 ww:py-1.5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\taria-expanded={open}\n\t\t\t{...props}\n\t\t>\n\t\t\t<div className=\"ww:flex ww:min-w-0 ww:items-center ww:gap-2\">\n\t\t\t\t<BracesIcon className=\"ww:size-4 ww:shrink-0 ww:text-muted-foreground\" />\n\t\t\t\t<span className=\"ww:truncate ww:text-sm ww:font-medium\">{title}</span>\n\t\t\t\t{isRunning && (\n\t\t\t\t\t<span className=\"ww:size-2 ww:shrink-0 ww:rounded-full ww:bg-primary ww:animate-pulse\" />\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<ChevronDownIcon\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:size-4 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-200\",\n\t\t\t\t\topen && \"ww:rotate-180\",\n\t\t\t\t)}\n\t\t\t/>\n\t\t</button>\n\t);\n}\n\nexport type ToolServerInfoProps = HTMLAttributes<HTMLDivElement> & {\n\tserverName?: string;\n\tserverIcon?: string;\n\ttoolName: string;\n};\n\n/** Optional MCP server identity card. Shows server icon + name and the tool function name. Renders nothing if no props need display. */\nexport function ToolServerInfo({\n\tclassName,\n\tserverName,\n\tserverIcon,\n\ttoolName,\n\t...props\n}: ToolServerInfoProps) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:flex ww:items-center ww:gap-3 ww:rounded-lg ww:border ww:border-border ww:px-3 ww:py-2.5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{serverIcon ? (\n\t\t\t\t<img\n\t\t\t\t\tsrc={serverIcon}\n\t\t\t\t\talt={serverName ?? \"\"}\n\t\t\t\t\tclassName=\"ww:size-8 ww:shrink-0 ww:rounded-full ww:object-cover\"\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"ww:flex ww:size-8 ww:shrink-0 ww:items-center ww:justify-center ww:rounded-full ww:border ww:border-border ww:bg-muted\">\n\t\t\t\t\t<ServerIcon className=\"ww:size-4 ww:text-muted-foreground\" />\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"ww:flex ww:min-w-0 ww:flex-col\">\n\t\t\t\t{serverName && (\n\t\t\t\t\t<span className=\"ww:text-xs ww:text-muted-foreground\">\n\t\t\t\t\t\t{serverName}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t\t<span className=\"ww:truncate ww:text-sm ww:font-semibold\">\n\t\t\t\t\t{toolName}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport type ToolContentProps = HTMLAttributes<HTMLDivElement>;\n\n/** Collapsible body that animates open/closed. Content below smoothly pushes up/down via a grid-row height transition. */\nexport function ToolContent({\n\tclassName,\n\tchildren,\n\t...props\n}: ToolContentProps) {\n\tconst { open } = useContext(ToolOpenContext);\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:grid ww:transition-[grid-template-rows,opacity] ww:duration-200 ww:ease-out\",\n\t\t\t\topen\n\t\t\t\t\t? \"ww:grid-rows-[1fr] ww:opacity-100\"\n\t\t\t\t\t: \"ww:grid-rows-[0fr] ww:opacity-0\",\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"ww:min-h-0 ww:overflow-hidden\">\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:mt-2 ww:space-y-3 ww:rounded-lg ww:border ww:border-border ww:bg-background ww:p-3\",\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t)}\n\t\t\t\t\t{...props}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport type ToolInputProps = HTMLAttributes<HTMLDivElement> & {\n\tinput: ToolUIPart[\"input\"];\n};\n\n/** Displays the tool call request parameters as a collapsible JSON section labeled \"Request\". */\nexport function ToolInput({ className, input, ...props }: ToolInputProps) {\n\treturn (\n\t\t<CollapsibleJSON\n\t\t\tdata={input}\n\t\t\tlabel=\"Request\"\n\t\t\tclassName={className}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport type ToolOutputProps = HTMLAttributes<HTMLDivElement> & {\n\toutput: ToolUIPart[\"output\"];\n\terrorText: ToolUIPart[\"errorText\"];\n};\n\nfunction getUiMeta(output: unknown): Record<string, unknown> | undefined {\n\tif (typeof output !== \"object\" || output === null) return undefined;\n\tconst meta = (output as Record<string, unknown>)._meta;\n\tif (typeof meta !== \"object\" || meta === null) return undefined;\n\tconst ui = (meta as Record<string, unknown>).ui;\n\tif (typeof ui !== \"object\" || ui === null) return undefined;\n\treturn ui as Record<string, unknown>;\n}\n\n/** Extract the MCP app resource URI from `output._meta.ui.resourceUri`, if present. */\nexport function getResourceUri(output: unknown): string | undefined {\n\tconst uri = getUiMeta(output)?.resourceUri;\n\treturn typeof uri === \"string\" ? uri : undefined;\n}\n\n/** Extract the auto-height flag from `output._meta.ui.autoHeight`, if present. */\nexport function getAutoHeight(output: unknown): boolean {\n\treturn getUiMeta(output)?.autoHeight === true;\n}\n\n/** Displays the tool call result as a collapsible JSON section labeled \"Response\", or an error block if `errorText` is set. */\nexport function ToolOutput({\n\tclassName,\n\toutput,\n\terrorText,\n\t...props\n}: ToolOutputProps) {\n\tif (!(output || errorText)) return null;\n\n\tif (errorText) {\n\t\treturn (\n\t\t\t<div className={cn(\"ww:space-y-2\", className)} {...props}>\n\t\t\t\t<h4 className=\"ww:text-xs ww:font-medium ww:uppercase ww:tracking-wide ww:text-muted-foreground\">\n\t\t\t\t\tError\n\t\t\t\t</h4>\n\t\t\t\t<div className=\"ww:rounded-lg ww:bg-destructive/10 ww:p-3 ww:text-xs ww:text-destructive\">\n\t\t\t\t\t{errorText}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<CollapsibleJSON\n\t\t\tdata={output}\n\t\t\tlabel=\"Response\"\n\t\t\tclassName={className}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nconst DEFAULT_RESOURCE_ENDPOINT = \"/api/mcp/resource\";\nconst MAX_HEIGHT = 500;\nconst DEFAULT_HEIGHT = 0;\nconst PROTOCOL_VERSION = \"2026-01-26\";\nconst RESIZE_ANIMATION_MS = 300;\nconst HANDSHAKE_TIMEOUT_MS = 3000;\nconst MAX_RETRIES = 3;\n\nexport interface McpAppFrameProps {\n\tresourceUri: string;\n\ttoolInput: Record<string, unknown>;\n\ttoolResult: {\n\t\tcontent?: Array<{ type: string; text?: string }>;\n\t\tstructuredContent?: Record<string, unknown>;\n\t};\n\tresourceEndpoint?: string;\n\tisDark?: boolean;\n\tclassName?: string;\n\t/** When true, the iframe height auto-adapts to its content. Set via `_meta.ui.autoHeight` in the tool result. */\n\tautoHeight?: boolean;\n\t/** Called when the view requests to open a URL */\n\tonOpenLink?: (url: string) => void;\n\t/** Called when a widget sends a follow-up message via `ui/message` */\n\tonFollowUp?: (message: {\n\t\trole: string;\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t}) => void;\n}\n\nexport function McpAppFrame({\n\tresourceUri,\n\ttoolInput,\n\ttoolResult,\n\tresourceEndpoint = DEFAULT_RESOURCE_ENDPOINT,\n\tisDark = false,\n\tclassName,\n\t// TODO: REMOVE — defaulting to true for playground testing\n\tautoHeight = true,\n\tonOpenLink,\n\tonFollowUp,\n}: McpAppFrameProps) {\n\tconst iframeRef = useRef<HTMLIFrameElement>(null);\n\tconst toolInputRef = useRef(toolInput);\n\tconst toolResultRef = useRef(toolResult);\n\tconst lastSizeRef = useRef({ width: 0, height: 0 });\n\tconst animationRef = useRef<Animation | null>(null);\n\tconst initializedRef = useRef(false);\n\tconst retryCountRef = useRef(0);\n\tconst [height, setHeight] = useState(DEFAULT_HEIGHT);\n\tconst [width, setWidth] = useState<number | undefined>(undefined);\n\tconst onOpenLinkRef = useRef(onOpenLink);\n\tconst onFollowUpRef = useRef(onFollowUp);\n\n\ttoolInputRef.current = toolInput;\n\ttoolResultRef.current = toolResult;\n\tonOpenLinkRef.current = onOpenLink;\n\tonFollowUpRef.current = onFollowUp;\n\n\tconst clampHeight = useCallback(\n\t\t(h: number) => {\n\t\t\tif (autoHeight) return Math.max(h, 0);\n\t\t\treturn Math.min(Math.max(h, 50), MAX_HEIGHT);\n\t\t},\n\t\t[autoHeight],\n\t);\n\n\t// Build the iframe src URL directly — avoids null-origin issues with srcdoc\n\tconst iframeSrc = useMemo(\n\t\t() => `${resourceEndpoint}?uri=${encodeURIComponent(resourceUri)}`,\n\t\t[resourceEndpoint, resourceUri],\n\t);\n\n\tconst isDarkRef = useRef(isDark);\n\tisDarkRef.current = isDark;\n\n\t// Send theme changes to the iframe (only after handshake is complete)\n\tuseEffect(() => {\n\t\tif (!initializedRef.current) return;\n\t\tconst iframe = iframeRef.current;\n\t\tif (!iframe?.contentWindow) return;\n\n\t\tiframe.contentWindow.postMessage(\n\t\t\t{\n\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\tmethod: \"ui/notifications/host-context-changed\",\n\t\t\t\tparams: {\n\t\t\t\t\ttheme: isDark ? \"dark\" : \"light\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"*\",\n\t\t);\n\t}, [isDark]);\n\n\t// Synchronous postMessage protocol handler — no async imports, no timing issues.\n\t// Handles the MCP UI protocol (ui/initialize, notifications, etc.) directly.\n\tuseEffect(() => {\n\t\tconst iframe = iframeRef.current;\n\t\tif (!iframe) return;\n\n\t\tlet disposed = false;\n\t\tlet handshakeReceived = false;\n\t\tconst debug = (...args: unknown[]) =>\n\t\t\tconsole.debug(\"[McpAppFrame]\", ...args);\n\n\t\tdebug(\"effect mounted, waiting for handshake\");\n\n\t\t// Retry: reload iframe if handshake doesn't arrive in time\n\t\tconst handshakeTimer = setTimeout(() => {\n\t\t\tif (disposed || handshakeReceived) return;\n\t\t\tif (retryCountRef.current >= MAX_RETRIES) {\n\t\t\t\tdebug(\"handshake failed after\", MAX_RETRIES, \"retries, giving up\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tretryCountRef.current += 1;\n\t\t\tdebug(\n\t\t\t\t\"handshake timeout, reloading iframe (retry\",\n\t\t\t\tretryCountRef.current,\n\t\t\t\t\"of\",\n\t\t\t\tMAX_RETRIES,\n\t\t\t\t\")\",\n\t\t\t);\n\t\t\t// Force reload with a cache-busting param\n\t\t\tconst url = new URL(iframe.src);\n\t\t\turl.searchParams.set(\"_retry\", String(retryCountRef.current));\n\t\t\tiframe.src = url.toString();\n\t\t}, HANDSHAKE_TIMEOUT_MS);\n\n\t\tconst postToIframe = (msg: Record<string, unknown>) => {\n\t\t\tdebug(\"→ send\", msg.method ?? `response:${msg.id}`, msg);\n\t\t\tiframe.contentWindow?.postMessage(msg, \"*\");\n\t\t};\n\n\t\tconst handleMessage = (event: MessageEvent) => {\n\t\t\tif (disposed) return;\n\t\t\tif (event.source !== iframe.contentWindow) return;\n\n\t\t\tconst data = event.data;\n\t\t\tif (!data || typeof data !== \"object\" || data.jsonrpc !== \"2.0\") return;\n\n\t\t\tconst method: string | undefined = data.method;\n\t\t\tconst id: number | string | undefined = data.id;\n\n\t\t\tdebug(\"← recv\", method ?? `response:${id}`, data);\n\n\t\t\t// ui/initialize — widget requests handshake\n\t\t\tif (method === \"ui/initialize\" && id != null) {\n\t\t\t\thandshakeReceived = true;\n\t\t\t\tclearTimeout(handshakeTimer);\n\t\t\t\tdebug(\"handshake started\");\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tid,\n\t\t\t\t\tresult: {\n\t\t\t\t\t\tprotocolVersion: data.params?.protocolVersion ?? PROTOCOL_VERSION,\n\t\t\t\t\t\thostInfo: { name: \"WaniWani Chat\", version: \"1.0.0\" },\n\t\t\t\t\t\thostCapabilities: {\n\t\t\t\t\t\t\topenLinks: {},\n\t\t\t\t\t\t\tmessage: {},\n\t\t\t\t\t\t},\n\t\t\t\t\t\thostContext: {\n\t\t\t\t\t\t\ttheme: isDarkRef.current ? \"dark\" : \"light\",\n\t\t\t\t\t\t\tdisplayMode: \"inline\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/notifications/initialized — widget confirms init, we send tool data\n\t\t\tif (method === \"ui/notifications/initialized\") {\n\t\t\t\tdebug(\"handshake complete, sending tool data\");\n\t\t\t\tinitializedRef.current = true;\n\t\t\t\tconst input = toolInputRef.current;\n\t\t\t\tconst result = toolResultRef.current;\n\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tmethod: \"ui/notifications/tool-input\",\n\t\t\t\t\tparams: { arguments: input },\n\t\t\t\t});\n\n\t\t\t\tconst content = result.content ?? [\n\t\t\t\t\t{ type: \"text\", text: JSON.stringify(result) },\n\t\t\t\t];\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tmethod: \"ui/notifications/tool-result\",\n\t\t\t\t\tparams: {\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\tstructuredContent: result.structuredContent,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/notifications/size-changed — widget reports content size\n\t\t\tif (method === \"ui/notifications/size-changed\") {\n\t\t\t\tconst params = data.params;\n\t\t\t\tconst newHeight =\n\t\t\t\t\ttypeof params?.height === \"number\" ? params.height : undefined;\n\t\t\t\tconst newWidth =\n\t\t\t\t\ttypeof params?.width === \"number\" ? params.width : undefined;\n\n\t\t\t\t// Deduplicate — only update if values actually changed (prevents feedback loops)\n\t\t\t\tconst last = lastSizeRef.current;\n\t\t\t\tconst heightChanged =\n\t\t\t\t\tnewHeight !== undefined && newHeight !== last.height;\n\t\t\t\tconst widthChanged = newWidth !== undefined && newWidth !== last.width;\n\n\t\t\t\tdebug(\"size-changed\", {\n\t\t\t\t\tnewHeight,\n\t\t\t\t\tnewWidth,\n\t\t\t\t\tlastHeight: last.height,\n\t\t\t\t\tlastWidth: last.width,\n\t\t\t\t\theightChanged,\n\t\t\t\t\twidthChanged,\n\t\t\t\t});\n\n\t\t\t\tif (!heightChanged && !widthChanged) return;\n\n\t\t\t\tif (heightChanged && newHeight !== undefined) {\n\t\t\t\t\tlast.height = newHeight;\n\t\t\t\t\tconst clamped = clampHeight(newHeight);\n\n\t\t\t\t\t// Get current visual height before canceling the old animation\n\t\t\t\t\tconst from = iframe.getBoundingClientRect().height;\n\n\t\t\t\t\t// Cancel previous animation so its fill: \"forwards\" stops overriding inline style\n\t\t\t\t\tif (animationRef.current) {\n\t\t\t\t\t\tanimationRef.current.cancel();\n\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the target height in React state (takes effect once no animation overrides it)\n\t\t\t\t\tsetHeight(clamped);\n\n\t\t\t\t\t// Animate the height transition\n\t\t\t\t\tif (iframe.animate && Math.abs(from - clamped) > 2) {\n\t\t\t\t\t\tconst anim = iframe.animate(\n\t\t\t\t\t\t\t[{ height: `${from}px` }, { height: `${clamped}px` }],\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tduration: RESIZE_ANIMATION_MS,\n\t\t\t\t\t\t\t\teasing: \"ease-out\",\n\t\t\t\t\t\t\t\tfill: \"forwards\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\tanimationRef.current = anim;\n\t\t\t\t\t\t// Once done, remove the animation so the inline style is the source of truth\n\t\t\t\t\t\tanim.onfinish = () => {\n\t\t\t\t\t\t\tif (animationRef.current === anim) {\n\t\t\t\t\t\t\t\tanim.cancel();\n\t\t\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (widthChanged && autoHeight && newWidth !== undefined) {\n\t\t\t\t\tlast.width = newWidth;\n\t\t\t\t\tsetWidth(newWidth);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/open-link — widget requests to open a URL\n\t\t\tif (method === \"ui/open-link\" && id != null) {\n\t\t\t\tconst url = data.params?.url;\n\t\t\t\tif (typeof url === \"string\") {\n\t\t\t\t\tif (onOpenLinkRef.current) {\n\t\t\t\t\t\tonOpenLinkRef.current(url);\n\t\t\t\t\t} else {\n\t\t\t\t\t\twindow.open(url, \"_blank\", \"noopener,noreferrer\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/message — widget sends a chat message\n\t\t\tif (method === \"ui/message\" && id != null) {\n\t\t\t\tif (onFollowUpRef.current && data.params) {\n\t\t\t\t\tonFollowUpRef.current(data.params);\n\t\t\t\t}\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/request-display-mode — widget requests fullscreen/inline/pip\n\t\t\tif (method === \"ui/request-display-mode\" && id != null) {\n\t\t\t\t// Acknowledge but stay inline for now\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/resource-teardown — graceful shutdown\n\t\t\tif (method === \"ui/resource-teardown\" && id != null) {\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ping — keep-alive\n\t\t\tif (method === \"ping\" && id != null) {\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"message\", handleMessage);\n\n\t\treturn () => {\n\t\t\tdebug(\"effect cleanup (disposed)\");\n\t\t\tdisposed = true;\n\t\t\tclearTimeout(handshakeTimer);\n\t\t\twindow.removeEventListener(\"message\", handleMessage);\n\t\t};\n\t}, [autoHeight, clampHeight]);\n\n\treturn (\n\t\t<iframe\n\t\t\tref={iframeRef}\n\t\t\tsrc={iframeSrc}\n\t\t\tsandbox=\"allow-scripts allow-forms allow-same-origin\"\n\t\t\tclassName={cn(\"ww:rounded-md ww:border ww:border-border\", className)}\n\t\t\tstyle={{\n\t\t\t\theight,\n\t\t\t\tminWidth: width ? `min(${width}px, 100%)` : undefined,\n\t\t\t\twidth: \"100%\",\n\t\t\t\tborder: \"none\",\n\t\t\t\tcolorScheme: \"auto\",\n\t\t\t}}\n\t\t\ttitle=\"MCP App\"\n\t\t/>\n\t);\n}\n","\"use client\";\n\nimport { Component, type ReactNode } from \"react\";\n\ninterface Props {\n\tchildren: ReactNode;\n}\n\ninterface State {\n\thasError: boolean;\n}\n\nexport class WidgetErrorBoundary extends Component<Props, State> {\n\tstate: State = { hasError: false };\n\n\tstatic getDerivedStateFromError(): State {\n\t\treturn { hasError: true };\n\t}\n\n\tcomponentDidCatch(error: Error) {\n\t\tconsole.warn(\"[WaniWani] Widget failed to render:\", error.message);\n\t}\n\n\trender() {\n\t\tif (this.state.hasError) {\n\t\t\treturn (\n\t\t\t\t<div className=\"ww:flex ww:items-center ww:justify-between ww:rounded-md ww:border ww:border-border ww:bg-muted/50 ww:px-4 ww:py-3 ww:text-sm ww:text-muted-foreground\">\n\t\t\t\t\t<span>Widget failed to load</span>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() => this.setState({ hasError: false })}\n\t\t\t\t\t\tclassName=\"ww:text-xs ww:font-medium ww:text-primary ww:hover:underline\"\n\t\t\t\t\t>\n\t\t\t\t\t\tRetry\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\n\t\treturn this.props.children;\n\t}\n}\n","\"use client\";\n\nimport type { ChatStatus, ReasoningUIPart, ToolUIPart, UIMessage } from \"ai\";\n\nimport { Attachments } from \"../ai-elements/attachments\";\nimport { Loader } from \"../ai-elements/loader\";\nimport {\n\tMessage,\n\tMessageContent,\n\tMessageResponse,\n} from \"../ai-elements/message\";\nimport { Reasoning } from \"../ai-elements/reasoning\";\nimport {\n\tgetAutoHeight,\n\tgetResourceUri,\n\tTool,\n\tToolContent,\n\tToolHeader,\n\tToolInput,\n\tToolOutput,\n} from \"../ai-elements/tool\";\nimport { McpAppFrame } from \"./mcp-app-frame\";\nimport { WidgetErrorBoundary } from \"./widget-error-boundary\";\n\n/** Converts `get_price_estimate` or `compare-prices` → `Get price estimate` / `Compare prices` */\nfunction formatToolName(name: string): string {\n\treturn name.replace(/[-_]/g, \" \").replace(/^\\w/, (c) => c.toUpperCase());\n}\n\ninterface MessageListProps {\n\tmessages: UIMessage[];\n\tstatus: ChatStatus;\n\twelcomeMessage?: string;\n\tresourceEndpoint?: string;\n\tisDark?: boolean;\n\tonFollowUp?: (message: {\n\t\trole: string;\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t}) => void;\n}\n\nexport function MessageList({\n\tmessages,\n\tstatus,\n\twelcomeMessage,\n\tresourceEndpoint,\n\tisDark,\n\tonFollowUp,\n}: MessageListProps) {\n\tconst isLoading = status === \"submitted\" || status === \"streaming\";\n\tconst lastMessage = messages[messages.length - 1];\n\tconst hasMessages = messages.length > 0;\n\tconst showLoaderBubble =\n\t\tisLoading && (!hasMessages || lastMessage.role === \"user\");\n\n\treturn (\n\t\t<>\n\t\t\t{welcomeMessage && (\n\t\t\t\t<Message from=\"assistant\">\n\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t<MessageResponse>{welcomeMessage}</MessageResponse>\n\t\t\t\t\t</MessageContent>\n\t\t\t\t</Message>\n\t\t\t)}\n\t\t\t{messages.map((message) => {\n\t\t\t\tconst textParts = message.parts.filter((p) => p.type === \"text\");\n\t\t\t\tconst reasoningParts = message.parts.filter(\n\t\t\t\t\t(p): p is ReasoningUIPart => p.type === \"reasoning\",\n\t\t\t\t);\n\t\t\t\tconst fileParts = message.parts.filter((p) => p.type === \"file\");\n\t\t\t\tconst toolParts = message.parts.filter(\n\t\t\t\t\t(\n\t\t\t\t\t\tp,\n\t\t\t\t\t): p is typeof p & {\n\t\t\t\t\t\ttoolCallId: string;\n\t\t\t\t\t\ttoolName: string;\n\t\t\t\t\t\tstate: ToolUIPart[\"state\"];\n\t\t\t\t\t\tinput: unknown;\n\t\t\t\t\t\ttitle?: string;\n\t\t\t\t\t} => \"toolCallId\" in p,\n\t\t\t\t);\n\t\t\t\tconst isLastAssistant =\n\t\t\t\t\tmessage === lastMessage && message.role === \"assistant\";\n\t\t\t\tconst hasTextContent = textParts.length > 0;\n\n\t\t\t\treturn (\n\t\t\t\t\t<Message from={message.role} key={message.id}>\n\t\t\t\t\t\t{reasoningParts.map((part, i) => (\n\t\t\t\t\t\t\t<Reasoning\n\t\t\t\t\t\t\t\tkey={`reasoning-${message.id}-${i}`}\n\t\t\t\t\t\t\t\ttext={part.text}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{toolParts.map((part) => {\n\t\t\t\t\t\t\tconst output = \"output\" in part ? part.output : undefined;\n\t\t\t\t\t\t\tconst resourceUri =\n\t\t\t\t\t\t\t\toutput !== undefined ? getResourceUri(output) : undefined;\n\t\t\t\t\t\t\tconst autoHeight =\n\t\t\t\t\t\t\t\toutput !== undefined ? getAutoHeight(output) : false;\n\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div key={part.toolCallId}>\n\t\t\t\t\t\t\t\t\t<Tool defaultOpen={part.state === \"output-available\"}>\n\t\t\t\t\t\t\t\t\t\t<ToolHeader\n\t\t\t\t\t\t\t\t\t\t\ttitle={part.title ?? formatToolName(part.toolName)}\n\t\t\t\t\t\t\t\t\t\t\tstate={part.state}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<ToolContent>\n\t\t\t\t\t\t\t\t\t\t\t<ToolInput input={part.input} />\n\t\t\t\t\t\t\t\t\t\t\t{output !== undefined && (\n\t\t\t\t\t\t\t\t\t\t\t\t<ToolOutput\n\t\t\t\t\t\t\t\t\t\t\t\t\toutput={output}\n\t\t\t\t\t\t\t\t\t\t\t\t\terrorText={\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"errorText\" in part ? part.errorText : undefined\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</ToolContent>\n\t\t\t\t\t\t\t\t\t</Tool>\n\t\t\t\t\t\t\t\t\t{resourceUri && output !== undefined && (\n\t\t\t\t\t\t\t\t\t\t<WidgetErrorBoundary>\n\t\t\t\t\t\t\t\t\t\t\t<McpAppFrame\n\t\t\t\t\t\t\t\t\t\t\t\tresourceUri={resourceUri}\n\t\t\t\t\t\t\t\t\t\t\t\ttoolInput={\n\t\t\t\t\t\t\t\t\t\t\t\t\t(part.input as Record<string, unknown>) ?? {}\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\ttoolResult={{\n\t\t\t\t\t\t\t\t\t\t\t\t\tcontent: (output as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.content as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| Array<{ type: string; text?: string }>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t\t\t\tstructuredContent: (output as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.structuredContent as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| Record<string, unknown>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tresourceEndpoint={resourceEndpoint}\n\t\t\t\t\t\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\t\t\t\t\t\tautoHeight={autoHeight}\n\t\t\t\t\t\t\t\t\t\t\t\tonFollowUp={onFollowUp}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</WidgetErrorBoundary>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t\t{fileParts.length > 0 && <Attachments files={fileParts} />}\n\t\t\t\t\t\t\t{hasTextContent\n\t\t\t\t\t\t\t\t? textParts.map((part, i) => (\n\t\t\t\t\t\t\t\t\t\t<MessageResponse key={`${message.id}-${i}`}>\n\t\t\t\t\t\t\t\t\t\t\t{part.type === \"text\" ? part.text : \"\"}\n\t\t\t\t\t\t\t\t\t\t</MessageResponse>\n\t\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t\t: isLastAssistant && isLoading && <Loader />}\n\t\t\t\t\t\t</MessageContent>\n\t\t\t\t\t</Message>\n\t\t\t\t);\n\t\t\t})}\n\t\t\t{showLoaderBubble && (\n\t\t\t\t<Message from=\"assistant\">\n\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t<Loader />\n\t\t\t\t\t</MessageContent>\n\t\t\t\t</Message>\n\t\t\t)}\n\t\t</>\n\t);\n}\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport interface SuggestionsProps\n\textends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n\tsuggestions: string[];\n\tisLoading?: boolean;\n\tonSelect: (suggestion: string) => void;\n}\n\nexport function Suggestions({\n\tsuggestions,\n\tisLoading,\n\tonSelect,\n\tclassName,\n\t...props\n}: SuggestionsProps) {\n\tif (suggestions.length === 0 && !isLoading) return null;\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-2 ww:px-3 ww:py-2\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{isLoading\n\t\t\t\t? [0, 1, 2].map((i) => (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\t\tclassName=\"ww:h-7 ww:rounded-full ww:bg-accent ww:animate-pulse\"\n\t\t\t\t\t\t\tstyle={{ width: `${60 + i * 20}px` }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))\n\t\t\t\t: suggestions.map((suggestion, index) => (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tkey={suggestion}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={() => onSelect(suggestion)}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"ww:rounded-full ww:border ww:border-border ww:bg-background ww:px-3 ww:py-1 ww:text-xs\",\n\t\t\t\t\t\t\t\t\"ww:text-foreground ww:hover:bg-accent ww:hover:border-primary/30\",\n\t\t\t\t\t\t\t\t\"ww:transition-all ww:duration-200 ww:ease-out ww:cursor-pointer\",\n\t\t\t\t\t\t\t\t\"ww:animate-[ww-fade-in_0.2s_ease-out_both]\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tstyle={{ animationDelay: `${index * 50}ms` }}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{suggestion}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t))}\n\t\t</div>\n\t);\n}\n","\"use client\";\n\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport } from \"ai\";\nimport { useCallback, useRef, useState } from \"react\";\nimport type { ChatBaseProps } from \"../@types\";\nimport type { PromptInputMessage } from \"../ai-elements/prompt-input\";\n\nexport function useChatEngine(props: ChatBaseProps) {\n\tconst {\n\t\tapi = \"https://app.waniwani.ai/api/chat\",\n\t\theaders: userHeaders,\n\t\tbody,\n\t\tonMessageSent,\n\t\tonResponseReceived,\n\t} = props;\n\n\tconst transportRef = useRef(\n\t\tnew DefaultChatTransport({\n\t\t\tapi,\n\t\t\theaders: {\n\t\t\t\t...userHeaders,\n\t\t\t},\n\t\t\tbody,\n\t\t}),\n\t);\n\n\tconst { messages, sendMessage, status } = useChat({\n\t\ttransport: transportRef.current,\n\t\tonFinish() {\n\t\t\tonResponseReceived?.();\n\t\t},\n\t\tonError(error) {\n\t\t\tconsole.warn(\"[WaniWani] Chat error:\", error.message);\n\t\t},\n\t});\n\n\tconst [text, setText] = useState(\"\");\n\n\tconst handleSubmit = useCallback(\n\t\t(message: PromptInputMessage) => {\n\t\t\tconst hasText = Boolean(message.text?.trim());\n\t\t\tconst hasFiles = Boolean(message.files?.length);\n\t\t\tif (!(hasText || hasFiles)) return;\n\n\t\t\tsendMessage({\n\t\t\t\ttext: message.text || \"\",\n\t\t\t\tfiles: message.files,\n\t\t\t});\n\n\t\t\tonMessageSent?.(message.text || \"\");\n\t\t\tsetText(\"\");\n\t\t},\n\t\t[sendMessage, onMessageSent],\n\t);\n\n\tconst handleTextChange = useCallback(\n\t\t(e: React.ChangeEvent<HTMLTextAreaElement>) => {\n\t\t\tsetText(e.target.value);\n\t\t},\n\t\t[],\n\t);\n\n\tconst isLoading = status === \"submitted\" || status === \"streaming\";\n\tconst lastMessage = messages[messages.length - 1];\n\tconst hasMessages = messages.length > 0;\n\tconst showLoaderBubble =\n\t\tisLoading && (!hasMessages || lastMessage.role === \"user\");\n\n\treturn {\n\t\tmessages,\n\t\tstatus,\n\t\ttext,\n\t\tsetText,\n\t\thandleSubmit,\n\t\thandleTextChange,\n\t\tisLoading,\n\t\tshowLoaderBubble,\n\t\tlastMessage,\n\t\thasMessages,\n\t\tsendMessage,\n\t};\n}\n","\"use client\";\n\nimport type { ChatStatus, UIMessage } from \"ai\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { SuggestionsConfig } from \"../@types\";\n\nexport interface UseSuggestionsOptions {\n\tmessages: UIMessage[];\n\tstatus: ChatStatus;\n\tconfig?: boolean | SuggestionsConfig;\n}\n\n/**\n * Extract suggestions from the last assistant message's data part.\n * The API streams a `data-suggestions` part at the end of the response:\n * `{ type: \"data-suggestions\", data: { suggestions: string[] } }`\n */\nfunction extractSuggestions(message: UIMessage): string[] | null {\n\tfor (const part of message.parts) {\n\t\tconst p = part as Record<string, unknown>;\n\t\t// Handle both \"data-suggestions\" and generic \"data\" part types\n\t\tif (p.type === \"data\" || p.type === \"data-suggestions\") {\n\t\t\tconst data = p.data as Record<string, unknown> | undefined;\n\t\t\tif (data && Array.isArray(data.suggestions)) {\n\t\t\t\treturn data.suggestions as string[];\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction isConfigObject(\n\tconfig: boolean | SuggestionsConfig | undefined,\n): config is SuggestionsConfig {\n\treturn typeof config === \"object\" && config !== null && \"initial\" in config;\n}\n\nexport function useSuggestions(options: UseSuggestionsOptions) {\n\tconst { messages, status, config } = options;\n\n\tconst [suggestions, setSuggestions] = useState<string[]>(\n\t\t(isConfigObject(config) && config.initial ? config.initial : []) ?? [],\n\t);\n\tconst prevStatusRef = useRef<ChatStatus>(status);\n\n\tconst isEnabled = Boolean(config);\n\n\tconst clear = useCallback(() => {\n\t\tsetSuggestions([]);\n\t}, []);\n\n\t// Clear when a new user message arrives\n\tconst lastMessage = messages[messages.length - 1];\n\tuseEffect(() => {\n\t\tif (lastMessage?.role === \"user\") {\n\t\t\tclear();\n\t\t}\n\t}, [lastMessage, clear]);\n\n\t// Extract suggestions from message parts on streaming → ready transition\n\tuseEffect(() => {\n\t\tconst prevStatus = prevStatusRef.current;\n\t\tprevStatusRef.current = status;\n\n\t\tif (prevStatus === \"streaming\" && status === \"ready\" && isEnabled) {\n\t\t\tconst lastAssistant = [...messages]\n\t\t\t\t.reverse()\n\t\t\t\t.find((m) => m.role === \"assistant\");\n\t\t\tif (!lastAssistant) return;\n\n\t\t\tconsole.log(\"[WaniWani] Assistant parts:\", lastAssistant.parts);\n\n\t\t\tconst extracted = extractSuggestions(lastAssistant);\n\t\t\tconsole.log(\"[WaniWani] Extracted suggestions:\", extracted);\n\t\t\tif (extracted) {\n\t\t\t\tsetSuggestions(extracted);\n\t\t\t}\n\t\t}\n\t}, [status, isEnabled, messages]);\n\n\treturn { suggestions, isLoading: false, clear };\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nconst TYPE_SPEED_MS = 50;\nconst DELETE_SPEED_MS = 30;\nconst PAUSE_AFTER_TYPE_MS = 2000;\nconst PAUSE_AFTER_DELETE_MS = 500;\n\n/**\n * Returns a string that animates like someone typing and deleting the placeholder text.\n * Only animates when `active` is true (i.e. the input is empty).\n */\nexport function useTypingPlaceholder(text: string, active = true): string {\n\tconst [displayed, setDisplayed] = useState(\"\");\n\tconst timerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\tuseEffect(() => {\n\t\tif (!active) {\n\t\t\t// Reset so the animation restarts fresh when re-activated\n\t\t\tsetDisplayed(\"\");\n\t\t\treturn;\n\t\t}\n\n\t\tlet i = 0;\n\t\tlet deleting = false;\n\t\tlet disposed = false;\n\n\t\tconst tick = () => {\n\t\t\tif (disposed) return;\n\n\t\t\tif (!deleting) {\n\t\t\t\t// Typing forward\n\t\t\t\ti++;\n\t\t\t\tsetDisplayed(text.slice(0, i));\n\n\t\t\t\tif (i >= text.length) {\n\t\t\t\t\t// Finished typing — pause then start deleting\n\t\t\t\t\tdeleting = true;\n\t\t\t\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_TYPE_MS);\n\t\t\t\t} else {\n\t\t\t\t\ttimerRef.current = setTimeout(tick, TYPE_SPEED_MS);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Deleting\n\t\t\t\ti--;\n\t\t\t\tsetDisplayed(text.slice(0, i));\n\n\t\t\t\tif (i <= 0) {\n\t\t\t\t\t// Finished deleting — pause then start typing again\n\t\t\t\t\tdeleting = false;\n\t\t\t\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_DELETE_MS);\n\t\t\t\t} else {\n\t\t\t\t\ttimerRef.current = setTimeout(tick, DELETE_SPEED_MS);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Start after a small delay\n\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_DELETE_MS);\n\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\tclearTimeout(timerRef.current);\n\t\t};\n\t}, [text, active]);\n\n\treturn displayed;\n}\n","import type { ChatTheme } from \"./@types\";\n\nexport const DEFAULT_THEME: Required<ChatTheme> = {\n\tprimaryColor: \"#6366f1\",\n\tprimaryForeground: \"#1f2937\",\n\tbackgroundColor: \"#ffffff\",\n\ttextColor: \"#1f2937\",\n\tmutedColor: \"#6b7280\",\n\tborderColor: \"#e5e7eb\",\n\tassistantBubbleColor: \"#f3f4f6\",\n\tuserBubbleColor: \"#f4f4f4\",\n\tinputBackgroundColor: \"#f9fafb\",\n\tborderRadius: 16,\n\tmessageBorderRadius: 12,\n\tfontFamily: \"system-ui, -apple-system, 'Segoe UI', sans-serif\",\n\theaderBackgroundColor: \"#ffffff\",\n\theaderTextColor: \"#1f2937\",\n\tstatusColor: \"#22c55e\",\n\ttoolCardColor: \"#f4f4f5\",\n};\n\nexport const DARK_THEME: ChatTheme = {\n\tbackgroundColor: \"#212121\",\n\theaderBackgroundColor: \"#1e1e1e\",\n\theaderTextColor: \"#ececec\",\n\ttextColor: \"#ececec\",\n\tprimaryForeground: \"#ffffff\",\n\tmutedColor: \"#8e8ea0\",\n\tborderColor: \"#444444\",\n\tassistantBubbleColor: \"#2f2f2f\",\n\tuserBubbleColor: \"#303030\",\n\tinputBackgroundColor: \"#2f2f2f\",\n\tprimaryColor: \"#6366f1\",\n\tstatusColor: \"#22c55e\",\n\ttoolCardColor: \"#262626\",\n};\n\nconst CSS_VAR_MAP: Record<keyof ChatTheme, string[]> = {\n\tprimaryColor: [\"--ww-primary\", \"--ww-color-primary\"],\n\tprimaryForeground: [\"--ww-primary-fg\", \"--ww-color-primary-foreground\"],\n\tbackgroundColor: [\"--ww-bg\", \"--ww-color-background\"],\n\ttextColor: [\n\t\t\"--ww-text\",\n\t\t\"--ww-color-foreground\",\n\t\t\"--ww-color-accent-foreground\",\n\t],\n\tmutedColor: [\"--ww-muted\", \"--ww-color-muted-foreground\"],\n\tborderColor: [\"--ww-border\", \"--ww-color-border\"],\n\tassistantBubbleColor: [\"--ww-assistant-bubble\", \"--ww-color-accent\"],\n\tuserBubbleColor: [\"--ww-user-bubble\"],\n\tinputBackgroundColor: [\"--ww-input-bg\", \"--ww-color-input\"],\n\tborderRadius: [\"--ww-radius\"],\n\tmessageBorderRadius: [\"--ww-msg-radius\"],\n\tfontFamily: [\"--ww-font\"],\n\theaderBackgroundColor: [\"--ww-header-bg\"],\n\theaderTextColor: [\"--ww-header-text\"],\n\tstatusColor: [\"--ww-status\"],\n\ttoolCardColor: [\"--ww-tool-card\", \"--ww-color-tool-card\"],\n};\n\nexport function mergeTheme(userTheme?: ChatTheme): Required<ChatTheme> {\n\treturn { ...DEFAULT_THEME, ...userTheme };\n}\n\nexport function isDarkTheme(theme: Required<ChatTheme>): boolean {\n\tconst hex = theme.backgroundColor.replace(\"#\", \"\");\n\tconst r = parseInt(hex.substring(0, 2), 16);\n\tconst g = parseInt(hex.substring(2, 4), 16);\n\tconst b = parseInt(hex.substring(4, 6), 16);\n\treturn (r * 299 + g * 587 + b * 114) / 1000 < 128;\n}\n\nexport function themeToCSSProperties(\n\ttheme: Required<ChatTheme>,\n): Record<string, string> {\n\tconst vars: Record<string, string> = {};\n\tfor (const [key, cssVars] of Object.entries(CSS_VAR_MAP)) {\n\t\tconst value = theme[key as keyof ChatTheme];\n\t\tconst resolved = typeof value === \"number\" ? `${value}px` : String(value);\n\t\tfor (const cssVar of cssVars) {\n\t\t\tvars[cssVar] = resolved;\n\t\t}\n\t}\n\treturn vars;\n}\n","\"use client\";\n\nimport {\n\tforwardRef,\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport type { ChatCardProps, ChatHandle } from \"../@types\";\nimport {\n\tConversation,\n\tConversationContent,\n\tConversationScrollButton,\n} from \"../ai-elements/conversation\";\nimport {\n\tPromptInput,\n\tPromptInputAddAttachments,\n\tPromptInputSubmit,\n\tPromptInputTextarea,\n} from \"../ai-elements/prompt-input\";\nimport { MessageList } from \"../components/message-list\";\nimport { Suggestions } from \"../components/suggestions\";\nimport { useChatEngine } from \"../hooks/use-chat-engine\";\nimport { useSuggestions } from \"../hooks/use-suggestions\";\nimport { useTypingPlaceholder } from \"../hooks/use-typing-placeholder\";\nimport { cn } from \"../lib/utils\";\nimport { isDarkTheme, mergeTheme, themeToCSSProperties } from \"../theme\";\n\nexport const ChatCard = forwardRef<ChatHandle, ChatCardProps>(\n\tfunction ChatCard(props, ref) {\n\t\tconst {\n\t\t\ttheme: userTheme,\n\t\t\ttitle = \"Assistant\",\n\t\t\tsubtitle,\n\t\t\tshowStatus = true,\n\t\t\twidth = 500,\n\t\t\theight = 600,\n\t\t\tallowAttachments = false,\n\t\t\twelcomeMessage,\n\t\t\tplaceholder = \"Ask me anything...\",\n\t\t\ttriggerEvent = \"triggerDemoRequest\",\n\t\t\tresourceEndpoint,\n\t\t\tapi,\n\t\t} = props;\n\n\t\tconst effectiveResourceEndpoint =\n\t\t\tresourceEndpoint ?? (api ? `${api}/resource` : undefined);\n\n\t\tconst resolvedTheme = mergeTheme(userTheme);\n\t\tconst cssVars = themeToCSSProperties(resolvedTheme);\n\t\tconst isDark = isDarkTheme(resolvedTheme);\n\n\t\tconst engine = useChatEngine(props);\n\n\t\tconst animatedPlaceholder = useTypingPlaceholder(placeholder, !engine.text);\n\n\t\tconst [isHighlighted, setIsHighlighted] = useState(false);\n\t\tconst cardRef = useRef<HTMLDivElement>(null);\n\t\tconst highlightTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\t\tconst focusInput = useCallback(() => {\n\t\t\tconst container = cardRef.current;\n\t\t\tif (!container) return;\n\t\t\tcontainer.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\t\tconst textarea = container.querySelector(\"textarea\");\n\t\t\tif (textarea) {\n\t\t\t\tsetTimeout(() => textarea.focus(), 300);\n\t\t\t}\n\t\t\tsetIsHighlighted(true);\n\t\t\tclearTimeout(highlightTimerRef.current);\n\t\t\thighlightTimerRef.current = setTimeout(\n\t\t\t\t() => setIsHighlighted(false),\n\t\t\t\t2000,\n\t\t\t);\n\t\t}, []);\n\n\t\tconst suggestionsState = useSuggestions({\n\t\t\tmessages: engine.messages,\n\t\t\tstatus: engine.status,\n\t\t\tconfig: props.suggestions,\n\t\t});\n\n\t\tconst handleWidgetMessage = useCallback(\n\t\t\t(message: {\n\t\t\t\trole: string;\n\t\t\t\tcontent: Array<{ type: string; text?: string }>;\n\t\t\t}) => {\n\t\t\t\tconst text = message.content\n\t\t\t\t\t.map((c) => c.text ?? \"\")\n\t\t\t\t\t.join(\"\")\n\t\t\t\t\t.trim();\n\t\t\t\tif (text) engine.handleSubmit({ text, files: [] });\n\t\t\t},\n\t\t\t[engine.handleSubmit],\n\t\t);\n\n\t\tconst handleSuggestionSelect = useCallback(\n\t\t\t(suggestion: string) => {\n\t\t\t\tsuggestionsState.clear();\n\t\t\t\tengine.handleSubmit({ text: suggestion, files: [] });\n\t\t\t},\n\t\t\t[suggestionsState.clear, engine.handleSubmit],\n\t\t);\n\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tsendMessage: (text: string) => {\n\t\t\t\t\tengine.handleSubmit({ text, files: [] });\n\t\t\t\t\tfocusInput();\n\t\t\t\t},\n\t\t\t\tfocus: focusInput,\n\t\t\t}),\n\t\t\t[engine.handleSubmit, focusInput],\n\t\t);\n\n\t\t// Listen for custom trigger event (e.g. \"triggerDemoRequest\")\n\t\tuseEffect(() => {\n\t\t\tif (!triggerEvent) return;\n\t\t\tconst handler = (e: Event) => {\n\t\t\t\tconst detail = (e as CustomEvent).detail;\n\t\t\t\tconst message =\n\t\t\t\t\ttypeof detail?.message === \"string\" ? detail.message : undefined;\n\t\t\t\tif (message) {\n\t\t\t\t\tengine.handleSubmit({ text: message, files: [] });\n\t\t\t\t}\n\t\t\t\tfocusInput();\n\t\t\t};\n\t\t\twindow.addEventListener(triggerEvent, handler);\n\t\t\treturn () => window.removeEventListener(triggerEvent, handler);\n\t\t}, [triggerEvent, engine.handleSubmit, focusInput]);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={cardRef}\n\t\t\t\tstyle={{ ...cssVars, width, height }}\n\t\t\t\tdata-waniwani-chat=\"\"\n\t\t\t\tdata-waniwani-layout=\"card\"\n\t\t\t\t{...(isDark ? { \"data-waniwani-dark\": \"\" } : {})}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground ww:bg-background ww:rounded-[var(--ww-radius)] ww:border ww:border-border ww:shadow-md ww:overflow-hidden ww:transition-shadow ww:duration-300\",\n\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{/* Header */}\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"ww:shrink-0 ww:flex ww:items-center ww:gap-3 ww:px-4 ww:py-2 ww:border-b ww:border-border\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackgroundColor: resolvedTheme.headerBackgroundColor,\n\t\t\t\t\t\tcolor: resolvedTheme.headerTextColor,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{showStatus && (\n\t\t\t\t\t\t<span className=\"ww:size-2.5 ww:rounded-full ww:bg-status\" />\n\t\t\t\t\t)}\n\t\t\t\t\t<div className=\"ww:flex-1 ww:min-w-0\">\n\t\t\t\t\t\t<div className=\"ww:text-xs ww:font-semibold ww:truncate\">\n\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{subtitle && (\n\t\t\t\t\t\t\t<div className=\"ww:text-[11px] ww:text-muted-foreground ww:truncate\">\n\t\t\t\t\t\t\t\t{subtitle}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Messages */}\n\t\t\t\t<Conversation className=\"ww:flex-1 ww:min-h-0 ww:bg-background\">\n\t\t\t\t\t<ConversationContent>\n\t\t\t\t\t\t<MessageList\n\t\t\t\t\t\t\tmessages={engine.messages}\n\t\t\t\t\t\t\tstatus={engine.status}\n\t\t\t\t\t\t\twelcomeMessage={welcomeMessage}\n\t\t\t\t\t\t\tresourceEndpoint={effectiveResourceEndpoint}\n\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\tonFollowUp={handleWidgetMessage}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</ConversationContent>\n\t\t\t\t\t<ConversationScrollButton />\n\t\t\t\t</Conversation>\n\n\t\t\t\t{/* Suggestions */}\n\t\t\t\t<Suggestions\n\t\t\t\t\tsuggestions={suggestionsState.suggestions}\n\t\t\t\t\tisLoading={suggestionsState.isLoading}\n\t\t\t\t\tonSelect={handleSuggestionSelect}\n\t\t\t\t\tclassName=\"ww:border-t ww:border-border\"\n\t\t\t\t/>\n\n\t\t\t\t{/* Input */}\n\t\t\t\t<div className=\"ww:shrink-0 ww:border-t ww:border-border ww:bg-background\">\n\t\t\t\t\t<PromptInput\n\t\t\t\t\t\tonSubmit={engine.handleSubmit}\n\t\t\t\t\t\tglobalDrop={allowAttachments}\n\t\t\t\t\t\tmultiple={allowAttachments}\n\t\t\t\t\t\tclassName={cn(\"ww:rounded-none ww:border-0\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2\">\n\t\t\t\t\t\t\t{allowAttachments && <PromptInputAddAttachments />}\n\t\t\t\t\t\t\t<PromptInputTextarea\n\t\t\t\t\t\t\t\tonChange={engine.handleTextChange}\n\t\t\t\t\t\t\t\tvalue={engine.text}\n\t\t\t\t\t\t\t\tplaceholder={animatedPlaceholder}\n\t\t\t\t\t\t\t\tclassName=\"ww:min-h-0 ww:py-1.5 ww:px-2\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<PromptInputSubmit status={engine.status} />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</PromptInput>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t},\n);\n"],"mappings":"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","names":["forwardRef","useCallback","useEffect","useImperativeHandle","useRef","useState","ArrowDownIcon","useCallback","StickToBottom","useStickToBottomContext","clsx","extendTailwindMerge","twMerge","cn","inputs","jsx","Button","className","variant","size","type","props","cn","jsx","Conversation","className","props","StickToBottom","cn","ConversationContent","ConversationScrollButton","isAtBottom","scrollToBottom","useStickToBottomContext","handleScrollToBottom","useCallback","Button","ArrowDownIcon","ArrowUpIcon","LoaderIcon","PaperclipIcon","SquareIcon","XIcon","nanoid","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","jsx","jsxs","convertBlobUrlToDataUrl","url","blob","resolve","reader","LocalAttachmentsContext","createContext","usePromptInputAttachments","context","useContext","PromptInput","className","accept","multiple","globalDrop","maxFiles","maxFileSize","onSubmit","children","props","inputRef","useRef","formRef","items","setItems","useState","filesRef","useEffect","openFileDialog","useCallback","add","fileList","incoming","withinSize","f","valid","prev","capacity","capped","file","nanoid","remove","id","found","clear","handleChange","event","onDragOver","e","onDrop","handleSubmit","form","text","convertedFiles","_id","item","dataUrl","result","attachmentsCtx","useMemo","cn","PromptInputTextarea","onChange","onKeyDown","className","placeholder","props","attachments","usePromptInputAttachments","isComposing","setIsComposing","useState","handleKeyDown","useCallback","e","form","lastAttachment","handlePaste","event","items","files","item","file","jsx","cn","PromptInputSubmit","status","onStop","onClick","children","isGenerating","Icon","ArrowUpIcon","LoaderIcon","SquareIcon","handleClick","Button","PromptInputAddAttachments","jsxs","XIcon","PaperclipIcon","FileIcon","jsx","jsxs","Attachments","files","className","props","cn","file","i","AttachmentItem","FileIcon","jsx","Loader","className","size","props","cn","i","cjk","code","memo","Streamdown","jsx","Message","className","from","props","cn","MessageContent","children","streamdownPlugins","cjk","code","MessageResponse","memo","Streamdown","prevProps","nextProps","jsx","Reasoning","className","text","props","cn","BracesIcon","CheckIcon","ChevronDownIcon","ChevronRightIcon","ClipboardCopyIcon","ServerIcon","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","Fragment","jsx","jsxs","truncateJSON","data","maxLength","stringified","entries","parts","remaining","key","value","keyAbbrev","valStr","part","CopyButton","text","className","copied","setCopied","useState","timeoutRef","useRef","useEffect","handleCopy","useCallback","e","Button","cn","CheckIcon","ClipboardCopyIcon","CollapsibleJSON","label","props","expanded","setExpanded","fullJSON","useMemo","preview","v","ChevronRightIcon","ToolOpenContext","createContext","Tool","defaultOpen","children","open","setOpen","o","ToolHeader","title","state","toggle","useContext","isRunning","BracesIcon","ChevronDownIcon","ToolContent","className","children","props","open","useContext","ToolOpenContext","jsx","cn","ToolInput","input","CollapsibleJSON","getUiMeta","output","meta","ui","getResourceUri","uri","getAutoHeight","ToolOutput","errorText","jsxs","useCallback","useEffect","useMemo","useRef","useState","jsx","DEFAULT_RESOURCE_ENDPOINT","MAX_HEIGHT","DEFAULT_HEIGHT","PROTOCOL_VERSION","RESIZE_ANIMATION_MS","HANDSHAKE_TIMEOUT_MS","MAX_RETRIES","McpAppFrame","resourceUri","toolInput","toolResult","resourceEndpoint","isDark","className","autoHeight","onOpenLink","onFollowUp","iframeRef","useRef","toolInputRef","toolResultRef","lastSizeRef","animationRef","initializedRef","retryCountRef","height","setHeight","useState","width","setWidth","onOpenLinkRef","onFollowUpRef","clampHeight","useCallback","h","iframeSrc","useMemo","isDarkRef","useEffect","iframe","disposed","handshakeReceived","debug","args","handshakeTimer","url","postToIframe","msg","handleMessage","event","data","method","id","input","result","content","params","newHeight","newWidth","last","heightChanged","widthChanged","clamped","from","anim","cn","Component","jsx","jsxs","WidgetErrorBoundary","error","Fragment","jsx","jsxs","formatToolName","name","c","MessageList","messages","status","welcomeMessage","resourceEndpoint","isDark","onFollowUp","isLoading","lastMessage","hasMessages","showLoaderBubble","Message","MessageContent","MessageResponse","message","textParts","p","reasoningParts","fileParts","toolParts","isLastAssistant","hasTextContent","part","i","Reasoning","output","resourceUri","getResourceUri","autoHeight","getAutoHeight","Tool","ToolHeader","ToolContent","ToolInput","ToolOutput","WidgetErrorBoundary","McpAppFrame","Attachments","Loader","jsx","Suggestions","suggestions","isLoading","onSelect","className","props","cn","i","suggestion","index","useChat","DefaultChatTransport","useCallback","useRef","useState","useChatEngine","props","api","userHeaders","body","onMessageSent","onResponseReceived","transportRef","messages","sendMessage","status","error","text","setText","handleSubmit","message","hasText","hasFiles","handleTextChange","e","isLoading","lastMessage","hasMessages","showLoaderBubble","useCallback","useEffect","useRef","useState","extractSuggestions","message","part","p","data","isConfigObject","config","useSuggestions","options","messages","status","suggestions","setSuggestions","prevStatusRef","isEnabled","clear","lastMessage","prevStatus","lastAssistant","m","extracted","useEffect","useRef","useState","TYPE_SPEED_MS","DELETE_SPEED_MS","PAUSE_AFTER_TYPE_MS","PAUSE_AFTER_DELETE_MS","useTypingPlaceholder","text","active","displayed","setDisplayed","timerRef","i","deleting","disposed","tick","DEFAULT_THEME","DARK_THEME","CSS_VAR_MAP","mergeTheme","userTheme","isDarkTheme","theme","hex","r","g","b","themeToCSSProperties","vars","key","cssVars","value","resolved","cssVar","jsx","jsxs","ChatBar","forwardRef","props","ref","userTheme","width","expandedHeight","allowAttachments","welcomeMessage","placeholder","triggerEvent","resourceEndpoint","api","effectiveResourceEndpoint","resolvedTheme","mergeTheme","cssVars","themeToCSSProperties","isDark","isDarkTheme","engine","useChatEngine","suggestionsState","useSuggestions","handleWidgetMessage","useCallback","message","text","c","handleSuggestionSelect","suggestion","animatedPlaceholder","useTypingPlaceholder","isFocused","setIsFocused","useState","isHighlighted","setIsHighlighted","containerRef","useRef","highlightTimerRef","focusInput","container","textarea","useImperativeHandle","useEffect","handler","e","detail","isExpanded","handleClickOutside","handleFocus","cn","Conversation","ConversationContent","MessageList","ConversationScrollButton","Suggestions","PromptInput","PromptInputAddAttachments","PromptInputTextarea","PromptInputSubmit","forwardRef","useCallback","useEffect","useImperativeHandle","useRef","useState","jsx","jsxs","ChatCard","forwardRef","props","ref","userTheme","title","subtitle","showStatus","width","height","allowAttachments","welcomeMessage","placeholder","triggerEvent","resourceEndpoint","api","effectiveResourceEndpoint","resolvedTheme","mergeTheme","cssVars","themeToCSSProperties","isDark","isDarkTheme","engine","useChatEngine","animatedPlaceholder","useTypingPlaceholder","isHighlighted","setIsHighlighted","useState","cardRef","useRef","highlightTimerRef","focusInput","useCallback","container","textarea","suggestionsState","useSuggestions","handleWidgetMessage","message","text","c","handleSuggestionSelect","suggestion","useImperativeHandle","useEffect","handler","e","detail","cn","Conversation","ConversationContent","MessageList","ConversationScrollButton","Suggestions","PromptInput","PromptInputAddAttachments","PromptInputTextarea","PromptInputSubmit"]}
|
|
1
|
+
{"version":3,"sources":["../../src/chat/web/layouts/chat-bar.tsx","../../src/chat/web/ai-elements/conversation.tsx","../../src/chat/web/lib/utils.ts","../../src/chat/web/ui/button.tsx","../../src/chat/web/ai-elements/prompt-input.tsx","../../src/chat/web/ai-elements/attachments.tsx","../../src/chat/web/ai-elements/loader.tsx","../../src/chat/web/ai-elements/message.tsx","../../src/chat/web/ai-elements/reasoning.tsx","../../src/chat/web/ai-elements/tool.tsx","../../src/chat/web/components/mcp-app-frame.tsx","../../src/chat/web/components/widget-error-boundary.tsx","../../src/chat/web/components/message-list.tsx","../../src/chat/web/components/suggestions.tsx","../../src/chat/web/hooks/use-chat-engine.ts","../../src/chat/web/hooks/use-suggestions.ts","../../src/chat/web/hooks/use-typing-placeholder.ts","../../src/chat/web/theme.ts","../../src/chat/web/layouts/chat-card.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tforwardRef,\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport type { ChatBarProps, ChatHandle } from \"../@types\";\nimport {\n\tConversation,\n\tConversationContent,\n\tConversationScrollButton,\n} from \"../ai-elements/conversation\";\nimport {\n\tPromptInput,\n\tPromptInputAddAttachments,\n\tPromptInputSubmit,\n\tPromptInputTextarea,\n} from \"../ai-elements/prompt-input\";\nimport { MessageList } from \"../components/message-list\";\nimport { Suggestions } from \"../components/suggestions\";\nimport { useChatEngine } from \"../hooks/use-chat-engine\";\nimport { useSuggestions } from \"../hooks/use-suggestions\";\nimport { useTypingPlaceholder } from \"../hooks/use-typing-placeholder\";\nimport { cn } from \"../lib/utils\";\nimport { isDarkTheme, mergeTheme, themeToCSSProperties } from \"../theme\";\n\nexport const ChatBar = forwardRef<ChatHandle, ChatBarProps>(\n\tfunction ChatBar(props, ref) {\n\t\tconst {\n\t\t\ttheme: userTheme,\n\t\t\twidth = 600,\n\t\t\texpandedHeight = 400,\n\t\t\tallowAttachments = false,\n\t\t\twelcomeMessage,\n\t\t\tplaceholder = \"Ask me anything...\",\n\t\t\ttriggerEvent = \"triggerDemoRequest\",\n\t\t\tresourceEndpoint,\n\t\t\tapi,\n\t\t} = props;\n\n\t\tconst effectiveResourceEndpoint =\n\t\t\tresourceEndpoint ?? (api ? `${api}/resource` : undefined);\n\n\t\tconst resolvedTheme = mergeTheme(userTheme);\n\t\tconst cssVars = themeToCSSProperties(resolvedTheme);\n\t\tconst isDark = isDarkTheme(resolvedTheme);\n\n\t\tconst engine = useChatEngine(props);\n\n\t\tconst suggestionsState = useSuggestions({\n\t\t\tmessages: engine.messages,\n\t\t\tstatus: engine.status,\n\t\t\tconfig: props.suggestions,\n\t\t});\n\n\t\tconst handleWidgetMessage = useCallback(\n\t\t\t(message: {\n\t\t\t\trole: string;\n\t\t\t\tcontent: Array<{ type: string; text?: string }>;\n\t\t\t}) => {\n\t\t\t\tconst text = message.content\n\t\t\t\t\t.map((c) => c.text ?? \"\")\n\t\t\t\t\t.join(\"\")\n\t\t\t\t\t.trim();\n\t\t\t\tif (text) engine.handleSubmit({ text, files: [] });\n\t\t\t},\n\t\t\t[engine.handleSubmit],\n\t\t);\n\n\t\tconst handleSuggestionSelect = useCallback(\n\t\t\t(suggestion: string) => {\n\t\t\t\tsuggestionsState.clear();\n\t\t\t\tengine.handleSubmit({ text: suggestion, files: [] });\n\t\t\t},\n\t\t\t[suggestionsState.clear, engine.handleSubmit],\n\t\t);\n\n\t\tconst animatedPlaceholder = useTypingPlaceholder(placeholder, !engine.text);\n\n\t\tconst [isFocused, setIsFocused] = useState(false);\n\t\tconst [isHighlighted, setIsHighlighted] = useState(false);\n\t\tconst containerRef = useRef<HTMLDivElement>(null);\n\t\tconst highlightTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\t\tconst focusInput = useCallback(() => {\n\t\t\tconst container = containerRef.current;\n\t\t\tif (!container) return;\n\t\t\tcontainer.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\t\tconst textarea = container.querySelector(\"textarea\");\n\t\t\tif (textarea) {\n\t\t\t\tsetTimeout(() => textarea.focus(), 300);\n\t\t\t}\n\t\t\tsetIsFocused(true);\n\t\t\tsetIsHighlighted(true);\n\t\t\tclearTimeout(highlightTimerRef.current);\n\t\t\thighlightTimerRef.current = setTimeout(\n\t\t\t\t() => setIsHighlighted(false),\n\t\t\t\t2000,\n\t\t\t);\n\t\t}, []);\n\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tsendMessage: (text: string) => {\n\t\t\t\t\tengine.handleSubmit({ text, files: [] });\n\t\t\t\t\tfocusInput();\n\t\t\t\t},\n\t\t\t\tfocus: focusInput,\n\t\t\t}),\n\t\t\t[engine.handleSubmit, focusInput],\n\t\t);\n\n\t\t// Listen for custom trigger event (e.g. \"triggerDemoRequest\")\n\t\tuseEffect(() => {\n\t\t\tif (!triggerEvent) return;\n\t\t\tconst handler = (e: Event) => {\n\t\t\t\tconst detail = (e as CustomEvent).detail;\n\t\t\t\tconst message =\n\t\t\t\t\ttypeof detail?.message === \"string\" ? detail.message : undefined;\n\t\t\t\tif (message) {\n\t\t\t\t\tengine.handleSubmit({ text: message, files: [] });\n\t\t\t\t}\n\t\t\t\tfocusInput();\n\t\t\t};\n\t\t\twindow.addEventListener(triggerEvent, handler);\n\t\t\treturn () => window.removeEventListener(triggerEvent, handler);\n\t\t}, [triggerEvent, engine.handleSubmit, focusInput]);\n\t\tconst isExpanded = isFocused;\n\n\t\t// Close on outside click\n\t\tuseEffect(() => {\n\t\t\tif (!isFocused) return;\n\t\t\tconst handleClickOutside = (e: MouseEvent) => {\n\t\t\t\tif (\n\t\t\t\t\tcontainerRef.current &&\n\t\t\t\t\t!containerRef.current.contains(e.target as Node)\n\t\t\t\t) {\n\t\t\t\t\tsetIsFocused(false);\n\t\t\t\t}\n\t\t\t};\n\t\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\t\treturn () =>\n\t\t\t\tdocument.removeEventListener(\"mousedown\", handleClickOutside);\n\t\t}, [isFocused]);\n\n\t\tconst handleFocus = useCallback(() => {\n\t\t\tsetIsFocused(true);\n\t\t}, []);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\tstyle={{ ...cssVars, width }}\n\t\t\t\tdata-waniwani-chat=\"\"\n\t\t\t\tdata-waniwani-layout=\"bar\"\n\t\t\t\t{...(isDark ? { \"data-waniwani-dark\": \"\" } : {})}\n\t\t\t\tclassName=\"ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground\"\n\t\t\t>\n\t\t\t\t{/* Messages panel — fades up on expand */}\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:overflow-hidden ww:bg-background/80 ww:backdrop-blur-xl ww:transition-all ww:duration-300 ww:ease-out\",\n\t\t\t\t\t\tisExpanded\n\t\t\t\t\t\t\t? \"ww:opacity-100 ww:translate-y-0\"\n\t\t\t\t\t\t\t: \"ww:opacity-0 ww:translate-y-2 ww:pointer-events-none ww:max-h-0\",\n\t\t\t\t\t)}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\t...(isExpanded ? { maxHeight: expandedHeight } : undefined),\n\t\t\t\t\t\tmaskImage:\n\t\t\t\t\t\t\t\"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)\",\n\t\t\t\t\t\tmaskComposite: \"intersect\",\n\t\t\t\t\t\tWebkitMaskImage:\n\t\t\t\t\t\t\t\"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)\",\n\t\t\t\t\t\tWebkitMaskComposite: \"source-in\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<Conversation\n\t\t\t\t\t\tclassName=\"ww:flex-1\"\n\t\t\t\t\t\tstyle={{ height: expandedHeight }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ConversationContent>\n\t\t\t\t\t\t\t<MessageList\n\t\t\t\t\t\t\t\tmessages={engine.messages}\n\t\t\t\t\t\t\t\tstatus={engine.status}\n\t\t\t\t\t\t\t\twelcomeMessage={welcomeMessage}\n\t\t\t\t\t\t\t\tresourceEndpoint={effectiveResourceEndpoint}\n\t\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\t\tonFollowUp={handleWidgetMessage}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</ConversationContent>\n\t\t\t\t\t\t<ConversationScrollButton />\n\t\t\t\t\t</Conversation>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Suggestions */}\n\t\t\t\t<Suggestions\n\t\t\t\t\tsuggestions={suggestionsState.suggestions}\n\t\t\t\t\tisLoading={suggestionsState.isLoading}\n\t\t\t\t\tonSelect={handleSuggestionSelect}\n\t\t\t\t/>\n\n\t\t\t\t{/* Input bar — always visible */}\n\t\t\t\t<div className=\"ww:shrink-0\">\n\t\t\t\t\t<PromptInput\n\t\t\t\t\t\tonSubmit={engine.handleSubmit}\n\t\t\t\t\t\tglobalDrop={allowAttachments}\n\t\t\t\t\t\tmultiple={allowAttachments}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"ww:rounded-[var(--ww-radius)] ww:shadow-sm ww:transition-all ww:duration-300 ww:ease-out\",\n\t\t\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\t\t\"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2\">\n\t\t\t\t\t\t\t{allowAttachments && <PromptInputAddAttachments />}\n\t\t\t\t\t\t\t<PromptInputTextarea\n\t\t\t\t\t\t\t\tonChange={engine.handleTextChange}\n\t\t\t\t\t\t\t\tvalue={engine.text}\n\t\t\t\t\t\t\t\tplaceholder={animatedPlaceholder}\n\t\t\t\t\t\t\t\tonFocus={handleFocus}\n\t\t\t\t\t\t\t\tclassName=\"ww:min-h-0 ww:py-1.5 ww:px-2\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<PromptInputSubmit status={engine.status} />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</PromptInput>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t},\n);\n","\"use client\";\n\nimport { ArrowDownIcon } from \"lucide-react\";\nimport type { ComponentProps } from \"react\";\nimport { useCallback } from \"react\";\nimport { StickToBottom, useStickToBottomContext } from \"use-stick-to-bottom\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\nexport type ConversationProps = ComponentProps<typeof StickToBottom>;\n\nexport const Conversation = ({ className, ...props }: ConversationProps) => (\n\t<StickToBottom\n\t\tclassName={cn(\"ww:relative ww:flex-1 ww:overflow-y-hidden\", className)}\n\t\tinitial=\"smooth\"\n\t\tresize=\"smooth\"\n\t\trole=\"log\"\n\t\t{...props}\n\t/>\n);\n\nexport type ConversationContentProps = ComponentProps<\n\ttypeof StickToBottom.Content\n>;\n\nexport const ConversationContent = ({\n\tclassName,\n\t...props\n}: ConversationContentProps) => (\n\t<StickToBottom.Content\n\t\tclassName={cn(\"ww:flex ww:flex-col ww:gap-8 ww:p-4\", className)}\n\t\t{...props}\n\t/>\n);\n\nexport type ConversationScrollButtonProps = ComponentProps<typeof Button>;\n\nexport const ConversationScrollButton = ({\n\tclassName,\n\t...props\n}: ConversationScrollButtonProps) => {\n\tconst { isAtBottom, scrollToBottom } = useStickToBottomContext();\n\n\tconst handleScrollToBottom = useCallback(() => {\n\t\tscrollToBottom();\n\t}, [scrollToBottom]);\n\n\treturn (\n\t\t!isAtBottom && (\n\t\t\t<Button\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:absolute ww:bottom-4 ww:left-[50%] ww:translate-x-[-50%] ww:rounded-full\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonClick={handleScrollToBottom}\n\t\t\t\tsize=\"icon\"\n\t\t\t\tvariant=\"outline\"\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<ArrowDownIcon className=\"ww:size-4\" />\n\t\t\t</Button>\n\t\t)\n\t);\n};\n","import { type ClassValue, clsx } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\nconst twMerge = extendTailwindMerge({ prefix: \"ww\" });\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type ButtonProps = ComponentProps<\"button\"> & {\n\tvariant?: \"default\" | \"outline\" | \"ghost\";\n\tsize?: \"default\" | \"sm\" | \"icon\" | \"icon-sm\";\n};\n\nexport const Button = ({\n\tclassName,\n\tvariant = \"default\",\n\tsize = \"default\",\n\ttype = \"button\",\n\t...props\n}: ButtonProps) => (\n\t<button\n\t\ttype={type}\n\t\tclassName={cn(\n\t\t\t\"ww:inline-flex ww:cursor-pointer ww:items-center ww:justify-center ww:rounded-md ww:font-medium ww:transition-colors ww:disabled:pointer-events-none ww:disabled:opacity-50\",\n\t\t\tvariant === \"default\" &&\n\t\t\t\t\"ww:bg-primary ww:text-primary-foreground ww:hover:bg-primary/90\",\n\t\t\tvariant === \"outline\" &&\n\t\t\t\t\"ww:border ww:border-border ww:bg-background ww:hover:bg-accent ww:hover:text-accent-foreground\",\n\t\t\tvariant === \"ghost\" &&\n\t\t\t\t\"ww:hover:bg-accent ww:hover:text-accent-foreground\",\n\t\t\tsize === \"default\" && \"ww:h-9 ww:px-4 ww:py-2 ww:text-sm\",\n\t\t\tsize === \"sm\" && \"ww:h-8 ww:px-3 ww:text-xs\",\n\t\t\tsize === \"icon\" && \"ww:size-9\",\n\t\t\tsize === \"icon-sm\" && \"ww:size-7\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n","\"use client\";\n\nimport type { ChatStatus, FileUIPart } from \"ai\";\nimport {\n\tArrowUpIcon,\n\tLoaderIcon,\n\tPaperclipIcon,\n\tSquareIcon,\n\tXIcon,\n} from \"lucide-react\";\nimport { nanoid } from \"nanoid\";\nimport type {\n\tChangeEvent,\n\tClipboardEventHandler,\n\tComponentProps,\n\tFormEvent,\n\tFormEventHandler,\n\tHTMLAttributes,\n\tKeyboardEventHandler,\n} from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst convertBlobUrlToDataUrl = async (url: string): Promise<string | null> => {\n\ttry {\n\t\tconst response = await fetch(url);\n\t\tconst blob = await response.blob();\n\t\treturn new Promise((resolve) => {\n\t\t\tconst reader = new FileReader();\n\t\t\treader.onloadend = () => resolve(reader.result as string);\n\t\t\treader.onerror = () => resolve(null);\n\t\t\treader.readAsDataURL(blob);\n\t\t});\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// ============================================================================\n// Attachments Context\n// ============================================================================\n\nexport interface AttachmentsContext {\n\tfiles: (FileUIPart & { id: string })[];\n\tadd: (files: File[] | FileList) => void;\n\tremove: (id: string) => void;\n\tclear: () => void;\n\topenFileDialog: () => void;\n}\n\nconst LocalAttachmentsContext = createContext<AttachmentsContext | null>(null);\n\nexport const usePromptInputAttachments = () => {\n\tconst context = useContext(LocalAttachmentsContext);\n\tif (!context) {\n\t\tthrow new Error(\n\t\t\t\"usePromptInputAttachments must be used within a PromptInput\",\n\t\t);\n\t}\n\treturn context;\n};\n\n// ============================================================================\n// PromptInput Message Type\n// ============================================================================\n\nexport interface PromptInputMessage {\n\ttext: string;\n\tfiles: FileUIPart[];\n}\n\n// ============================================================================\n// PromptInput\n// ============================================================================\n\nexport type PromptInputProps = Omit<\n\tHTMLAttributes<HTMLFormElement>,\n\t\"onSubmit\"\n> & {\n\taccept?: string;\n\tmultiple?: boolean;\n\tglobalDrop?: boolean;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tonSubmit: (\n\t\tmessage: PromptInputMessage,\n\t\tevent: FormEvent<HTMLFormElement>,\n\t) => void | Promise<void>;\n};\n\nexport const PromptInput = ({\n\tclassName,\n\taccept,\n\tmultiple,\n\tglobalDrop,\n\tmaxFiles,\n\tmaxFileSize,\n\tonSubmit,\n\tchildren,\n\t...props\n}: PromptInputProps) => {\n\tconst inputRef = useRef<HTMLInputElement | null>(null);\n\tconst formRef = useRef<HTMLFormElement | null>(null);\n\tconst [items, setItems] = useState<(FileUIPart & { id: string })[]>([]);\n\tconst filesRef = useRef(items);\n\n\tuseEffect(() => {\n\t\tfilesRef.current = items;\n\t}, [items]);\n\n\tconst openFileDialog = useCallback(() => {\n\t\tinputRef.current?.click();\n\t}, []);\n\n\tconst add = useCallback(\n\t\t(fileList: File[] | FileList) => {\n\t\t\tconst incoming = [...fileList];\n\t\t\tif (incoming.length === 0) return;\n\n\t\t\tconst withinSize = (f: File) =>\n\t\t\t\tmaxFileSize ? f.size <= maxFileSize : true;\n\t\t\tconst valid = incoming.filter(withinSize);\n\n\t\t\tsetItems((prev) => {\n\t\t\t\tconst capacity =\n\t\t\t\t\ttypeof maxFiles === \"number\"\n\t\t\t\t\t\t? Math.max(0, maxFiles - prev.length)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst capped =\n\t\t\t\t\ttypeof capacity === \"number\" ? valid.slice(0, capacity) : valid;\n\t\t\t\treturn [\n\t\t\t\t\t...prev,\n\t\t\t\t\t...capped.map((file) => ({\n\t\t\t\t\t\tfilename: file.name,\n\t\t\t\t\t\tid: nanoid(),\n\t\t\t\t\t\tmediaType: file.type,\n\t\t\t\t\t\ttype: \"file\" as const,\n\t\t\t\t\t\turl: URL.createObjectURL(file),\n\t\t\t\t\t})),\n\t\t\t\t];\n\t\t\t});\n\t\t},\n\t\t[maxFiles, maxFileSize],\n\t);\n\n\tconst remove = useCallback((id: string) => {\n\t\tsetItems((prev) => {\n\t\t\tconst found = prev.find((f) => f.id === id);\n\t\t\tif (found?.url) URL.revokeObjectURL(found.url);\n\t\t\treturn prev.filter((f) => f.id !== id);\n\t\t});\n\t}, []);\n\n\tconst clear = useCallback(() => {\n\t\tsetItems((prev) => {\n\t\t\tfor (const f of prev) {\n\t\t\t\tif (f.url) URL.revokeObjectURL(f.url);\n\t\t\t}\n\t\t\treturn [];\n\t\t});\n\t}, []);\n\n\t// Cleanup blob URLs on unmount\n\tuseEffect(\n\t\t() => () => {\n\t\t\tfor (const f of filesRef.current) {\n\t\t\t\tif (f.url) URL.revokeObjectURL(f.url);\n\t\t\t}\n\t\t},\n\t\t[],\n\t);\n\n\tconst handleChange = useCallback(\n\t\t(event: ChangeEvent<HTMLInputElement>) => {\n\t\t\tif (event.currentTarget.files) {\n\t\t\t\tadd(event.currentTarget.files);\n\t\t\t}\n\t\t\tevent.currentTarget.value = \"\";\n\t\t},\n\t\t[add],\n\t);\n\n\t// Global drop support\n\tuseEffect(() => {\n\t\tif (!globalDrop) return;\n\t\tconst onDragOver = (e: DragEvent) => {\n\t\t\tif (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n\t\t};\n\t\tconst onDrop = (e: DragEvent) => {\n\t\t\tif (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n\t\t\tif (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {\n\t\t\t\tadd(e.dataTransfer.files);\n\t\t\t}\n\t\t};\n\t\tdocument.addEventListener(\"dragover\", onDragOver);\n\t\tdocument.addEventListener(\"drop\", onDrop);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"dragover\", onDragOver);\n\t\t\tdocument.removeEventListener(\"drop\", onDrop);\n\t\t};\n\t}, [add, globalDrop]);\n\n\tconst handleSubmit: FormEventHandler<HTMLFormElement> = useCallback(\n\t\tasync (event) => {\n\t\t\tevent.preventDefault();\n\t\t\tconst form = event.currentTarget;\n\t\t\tconst formData = new FormData(form);\n\t\t\tconst text = (formData.get(\"message\") as string) || \"\";\n\n\t\t\tform.reset();\n\n\t\t\tconst convertedFiles: FileUIPart[] = await Promise.all(\n\t\t\t\titems.map(async ({ id: _id, ...item }) => {\n\t\t\t\t\tif (item.url?.startsWith(\"blob:\")) {\n\t\t\t\t\t\tconst dataUrl = await convertBlobUrlToDataUrl(item.url);\n\t\t\t\t\t\treturn { ...item, url: dataUrl ?? item.url };\n\t\t\t\t\t}\n\t\t\t\t\treturn item;\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\ttry {\n\t\t\t\tconst result = onSubmit({ files: convertedFiles, text }, event);\n\t\t\t\tif (result instanceof Promise) {\n\t\t\t\t\tawait result;\n\t\t\t\t}\n\t\t\t\tclear();\n\t\t\t} catch {\n\t\t\t\t// Don't clear on error\n\t\t\t}\n\t\t},\n\t\t[items, onSubmit, clear],\n\t);\n\n\tconst attachmentsCtx = useMemo<AttachmentsContext>(\n\t\t() => ({\n\t\t\tadd,\n\t\t\tclear,\n\t\t\tfiles: items,\n\t\t\topenFileDialog,\n\t\t\tremove,\n\t\t}),\n\t\t[items, add, remove, clear, openFileDialog],\n\t);\n\n\treturn (\n\t\t<LocalAttachmentsContext.Provider value={attachmentsCtx}>\n\t\t\t<input\n\t\t\t\taccept={accept}\n\t\t\t\taria-label=\"Upload files\"\n\t\t\t\tclassName=\"ww:hidden\"\n\t\t\t\tmultiple={multiple}\n\t\t\t\tonChange={handleChange}\n\t\t\t\tref={inputRef}\n\t\t\t\ttitle=\"Upload files\"\n\t\t\t\ttype=\"file\"\n\t\t\t/>\n\t\t\t<form\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:flex ww:w-full ww:flex-col ww:rounded-lg ww:border ww:border-border ww:bg-background\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\tref={formRef}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</form>\n\t\t</LocalAttachmentsContext.Provider>\n\t);\n};\n\n// ============================================================================\n// Layout Components\n// ============================================================================\n\nexport type PromptInputHeaderProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputHeader = ({\n\tclassName,\n\t...props\n}: PromptInputHeaderProps) => (\n\t<div\n\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-1 ww:px-3 ww:pt-3\", className)}\n\t\t{...props}\n\t/>\n);\n\nexport type PromptInputBodyProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputBody = ({\n\tclassName,\n\t...props\n}: PromptInputBodyProps) => (\n\t<div className={cn(\"ww:contents\", className)} {...props} />\n);\n\nexport type PromptInputFooterProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputFooter = ({\n\tclassName,\n\t...props\n}: PromptInputFooterProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:flex ww:items-center ww:justify-between ww:gap-1 ww:px-3 ww:pb-3\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputTools = ({\n\tclassName,\n\t...props\n}: PromptInputToolsProps) => (\n\t<div\n\t\tclassName={cn(\"ww:flex ww:min-w-0 ww:items-center ww:gap-1\", className)}\n\t\t{...props}\n\t/>\n);\n\n// ============================================================================\n// Textarea\n// ============================================================================\n\nexport type PromptInputTextareaProps = ComponentProps<\"textarea\">;\n\nexport const PromptInputTextarea = ({\n\tonChange,\n\tonKeyDown,\n\tclassName,\n\tplaceholder = \"What would you like to know?\",\n\t...props\n}: PromptInputTextareaProps) => {\n\tconst attachments = usePromptInputAttachments();\n\tconst [isComposing, setIsComposing] = useState(false);\n\n\tconst handleKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = useCallback(\n\t\t(e) => {\n\t\t\tonKeyDown?.(e);\n\t\t\tif (e.defaultPrevented) return;\n\n\t\t\tif (e.key === \"Enter\") {\n\t\t\t\tif (isComposing || e.nativeEvent.isComposing) return;\n\t\t\t\tif (e.shiftKey) return;\n\t\t\t\te.preventDefault();\n\n\t\t\t\tconst { form } = e.currentTarget;\n\t\t\t\tconst submitButton = form?.querySelector(\n\t\t\t\t\t'button[type=\"submit\"]',\n\t\t\t\t) as HTMLButtonElement | null;\n\t\t\t\tif (submitButton?.disabled) return;\n\t\t\t\tform?.requestSubmit();\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\te.key === \"Backspace\" &&\n\t\t\t\te.currentTarget.value === \"\" &&\n\t\t\t\tattachments.files.length > 0\n\t\t\t) {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst lastAttachment = attachments.files.at(-1);\n\t\t\t\tif (lastAttachment) attachments.remove(lastAttachment.id);\n\t\t\t}\n\t\t},\n\t\t[onKeyDown, isComposing, attachments],\n\t);\n\n\tconst handlePaste: ClipboardEventHandler<HTMLTextAreaElement> = useCallback(\n\t\t(event) => {\n\t\t\tconst items = event.clipboardData?.items;\n\t\t\tif (!items) return;\n\n\t\t\tconst files: File[] = [];\n\t\t\tfor (const item of items) {\n\t\t\t\tif (item.kind === \"file\") {\n\t\t\t\t\tconst file = item.getAsFile();\n\t\t\t\t\tif (file) files.push(file);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (files.length > 0) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tattachments.add(files);\n\t\t\t}\n\t\t},\n\t\t[attachments],\n\t);\n\n\treturn (\n\t\t<textarea\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:field-sizing-content ww:max-h-48 ww:min-h-16 ww:w-full ww:resize-none ww:border-0 ww:bg-transparent ww:px-3 ww:py-3 ww:text-sm ww:outline-none ww:placeholder:text-muted-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tname=\"message\"\n\t\t\tonCompositionEnd={() => setIsComposing(false)}\n\t\t\tonCompositionStart={() => setIsComposing(true)}\n\t\t\tonKeyDown={handleKeyDown}\n\t\t\tonPaste={handlePaste}\n\t\t\tplaceholder={placeholder}\n\t\t\tonChange={onChange}\n\t\t\t{...props}\n\t\t/>\n\t);\n};\n\n// ============================================================================\n// Submit Button\n// ============================================================================\n\nexport type PromptInputSubmitProps = ComponentProps<typeof Button> & {\n\tstatus?: ChatStatus;\n\tonStop?: () => void;\n};\n\nexport const PromptInputSubmit = ({\n\tclassName,\n\tstatus,\n\tonStop,\n\tonClick,\n\tchildren,\n\t...props\n}: PromptInputSubmitProps) => {\n\tconst isGenerating = status === \"submitted\" || status === \"streaming\";\n\n\tlet Icon = <ArrowUpIcon className=\"ww:size-4\" />;\n\tif (status === \"submitted\") {\n\t\tIcon = <LoaderIcon className=\"ww:size-4 ww:animate-spin\" />;\n\t} else if (status === \"streaming\") {\n\t\tIcon = <SquareIcon className=\"ww:size-4\" />;\n\t}\n\n\tconst handleClick = useCallback(\n\t\t(e: React.MouseEvent<HTMLButtonElement>) => {\n\t\t\tif (isGenerating && onStop) {\n\t\t\t\te.preventDefault();\n\t\t\t\tonStop();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tonClick?.(e);\n\t\t},\n\t\t[isGenerating, onStop, onClick],\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\taria-label={isGenerating ? \"Stop\" : \"Submit\"}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:bg-foreground ww:text-background ww:hover:bg-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tonClick={handleClick}\n\t\t\tsize=\"icon-sm\"\n\t\t\ttype={isGenerating && onStop ? \"button\" : \"submit\"}\n\t\t\tvariant=\"ghost\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{children ?? Icon}\n\t\t</Button>\n\t);\n};\n\n// ============================================================================\n// Attachment Add Button (simple file picker, no Radix dropdown)\n// ============================================================================\n\nexport type PromptInputAddAttachmentsProps = ComponentProps<typeof Button>;\n\nexport const PromptInputAddAttachments = ({\n\tclassName,\n\tchildren,\n\t...props\n}: PromptInputAddAttachmentsProps) => {\n\tconst attachments = usePromptInputAttachments();\n\tconst hasFiles = attachments.files.length > 0;\n\n\tif (hasFiles) {\n\t\treturn (\n\t\t\t<Button\n\t\t\t\tclassName={cn(\"ww:group ww:relative\", className)}\n\t\t\t\tonClick={() => attachments.clear()}\n\t\t\t\tsize=\"icon-sm\"\n\t\t\t\ttype=\"button\"\n\t\t\t\tvariant=\"ghost\"\n\t\t\t\taria-label=\"Remove all attachments\"\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<span className=\"ww:flex ww:size-5 ww:items-center ww:justify-center ww:rounded-full ww:bg-primary ww:text-[10px] ww:font-medium ww:text-primary-foreground ww:transition-opacity ww:group-hover:opacity-0\">\n\t\t\t\t\t{attachments.files.length}\n\t\t\t\t</span>\n\t\t\t\t<XIcon className=\"ww:absolute ww:size-4 ww:opacity-0 ww:transition-opacity ww:group-hover:opacity-100\" />\n\t\t\t</Button>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Button\n\t\t\tclassName={cn(className)}\n\t\t\tonClick={() => attachments.openFileDialog()}\n\t\t\tsize=\"icon-sm\"\n\t\t\ttype=\"button\"\n\t\t\tvariant=\"ghost\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{children ?? <PaperclipIcon className=\"ww:size-4\" />}\n\t\t</Button>\n\t);\n};\n","\"use client\";\n\nimport type { FileUIPart } from \"ai\";\nimport { FileIcon } from \"lucide-react\";\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\n// ============================================================================\n// Attachments (inline list for chat bubbles)\n// ============================================================================\n\nexport type AttachmentsProps = HTMLAttributes<HTMLDivElement> & {\n\tfiles: FileUIPart[];\n};\n\nexport const Attachments = ({\n\tfiles,\n\tclassName,\n\t...props\n}: AttachmentsProps) => {\n\tif (files.length === 0) return null;\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-1.5\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{files.map((file, i) => (\n\t\t\t\t<AttachmentItem key={i} file={file} />\n\t\t\t))}\n\t\t</div>\n\t);\n};\n\n// ============================================================================\n// AttachmentItem\n// ============================================================================\n\nfunction AttachmentItem({ file }: { file: FileUIPart }) {\n\tconst isImage = file.mediaType?.startsWith(\"image/\");\n\n\tif (isImage && file.url) {\n\t\treturn (\n\t\t\t<img\n\t\t\t\tsrc={file.url}\n\t\t\t\talt={file.filename ?? \"attachment\"}\n\t\t\t\tclassName=\"ww:h-16 ww:max-w-32 ww:rounded ww:object-cover\"\n\t\t\t/>\n\t\t);\n\t}\n\n\treturn (\n\t\t<span className=\"ww:inline-flex ww:items-center ww:gap-1.5 ww:rounded ww:bg-background/20 ww:px-2 ww:py-1 ww:text-xs\">\n\t\t\t<FileIcon className=\"ww:size-3 ww:shrink-0\" />\n\t\t\t<span className=\"ww:max-w-24 ww:truncate\">{file.filename ?? \"file\"}</span>\n\t\t</span>\n\t);\n}\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type LoaderProps = HTMLAttributes<HTMLDivElement> & {\n\tsize?: number;\n};\n\nexport const Loader = ({ className, size = 5, ...props }: LoaderProps) => (\n\t<div className={cn(\"ww:flex ww:items-center ww:gap-1\", className)} {...props}>\n\t\t{[0, 1, 2].map((i) => (\n\t\t\t<div\n\t\t\t\tkey={i}\n\t\t\t\tclassName=\"ww:rounded-full ww:bg-muted-foreground/60\"\n\t\t\t\tstyle={{\n\t\t\t\t\twidth: size,\n\t\t\t\t\theight: size,\n\t\t\t\t\tanimation: \"ww-pulse 1.4s ease-in-out infinite\",\n\t\t\t\t\tanimationDelay: `${i * 0.2}s`,\n\t\t\t\t}}\n\t\t\t/>\n\t\t))}\n\t</div>\n);\n","\"use client\";\n\nimport { cjk } from \"@streamdown/cjk\";\nimport { code } from \"@streamdown/code\";\nimport type { UIMessage } from \"ai\";\nimport type { ComponentProps, HTMLAttributes } from \"react\";\nimport { memo } from \"react\";\nimport { Streamdown } from \"streamdown\";\nimport { cn } from \"../lib/utils\";\n\nexport type MessageProps = HTMLAttributes<HTMLDivElement> & {\n\tfrom: UIMessage[\"role\"];\n};\n\nexport const Message = ({ className, from, ...props }: MessageProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:group ww:flex ww:w-full ww:max-w-[95%] ww:flex-col ww:gap-2\",\n\t\t\tfrom === \"user\" ? \"is-user ww:ml-auto ww:justify-end\" : \"is-assistant\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type MessageContentProps = HTMLAttributes<HTMLDivElement>;\n\nexport const MessageContent = ({\n\tchildren,\n\tclassName,\n\t...props\n}: MessageContentProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:flex ww:w-fit ww:min-w-0 ww:max-w-full ww:flex-col ww:gap-2 ww:overflow-hidden ww:text-base\",\n\t\t\t\"ww:group-[.is-user]:ml-auto ww:group-[.is-user]:rounded-lg ww:group-[.is-user]:bg-user-bubble ww:group-[.is-user]:px-4 ww:group-[.is-user]:py-3 ww:group-[.is-user]:text-primary-foreground\",\n\t\t\t\"ww:group-[.is-assistant]:text-foreground\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t>\n\t\t{children}\n\t</div>\n);\n\nexport type MessageResponseProps = ComponentProps<typeof Streamdown>;\n\nconst streamdownPlugins = { cjk, code };\n\nexport const MessageResponse = memo(\n\t({ className, ...props }: MessageResponseProps) => (\n\t\t<Streamdown\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:size-full ww:[&>*:first-child]:mt-0 ww:[&>*:last-child]:mb-0\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tplugins={streamdownPlugins}\n\t\t\t{...props}\n\t\t/>\n\t),\n\t(prevProps, nextProps) => prevProps.children === nextProps.children,\n);\n\nMessageResponse.displayName = \"MessageResponse\";\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type ReasoningProps = HTMLAttributes<HTMLPreElement> & {\n\ttext: string;\n};\n\n/** Displays reasoning text inline with muted styling. */\nexport function Reasoning({ className, text, ...props }: ReasoningProps) {\n\tif (!text) return null;\n\n\treturn (\n\t\t<pre\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:mb-2 ww:overflow-x-auto ww:whitespace-pre-wrap ww:break-words ww:text-xs ww:font-mono ww:text-muted-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{text}\n\t\t</pre>\n\t);\n}\n","\"use client\";\n\nimport type { ToolUIPart } from \"ai\";\nimport {\n\tBracesIcon,\n\tCheckIcon,\n\tChevronDownIcon,\n\tChevronRightIcon,\n\tClipboardCopyIcon,\n\tServerIcon,\n} from \"lucide-react\";\nimport type { HTMLAttributes } from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\n/**\n * Produces an abbreviated single-line JSON preview for collapsed display.\n * Objects show truncated keys/values: `{ci… 'M…, pos… '2…, squa… 80}`\n * Arrays show their length: `Array(13)`\n */\nfunction truncateJSON(data: unknown, maxLength = 80): string {\n\tif (data === null || data === undefined) return String(data);\n\tif (typeof data !== \"object\") return String(data).slice(0, maxLength);\n\n\tif (Array.isArray(data)) {\n\t\treturn `Array(${data.length})`;\n\t}\n\n\tconst stringified = JSON.stringify(data);\n\tif (stringified.length <= maxLength) return stringified;\n\n\tconst entries = Object.entries(data as Record<string, unknown>);\n\tconst parts: string[] = [];\n\tlet remaining = maxLength - 2;\n\n\tfor (const [key, value] of entries) {\n\t\tif (remaining <= 8) break;\n\t\tconst keyAbbrev = key.length > 4 ? `${key.slice(0, 4)}\\u2026` : key;\n\t\tlet valStr: string;\n\t\tif (typeof value === \"string\") {\n\t\t\tvalStr = value.length > 2 ? `'${value.slice(0, 1)}\\u2026` : `'${value}'`;\n\t\t} else if (Array.isArray(value)) {\n\t\t\tvalStr = `Array(${value.length})`;\n\t\t} else if (typeof value === \"object\" && value !== null) {\n\t\t\tvalStr = `{\\u2026}`;\n\t\t} else {\n\t\t\tvalStr = String(value);\n\t\t}\n\t\tconst part = `${keyAbbrev}\\u2009${valStr}`;\n\t\tparts.push(part);\n\t\tremaining -= part.length + 3;\n\t}\n\n\treturn `{${parts.join(\", \")}}`;\n}\n\ninterface CopyButtonProps {\n\ttext: string;\n\tclassName?: string;\n}\n\n/** Ghost button that copies `text` to clipboard, showing \"Copied\" for 2s. */\nfunction CopyButton({ text, className }: CopyButtonProps) {\n\tconst [copied, setCopied] = useState(false);\n\tconst timeoutRef = useRef<ReturnType<typeof setTimeout>>(null);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (timeoutRef.current) {\n\t\t\t\tclearTimeout(timeoutRef.current);\n\t\t\t}\n\t\t};\n\t}, []);\n\n\tconst handleCopy = useCallback(\n\t\tasync (e: React.MouseEvent) => {\n\t\t\te.stopPropagation();\n\t\t\ttry {\n\t\t\t\tawait navigator.clipboard.writeText(text);\n\t\t\t\tsetCopied(true);\n\t\t\t\tif (timeoutRef.current) {\n\t\t\t\t\tclearTimeout(timeoutRef.current);\n\t\t\t\t}\n\t\t\t\ttimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n\t\t\t} catch {\n\t\t\t\t// Clipboard API not available\n\t\t\t}\n\t\t},\n\t\t[text],\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\tvariant=\"ghost\"\n\t\t\tsize=\"sm\"\n\t\t\tonClick={handleCopy}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:h-auto ww:gap-1 ww:px-1.5 ww:py-0.5 ww:text-xs ww:text-muted-foreground ww:hover:text-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t{copied ? (\n\t\t\t\t<>\n\t\t\t\t\t<CheckIcon className=\"ww:size-3.5\" />\n\t\t\t\t\t<span>Copied</span>\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<ClipboardCopyIcon className=\"ww:size-3.5\" />\n\t\t\t\t\t<span>Copy</span>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</Button>\n\t);\n}\n\ninterface CollapsibleJSONProps extends HTMLAttributes<HTMLDivElement> {\n\tdata: unknown;\n\tlabel: string;\n}\n\n/**\n * Labeled JSON section with a Copy button and a collapsible preview.\n * Collapsed: shows a truncated single-line abbreviation with a `>` chevron.\n * Expanded: rotates the chevron and shows full pretty-printed JSON.\n */\nfunction CollapsibleJSON({\n\tdata,\n\tlabel,\n\tclassName,\n\t...props\n}: CollapsibleJSONProps) {\n\tconst [expanded, setExpanded] = useState(false);\n\tconst fullJSON = useMemo(() => JSON.stringify(data, null, 2), [data]);\n\tconst preview = truncateJSON(data);\n\n\treturn (\n\t\t<div className={cn(\"ww:rounded-lg ww:bg-tool-card\", className)} {...props}>\n\t\t\t<div className=\"ww:flex ww:items-center ww:justify-between ww:px-3 ww:pt-2.5 ww:pb-1.5\">\n\t\t\t\t<span className=\"ww:text-xs ww:font-medium ww:text-muted-foreground\">\n\t\t\t\t\t{label}\n\t\t\t\t</span>\n\t\t\t\t<CopyButton text={fullJSON} />\n\t\t\t</div>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={() => setExpanded((v) => !v)}\n\t\t\t\tclassName=\"ww:flex ww:w-full ww:items-start ww:gap-2 ww:px-3 ww:pb-3 ww:text-left\"\n\t\t\t>\n\t\t\t\t<ChevronRightIcon\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:mt-0.5 ww:size-3.5 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-150\",\n\t\t\t\t\t\texpanded && \"ww:rotate-90\",\n\t\t\t\t\t)}\n\t\t\t\t/>\n\t\t\t\t{expanded ? (\n\t\t\t\t\t<pre className=\"ww:overflow-x-auto ww:text-xs ww:font-mono ww:text-foreground ww:whitespace-pre-wrap ww:break-all\">\n\t\t\t\t\t\t<code>{fullJSON}</code>\n\t\t\t\t\t</pre>\n\t\t\t\t) : (\n\t\t\t\t\t<span className=\"ww:truncate ww:text-xs ww:font-mono ww:text-foreground/80\">\n\t\t\t\t\t\t{preview}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</button>\n\t\t</div>\n\t);\n}\n\nconst ToolOpenContext = createContext<{\n\topen: boolean;\n\ttoggle: () => void;\n}>({ open: false, toggle: () => {} });\n\nexport type ToolProps = HTMLAttributes<HTMLDivElement> & {\n\tdefaultOpen?: boolean;\n};\n\n/**\n * Compound component root for a tool call display.\n * Provides open/closed state via context to ToolHeader and ToolContent.\n *\n * ```tsx\n * <Tool defaultOpen>\n * <ToolHeader title=\"Price estimate ready\" state=\"output-available\" />\n * <ToolContent>\n * <ToolServerInfo toolName=\"get_price_estimate\" serverName=\"Tuio v2\" />\n * <ToolInput input={args} />\n * <ToolOutput output={result} errorText={undefined} />\n * </ToolContent>\n * </Tool>\n * ```\n */\nexport function Tool({\n\tclassName,\n\tdefaultOpen = false,\n\tchildren,\n\t...props\n}: ToolProps) {\n\tconst [open, setOpen] = useState(defaultOpen);\n\treturn (\n\t\t<ToolOpenContext.Provider\n\t\t\tvalue={{ open, toggle: () => setOpen((o) => !o) }}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName={cn(\"ww:mb-4 ww:w-full\", className)}\n\t\t\t\tdata-state={open ? \"open\" : \"closed\"}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t</ToolOpenContext.Provider>\n\t);\n}\n\nexport type ToolHeaderProps = HTMLAttributes<HTMLButtonElement> & {\n\ttitle?: string;\n\tstate: ToolUIPart[\"state\"];\n};\n\n/** Clickable header that toggles the tool accordion. Shows a `{≡}` icon, title, and chevron. */\nexport function ToolHeader({\n\tclassName,\n\ttitle,\n\tstate,\n\t...props\n}: ToolHeaderProps) {\n\tconst { open, toggle } = useContext(ToolOpenContext);\n\tconst isRunning = state === \"input-available\" || state === \"input-streaming\";\n\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tonClick={toggle}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:flex ww:w-full ww:items-center ww:justify-between ww:gap-3 ww:py-1.5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\taria-expanded={open}\n\t\t\t{...props}\n\t\t>\n\t\t\t<div className=\"ww:flex ww:min-w-0 ww:items-center ww:gap-2\">\n\t\t\t\t<BracesIcon className=\"ww:size-4 ww:shrink-0 ww:text-muted-foreground\" />\n\t\t\t\t<span className=\"ww:truncate ww:text-sm ww:font-medium\">{title}</span>\n\t\t\t\t{isRunning && (\n\t\t\t\t\t<span className=\"ww:size-2 ww:shrink-0 ww:rounded-full ww:bg-primary ww:animate-pulse\" />\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<ChevronDownIcon\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:size-4 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-200\",\n\t\t\t\t\topen && \"ww:rotate-180\",\n\t\t\t\t)}\n\t\t\t/>\n\t\t</button>\n\t);\n}\n\nexport type ToolServerInfoProps = HTMLAttributes<HTMLDivElement> & {\n\tserverName?: string;\n\tserverIcon?: string;\n\ttoolName: string;\n};\n\n/** Optional MCP server identity card. Shows server icon + name and the tool function name. Renders nothing if no props need display. */\nexport function ToolServerInfo({\n\tclassName,\n\tserverName,\n\tserverIcon,\n\ttoolName,\n\t...props\n}: ToolServerInfoProps) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:flex ww:items-center ww:gap-3 ww:rounded-lg ww:border ww:border-border ww:px-3 ww:py-2.5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{serverIcon ? (\n\t\t\t\t<img\n\t\t\t\t\tsrc={serverIcon}\n\t\t\t\t\talt={serverName ?? \"\"}\n\t\t\t\t\tclassName=\"ww:size-8 ww:shrink-0 ww:rounded-full ww:object-cover\"\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"ww:flex ww:size-8 ww:shrink-0 ww:items-center ww:justify-center ww:rounded-full ww:border ww:border-border ww:bg-muted\">\n\t\t\t\t\t<ServerIcon className=\"ww:size-4 ww:text-muted-foreground\" />\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"ww:flex ww:min-w-0 ww:flex-col\">\n\t\t\t\t{serverName && (\n\t\t\t\t\t<span className=\"ww:text-xs ww:text-muted-foreground\">\n\t\t\t\t\t\t{serverName}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t\t<span className=\"ww:truncate ww:text-sm ww:font-semibold\">\n\t\t\t\t\t{toolName}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport type ToolContentProps = HTMLAttributes<HTMLDivElement>;\n\n/** Collapsible body that animates open/closed. Content below smoothly pushes up/down via a grid-row height transition. */\nexport function ToolContent({\n\tclassName,\n\tchildren,\n\t...props\n}: ToolContentProps) {\n\tconst { open } = useContext(ToolOpenContext);\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:grid ww:transition-[grid-template-rows,opacity] ww:duration-200 ww:ease-out\",\n\t\t\t\topen\n\t\t\t\t\t? \"ww:grid-rows-[1fr] ww:opacity-100\"\n\t\t\t\t\t: \"ww:grid-rows-[0fr] ww:opacity-0\",\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"ww:min-h-0 ww:overflow-hidden\">\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:mt-2 ww:space-y-3 ww:rounded-lg ww:border ww:border-border ww:bg-background ww:p-3\",\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t)}\n\t\t\t\t\t{...props}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport type ToolInputProps = HTMLAttributes<HTMLDivElement> & {\n\tinput: ToolUIPart[\"input\"];\n};\n\n/** Displays the tool call request parameters as a collapsible JSON section labeled \"Request\". */\nexport function ToolInput({ className, input, ...props }: ToolInputProps) {\n\treturn (\n\t\t<CollapsibleJSON\n\t\t\tdata={input}\n\t\t\tlabel=\"Request\"\n\t\t\tclassName={className}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport type ToolOutputProps = HTMLAttributes<HTMLDivElement> & {\n\toutput: ToolUIPart[\"output\"];\n\terrorText: ToolUIPart[\"errorText\"];\n};\n\nfunction getUiMeta(output: unknown): Record<string, unknown> | undefined {\n\tif (typeof output !== \"object\" || output === null) return undefined;\n\tconst meta = (output as Record<string, unknown>)._meta;\n\tif (typeof meta !== \"object\" || meta === null) return undefined;\n\tconst ui = (meta as Record<string, unknown>).ui;\n\tif (typeof ui !== \"object\" || ui === null) return undefined;\n\treturn ui as Record<string, unknown>;\n}\n\n/** Extract the MCP app resource URI from `output._meta.ui.resourceUri`, if present. */\nexport function getResourceUri(output: unknown): string | undefined {\n\tconst uri = getUiMeta(output)?.resourceUri;\n\treturn typeof uri === \"string\" ? uri : undefined;\n}\n\n/** Extract the auto-height flag from `output._meta.ui.autoHeight`, if present. */\nexport function getAutoHeight(output: unknown): boolean {\n\treturn getUiMeta(output)?.autoHeight === true;\n}\n\n/** Displays the tool call result as a collapsible JSON section labeled \"Response\", or an error block if `errorText` is set. */\nexport function ToolOutput({\n\tclassName,\n\toutput,\n\terrorText,\n\t...props\n}: ToolOutputProps) {\n\tif (!(output || errorText)) return null;\n\n\tif (errorText) {\n\t\treturn (\n\t\t\t<div className={cn(\"ww:space-y-2\", className)} {...props}>\n\t\t\t\t<h4 className=\"ww:text-xs ww:font-medium ww:uppercase ww:tracking-wide ww:text-muted-foreground\">\n\t\t\t\t\tError\n\t\t\t\t</h4>\n\t\t\t\t<div className=\"ww:rounded-lg ww:bg-destructive/10 ww:p-3 ww:text-xs ww:text-destructive\">\n\t\t\t\t\t{errorText}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<CollapsibleJSON\n\t\t\tdata={output}\n\t\t\tlabel=\"Response\"\n\t\t\tclassName={className}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nconst DEFAULT_RESOURCE_ENDPOINT = \"/api/mcp/resource\";\nconst MAX_HEIGHT = 500;\nconst DEFAULT_HEIGHT = 0;\nconst PROTOCOL_VERSION = \"2026-01-26\";\nconst RESIZE_ANIMATION_MS = 300;\nconst HANDSHAKE_TIMEOUT_MS = 3000;\nconst MAX_RETRIES = 3;\n\nexport interface McpAppFrameProps {\n\tresourceUri: string;\n\ttoolInput: Record<string, unknown>;\n\ttoolResult: {\n\t\tcontent?: Array<{ type: string; text?: string }>;\n\t\tstructuredContent?: Record<string, unknown>;\n\t};\n\tresourceEndpoint?: string;\n\tisDark?: boolean;\n\tclassName?: string;\n\t/** When true, the iframe height auto-adapts to its content. Set via `_meta.ui.autoHeight` in the tool result. */\n\tautoHeight?: boolean;\n\t/** Called when the view requests to open a URL */\n\tonOpenLink?: (url: string) => void;\n\t/** Called when a widget sends a follow-up message via `ui/message` */\n\tonFollowUp?: (message: {\n\t\trole: string;\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t}) => void;\n}\n\nexport function McpAppFrame({\n\tresourceUri,\n\ttoolInput,\n\ttoolResult,\n\tresourceEndpoint = DEFAULT_RESOURCE_ENDPOINT,\n\tisDark = false,\n\tclassName,\n\t// TODO: REMOVE — defaulting to true for playground testing\n\tautoHeight = true,\n\tonOpenLink,\n\tonFollowUp,\n}: McpAppFrameProps) {\n\tconst iframeRef = useRef<HTMLIFrameElement>(null);\n\tconst toolInputRef = useRef(toolInput);\n\tconst toolResultRef = useRef(toolResult);\n\tconst lastSizeRef = useRef({ width: 0, height: 0 });\n\tconst animationRef = useRef<Animation | null>(null);\n\tconst initializedRef = useRef(false);\n\tconst retryCountRef = useRef(0);\n\tconst [height, setHeight] = useState(DEFAULT_HEIGHT);\n\tconst [width, setWidth] = useState<number | undefined>(undefined);\n\tconst onOpenLinkRef = useRef(onOpenLink);\n\tconst onFollowUpRef = useRef(onFollowUp);\n\n\ttoolInputRef.current = toolInput;\n\ttoolResultRef.current = toolResult;\n\tonOpenLinkRef.current = onOpenLink;\n\tonFollowUpRef.current = onFollowUp;\n\n\tconst clampHeight = useCallback(\n\t\t(h: number) => {\n\t\t\tif (autoHeight) return Math.max(h, 0);\n\t\t\treturn Math.min(Math.max(h, 50), MAX_HEIGHT);\n\t\t},\n\t\t[autoHeight],\n\t);\n\n\t// Build the iframe src URL directly — avoids null-origin issues with srcdoc\n\tconst iframeSrc = useMemo(\n\t\t() => `${resourceEndpoint}?uri=${encodeURIComponent(resourceUri)}`,\n\t\t[resourceEndpoint, resourceUri],\n\t);\n\n\tconst isDarkRef = useRef(isDark);\n\tisDarkRef.current = isDark;\n\n\t// Send theme changes to the iframe (only after handshake is complete)\n\tuseEffect(() => {\n\t\tif (!initializedRef.current) return;\n\t\tconst iframe = iframeRef.current;\n\t\tif (!iframe?.contentWindow) return;\n\n\t\tiframe.contentWindow.postMessage(\n\t\t\t{\n\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\tmethod: \"ui/notifications/host-context-changed\",\n\t\t\t\tparams: {\n\t\t\t\t\ttheme: isDark ? \"dark\" : \"light\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"*\",\n\t\t);\n\t}, [isDark]);\n\n\t// Synchronous postMessage protocol handler — no async imports, no timing issues.\n\t// Handles the MCP UI protocol (ui/initialize, notifications, etc.) directly.\n\tuseEffect(() => {\n\t\tconst iframe = iframeRef.current;\n\t\tif (!iframe) return;\n\n\t\tlet disposed = false;\n\t\tlet handshakeReceived = false;\n\t\tconst debug = (...args: unknown[]) =>\n\t\t\tconsole.debug(\"[McpAppFrame]\", ...args);\n\n\t\tdebug(\"effect mounted, waiting for handshake\");\n\n\t\t// Retry: reload iframe if handshake doesn't arrive in time\n\t\tconst handshakeTimer = setTimeout(() => {\n\t\t\tif (disposed || handshakeReceived) return;\n\t\t\tif (retryCountRef.current >= MAX_RETRIES) {\n\t\t\t\tdebug(\"handshake failed after\", MAX_RETRIES, \"retries, giving up\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tretryCountRef.current += 1;\n\t\t\tdebug(\n\t\t\t\t\"handshake timeout, reloading iframe (retry\",\n\t\t\t\tretryCountRef.current,\n\t\t\t\t\"of\",\n\t\t\t\tMAX_RETRIES,\n\t\t\t\t\")\",\n\t\t\t);\n\t\t\t// Force reload with a cache-busting param\n\t\t\tconst url = new URL(iframe.src);\n\t\t\turl.searchParams.set(\"_retry\", String(retryCountRef.current));\n\t\t\tiframe.src = url.toString();\n\t\t}, HANDSHAKE_TIMEOUT_MS);\n\n\t\tconst postToIframe = (msg: Record<string, unknown>) => {\n\t\t\tdebug(\"→ send\", msg.method ?? `response:${msg.id}`, msg);\n\t\t\tiframe.contentWindow?.postMessage(msg, \"*\");\n\t\t};\n\n\t\tconst handleMessage = (event: MessageEvent) => {\n\t\t\tif (disposed) return;\n\t\t\tif (event.source !== iframe.contentWindow) return;\n\n\t\t\tconst data = event.data;\n\t\t\tif (!data || typeof data !== \"object\" || data.jsonrpc !== \"2.0\") return;\n\n\t\t\tconst method: string | undefined = data.method;\n\t\t\tconst id: number | string | undefined = data.id;\n\n\t\t\tdebug(\"← recv\", method ?? `response:${id}`, data);\n\n\t\t\t// ui/initialize — widget requests handshake\n\t\t\tif (method === \"ui/initialize\" && id != null) {\n\t\t\t\thandshakeReceived = true;\n\t\t\t\tclearTimeout(handshakeTimer);\n\t\t\t\tdebug(\"handshake started\");\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tid,\n\t\t\t\t\tresult: {\n\t\t\t\t\t\tprotocolVersion: data.params?.protocolVersion ?? PROTOCOL_VERSION,\n\t\t\t\t\t\thostInfo: { name: \"WaniWani Chat\", version: \"1.0.0\" },\n\t\t\t\t\t\thostCapabilities: {\n\t\t\t\t\t\t\topenLinks: {},\n\t\t\t\t\t\t\tmessage: {},\n\t\t\t\t\t\t},\n\t\t\t\t\t\thostContext: {\n\t\t\t\t\t\t\ttheme: isDarkRef.current ? \"dark\" : \"light\",\n\t\t\t\t\t\t\tdisplayMode: \"inline\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/notifications/initialized — widget confirms init, we send tool data\n\t\t\tif (method === \"ui/notifications/initialized\") {\n\t\t\t\tdebug(\"handshake complete, sending tool data\");\n\t\t\t\tinitializedRef.current = true;\n\t\t\t\tconst input = toolInputRef.current;\n\t\t\t\tconst result = toolResultRef.current;\n\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tmethod: \"ui/notifications/tool-input\",\n\t\t\t\t\tparams: { arguments: input },\n\t\t\t\t});\n\n\t\t\t\tconst content = result.content ?? [\n\t\t\t\t\t{ type: \"text\", text: JSON.stringify(result) },\n\t\t\t\t];\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tmethod: \"ui/notifications/tool-result\",\n\t\t\t\t\tparams: {\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\tstructuredContent: result.structuredContent,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/notifications/size-changed — widget reports content size\n\t\t\tif (method === \"ui/notifications/size-changed\") {\n\t\t\t\tconst params = data.params;\n\t\t\t\tconst newHeight =\n\t\t\t\t\ttypeof params?.height === \"number\" ? params.height : undefined;\n\t\t\t\tconst newWidth =\n\t\t\t\t\ttypeof params?.width === \"number\" ? params.width : undefined;\n\n\t\t\t\t// Deduplicate — only update if values actually changed (prevents feedback loops)\n\t\t\t\tconst last = lastSizeRef.current;\n\t\t\t\tconst heightChanged =\n\t\t\t\t\tnewHeight !== undefined && newHeight !== last.height;\n\t\t\t\tconst widthChanged = newWidth !== undefined && newWidth !== last.width;\n\n\t\t\t\tdebug(\"size-changed\", {\n\t\t\t\t\tnewHeight,\n\t\t\t\t\tnewWidth,\n\t\t\t\t\tlastHeight: last.height,\n\t\t\t\t\tlastWidth: last.width,\n\t\t\t\t\theightChanged,\n\t\t\t\t\twidthChanged,\n\t\t\t\t});\n\n\t\t\t\tif (!heightChanged && !widthChanged) return;\n\n\t\t\t\tif (heightChanged && newHeight !== undefined) {\n\t\t\t\t\tlast.height = newHeight;\n\t\t\t\t\tconst clamped = clampHeight(newHeight);\n\n\t\t\t\t\t// Get current visual height before canceling the old animation\n\t\t\t\t\tconst from = iframe.getBoundingClientRect().height;\n\n\t\t\t\t\t// Cancel previous animation so its fill: \"forwards\" stops overriding inline style\n\t\t\t\t\tif (animationRef.current) {\n\t\t\t\t\t\tanimationRef.current.cancel();\n\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the target height in React state (takes effect once no animation overrides it)\n\t\t\t\t\tsetHeight(clamped);\n\n\t\t\t\t\t// Animate the height transition\n\t\t\t\t\tif (iframe.animate && Math.abs(from - clamped) > 2) {\n\t\t\t\t\t\tconst anim = iframe.animate(\n\t\t\t\t\t\t\t[{ height: `${from}px` }, { height: `${clamped}px` }],\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tduration: RESIZE_ANIMATION_MS,\n\t\t\t\t\t\t\t\teasing: \"ease-out\",\n\t\t\t\t\t\t\t\tfill: \"forwards\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\tanimationRef.current = anim;\n\t\t\t\t\t\t// Once done, remove the animation so the inline style is the source of truth\n\t\t\t\t\t\tanim.onfinish = () => {\n\t\t\t\t\t\t\tif (animationRef.current === anim) {\n\t\t\t\t\t\t\t\tanim.cancel();\n\t\t\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (widthChanged && autoHeight && newWidth !== undefined) {\n\t\t\t\t\tlast.width = newWidth;\n\t\t\t\t\tsetWidth(newWidth);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/open-link — widget requests to open a URL\n\t\t\tif (method === \"ui/open-link\" && id != null) {\n\t\t\t\tconst url = data.params?.url;\n\t\t\t\tif (typeof url === \"string\") {\n\t\t\t\t\tif (onOpenLinkRef.current) {\n\t\t\t\t\t\tonOpenLinkRef.current(url);\n\t\t\t\t\t} else {\n\t\t\t\t\t\twindow.open(url, \"_blank\", \"noopener,noreferrer\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/message — widget sends a chat message\n\t\t\tif (method === \"ui/message\" && id != null) {\n\t\t\t\tif (onFollowUpRef.current && data.params) {\n\t\t\t\t\tonFollowUpRef.current(data.params);\n\t\t\t\t}\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/request-display-mode — widget requests fullscreen/inline/pip\n\t\t\tif (method === \"ui/request-display-mode\" && id != null) {\n\t\t\t\t// Acknowledge but stay inline for now\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/resource-teardown — graceful shutdown\n\t\t\tif (method === \"ui/resource-teardown\" && id != null) {\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ping — keep-alive\n\t\t\tif (method === \"ping\" && id != null) {\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"message\", handleMessage);\n\n\t\treturn () => {\n\t\t\tdebug(\"effect cleanup (disposed)\");\n\t\t\tdisposed = true;\n\t\t\tclearTimeout(handshakeTimer);\n\t\t\twindow.removeEventListener(\"message\", handleMessage);\n\t\t};\n\t}, [autoHeight, clampHeight]);\n\n\treturn (\n\t\t<iframe\n\t\t\tref={iframeRef}\n\t\t\tsrc={iframeSrc}\n\t\t\tsandbox=\"allow-scripts allow-forms allow-same-origin\"\n\t\t\tclassName={cn(\"ww:rounded-md ww:border ww:border-border\", className)}\n\t\t\tstyle={{\n\t\t\t\theight,\n\t\t\t\tminWidth: width ? `min(${width}px, 100%)` : undefined,\n\t\t\t\twidth: \"100%\",\n\t\t\t\tborder: \"none\",\n\t\t\t\tcolorScheme: \"auto\",\n\t\t\t}}\n\t\t\ttitle=\"MCP App\"\n\t\t/>\n\t);\n}\n","\"use client\";\n\nimport { Component, type ReactNode } from \"react\";\n\ninterface Props {\n\tchildren: ReactNode;\n}\n\ninterface State {\n\thasError: boolean;\n}\n\nexport class WidgetErrorBoundary extends Component<Props, State> {\n\tstate: State = { hasError: false };\n\n\tstatic getDerivedStateFromError(): State {\n\t\treturn { hasError: true };\n\t}\n\n\tcomponentDidCatch(error: Error) {\n\t\tconsole.warn(\"[WaniWani] Widget failed to render:\", error.message);\n\t}\n\n\trender() {\n\t\tif (this.state.hasError) {\n\t\t\treturn (\n\t\t\t\t<div className=\"ww:flex ww:items-center ww:justify-between ww:rounded-md ww:border ww:border-border ww:bg-muted/50 ww:px-4 ww:py-3 ww:text-sm ww:text-muted-foreground\">\n\t\t\t\t\t<span>Widget failed to load</span>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() => this.setState({ hasError: false })}\n\t\t\t\t\t\tclassName=\"ww:text-xs ww:font-medium ww:text-primary ww:hover:underline\"\n\t\t\t\t\t>\n\t\t\t\t\t\tRetry\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\n\t\treturn this.props.children;\n\t}\n}\n","\"use client\";\n\nimport type { ChatStatus, ReasoningUIPart, ToolUIPart, UIMessage } from \"ai\";\n\nimport { Attachments } from \"../ai-elements/attachments\";\nimport { Loader } from \"../ai-elements/loader\";\nimport {\n\tMessage,\n\tMessageContent,\n\tMessageResponse,\n} from \"../ai-elements/message\";\nimport { Reasoning } from \"../ai-elements/reasoning\";\nimport {\n\tgetAutoHeight,\n\tgetResourceUri,\n\tTool,\n\tToolContent,\n\tToolHeader,\n\tToolInput,\n\tToolOutput,\n} from \"../ai-elements/tool\";\nimport { McpAppFrame } from \"./mcp-app-frame\";\nimport { WidgetErrorBoundary } from \"./widget-error-boundary\";\n\n/** Converts `get_price_estimate` or `compare-prices` → `Get price estimate` / `Compare prices` */\nfunction formatToolName(name: string): string {\n\treturn name.replace(/[-_]/g, \" \").replace(/^\\w/, (c) => c.toUpperCase());\n}\n\ninterface MessageListProps {\n\tmessages: UIMessage[];\n\tstatus: ChatStatus;\n\twelcomeMessage?: string;\n\tresourceEndpoint?: string;\n\tisDark?: boolean;\n\tonFollowUp?: (message: {\n\t\trole: string;\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t}) => void;\n}\n\nexport function MessageList({\n\tmessages,\n\tstatus,\n\twelcomeMessage,\n\tresourceEndpoint,\n\tisDark,\n\tonFollowUp,\n}: MessageListProps) {\n\tconst isLoading = status === \"submitted\" || status === \"streaming\";\n\tconst lastMessage = messages[messages.length - 1];\n\tconst hasMessages = messages.length > 0;\n\tconst showLoaderBubble =\n\t\tisLoading && (!hasMessages || lastMessage.role === \"user\");\n\n\treturn (\n\t\t<>\n\t\t\t{welcomeMessage && (\n\t\t\t\t<Message from=\"assistant\">\n\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t<MessageResponse>{welcomeMessage}</MessageResponse>\n\t\t\t\t\t</MessageContent>\n\t\t\t\t</Message>\n\t\t\t)}\n\t\t\t{messages.map((message) => {\n\t\t\t\tconst textParts = message.parts.filter((p) => p.type === \"text\");\n\t\t\t\tconst reasoningParts = message.parts.filter(\n\t\t\t\t\t(p): p is ReasoningUIPart => p.type === \"reasoning\",\n\t\t\t\t);\n\t\t\t\tconst fileParts = message.parts.filter((p) => p.type === \"file\");\n\t\t\t\tconst toolParts = message.parts.filter(\n\t\t\t\t\t(\n\t\t\t\t\t\tp,\n\t\t\t\t\t): p is typeof p & {\n\t\t\t\t\t\ttoolCallId: string;\n\t\t\t\t\t\ttoolName: string;\n\t\t\t\t\t\tstate: ToolUIPart[\"state\"];\n\t\t\t\t\t\tinput: unknown;\n\t\t\t\t\t\ttitle?: string;\n\t\t\t\t\t} => \"toolCallId\" in p,\n\t\t\t\t);\n\t\t\t\tconst isLastAssistant =\n\t\t\t\t\tmessage === lastMessage && message.role === \"assistant\";\n\t\t\t\tconst hasTextContent = textParts.length > 0;\n\n\t\t\t\treturn (\n\t\t\t\t\t<Message from={message.role} key={message.id}>\n\t\t\t\t\t\t{reasoningParts.map((part, i) => (\n\t\t\t\t\t\t\t<Reasoning\n\t\t\t\t\t\t\t\tkey={`reasoning-${message.id}-${i}`}\n\t\t\t\t\t\t\t\ttext={part.text}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{toolParts.map((part) => {\n\t\t\t\t\t\t\tconst output = \"output\" in part ? part.output : undefined;\n\t\t\t\t\t\t\tconst resourceUri =\n\t\t\t\t\t\t\t\toutput !== undefined ? getResourceUri(output) : undefined;\n\t\t\t\t\t\t\tconst autoHeight =\n\t\t\t\t\t\t\t\toutput !== undefined ? getAutoHeight(output) : false;\n\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div key={part.toolCallId}>\n\t\t\t\t\t\t\t\t\t<Tool defaultOpen={part.state === \"output-available\"}>\n\t\t\t\t\t\t\t\t\t\t<ToolHeader\n\t\t\t\t\t\t\t\t\t\t\ttitle={part.title ?? formatToolName(part.toolName)}\n\t\t\t\t\t\t\t\t\t\t\tstate={part.state}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<ToolContent>\n\t\t\t\t\t\t\t\t\t\t\t<ToolInput input={part.input} />\n\t\t\t\t\t\t\t\t\t\t\t{output !== undefined && (\n\t\t\t\t\t\t\t\t\t\t\t\t<ToolOutput\n\t\t\t\t\t\t\t\t\t\t\t\t\toutput={output}\n\t\t\t\t\t\t\t\t\t\t\t\t\terrorText={\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"errorText\" in part ? part.errorText : undefined\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</ToolContent>\n\t\t\t\t\t\t\t\t\t</Tool>\n\t\t\t\t\t\t\t\t\t{resourceUri && output !== undefined && (\n\t\t\t\t\t\t\t\t\t\t<WidgetErrorBoundary>\n\t\t\t\t\t\t\t\t\t\t\t<McpAppFrame\n\t\t\t\t\t\t\t\t\t\t\t\tresourceUri={resourceUri}\n\t\t\t\t\t\t\t\t\t\t\t\ttoolInput={\n\t\t\t\t\t\t\t\t\t\t\t\t\t(part.input as Record<string, unknown>) ?? {}\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\ttoolResult={{\n\t\t\t\t\t\t\t\t\t\t\t\t\tcontent: (output as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.content as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| Array<{ type: string; text?: string }>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t\t\t\tstructuredContent: (output as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.structuredContent as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| Record<string, unknown>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tresourceEndpoint={resourceEndpoint}\n\t\t\t\t\t\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\t\t\t\t\t\tautoHeight={autoHeight}\n\t\t\t\t\t\t\t\t\t\t\t\tonFollowUp={onFollowUp}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</WidgetErrorBoundary>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t\t{fileParts.length > 0 && <Attachments files={fileParts} />}\n\t\t\t\t\t\t\t{hasTextContent\n\t\t\t\t\t\t\t\t? textParts.map((part, i) => (\n\t\t\t\t\t\t\t\t\t\t<MessageResponse key={`${message.id}-${i}`}>\n\t\t\t\t\t\t\t\t\t\t\t{part.type === \"text\" ? part.text : \"\"}\n\t\t\t\t\t\t\t\t\t\t</MessageResponse>\n\t\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t\t: isLastAssistant && isLoading && <Loader />}\n\t\t\t\t\t\t</MessageContent>\n\t\t\t\t\t</Message>\n\t\t\t\t);\n\t\t\t})}\n\t\t\t{showLoaderBubble && (\n\t\t\t\t<Message from=\"assistant\">\n\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t<Loader />\n\t\t\t\t\t</MessageContent>\n\t\t\t\t</Message>\n\t\t\t)}\n\t\t</>\n\t);\n}\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport interface SuggestionsProps\n\textends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n\tsuggestions: string[];\n\tisLoading?: boolean;\n\tonSelect: (suggestion: string) => void;\n}\n\nexport function Suggestions({\n\tsuggestions,\n\tisLoading,\n\tonSelect,\n\tclassName,\n\t...props\n}: SuggestionsProps) {\n\tif (suggestions.length === 0 && !isLoading) return null;\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-2 ww:px-3 ww:py-2\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{isLoading\n\t\t\t\t? [0, 1, 2].map((i) => (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\t\tclassName=\"ww:h-7 ww:rounded-full ww:bg-accent ww:animate-pulse\"\n\t\t\t\t\t\t\tstyle={{ width: `${60 + i * 20}px` }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))\n\t\t\t\t: suggestions.map((suggestion, index) => (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tkey={suggestion}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={() => onSelect(suggestion)}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"ww:rounded-full ww:border ww:border-border ww:bg-background ww:px-3 ww:py-1 ww:text-xs\",\n\t\t\t\t\t\t\t\t\"ww:text-foreground ww:hover:bg-accent ww:hover:border-primary/30\",\n\t\t\t\t\t\t\t\t\"ww:transition-all ww:duration-200 ww:ease-out ww:cursor-pointer\",\n\t\t\t\t\t\t\t\t\"ww:animate-[ww-fade-in_0.2s_ease-out_both]\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tstyle={{ animationDelay: `${index * 50}ms` }}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{suggestion}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t))}\n\t\t</div>\n\t);\n}\n","\"use client\";\n\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport } from \"ai\";\nimport { useCallback, useRef, useState } from \"react\";\nimport type { ChatBaseProps } from \"../@types\";\nimport type { PromptInputMessage } from \"../ai-elements/prompt-input\";\n\nexport function useChatEngine(props: ChatBaseProps) {\n\tconst {\n\t\tapi = \"https://app.waniwani.ai/api/chat\",\n\t\theaders: userHeaders,\n\t\tbody,\n\t\tonMessageSent,\n\t\tonResponseReceived,\n\t} = props;\n\n\tconst transportRef = useRef(\n\t\tnew DefaultChatTransport({\n\t\t\tapi,\n\t\t\theaders: {\n\t\t\t\t...userHeaders,\n\t\t\t},\n\t\t\tbody,\n\t\t}),\n\t);\n\n\tconst { messages, sendMessage, status } = useChat({\n\t\ttransport: transportRef.current,\n\t\tonFinish() {\n\t\t\tonResponseReceived?.();\n\t\t},\n\t\tonError(error) {\n\t\t\tconsole.warn(\"[WaniWani] Chat error:\", error.message);\n\t\t},\n\t});\n\n\tconst [text, setText] = useState(\"\");\n\n\tconst handleSubmit = useCallback(\n\t\t(message: PromptInputMessage) => {\n\t\t\tconst hasText = Boolean(message.text?.trim());\n\t\t\tconst hasFiles = Boolean(message.files?.length);\n\t\t\tif (!(hasText || hasFiles)) return;\n\n\t\t\tsendMessage({\n\t\t\t\ttext: message.text || \"\",\n\t\t\t\tfiles: message.files,\n\t\t\t});\n\n\t\t\tonMessageSent?.(message.text || \"\");\n\t\t\tsetText(\"\");\n\t\t},\n\t\t[sendMessage, onMessageSent],\n\t);\n\n\tconst handleTextChange = useCallback(\n\t\t(e: React.ChangeEvent<HTMLTextAreaElement>) => {\n\t\t\tsetText(e.target.value);\n\t\t},\n\t\t[],\n\t);\n\n\tconst isLoading = status === \"submitted\" || status === \"streaming\";\n\tconst lastMessage = messages[messages.length - 1];\n\tconst hasMessages = messages.length > 0;\n\tconst showLoaderBubble =\n\t\tisLoading && (!hasMessages || lastMessage.role === \"user\");\n\n\treturn {\n\t\tmessages,\n\t\tstatus,\n\t\ttext,\n\t\tsetText,\n\t\thandleSubmit,\n\t\thandleTextChange,\n\t\tisLoading,\n\t\tshowLoaderBubble,\n\t\tlastMessage,\n\t\thasMessages,\n\t\tsendMessage,\n\t};\n}\n","\"use client\";\n\nimport type { ChatStatus, UIMessage } from \"ai\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { SuggestionsConfig } from \"../@types\";\n\nexport interface UseSuggestionsOptions {\n\tmessages: UIMessage[];\n\tstatus: ChatStatus;\n\tconfig?: boolean | SuggestionsConfig;\n}\n\n/**\n * Extract suggestions from the last assistant message's data part.\n * The API streams a `data-suggestions` part at the end of the response:\n * `{ type: \"data-suggestions\", data: { suggestions: string[] } }`\n */\nfunction extractSuggestions(message: UIMessage): string[] | null {\n\tfor (const part of message.parts) {\n\t\tconst p = part as Record<string, unknown>;\n\t\t// Handle both \"data-suggestions\" and generic \"data\" part types\n\t\tif (p.type === \"data\" || p.type === \"data-suggestions\") {\n\t\t\tconst data = p.data as Record<string, unknown> | undefined;\n\t\t\tif (data && Array.isArray(data.suggestions)) {\n\t\t\t\treturn data.suggestions as string[];\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction isConfigObject(\n\tconfig: boolean | SuggestionsConfig | undefined,\n): config is SuggestionsConfig {\n\treturn typeof config === \"object\" && config !== null && \"initial\" in config;\n}\n\nexport function useSuggestions(options: UseSuggestionsOptions) {\n\tconst { messages, status, config } = options;\n\n\tconst [suggestions, setSuggestions] = useState<string[]>(\n\t\t(isConfigObject(config) && config.initial ? config.initial : []) ?? [],\n\t);\n\tconst prevStatusRef = useRef<ChatStatus>(status);\n\n\tconst isDynamicEnabled =\n\t\tconfig === true || (isConfigObject(config) && config.dynamic !== false);\n\n\tconst clear = useCallback(() => {\n\t\tsetSuggestions([]);\n\t}, []);\n\n\t// Clear when a new user message arrives\n\tconst lastMessage = messages[messages.length - 1];\n\tuseEffect(() => {\n\t\tif (lastMessage?.role === \"user\") {\n\t\t\tclear();\n\t\t}\n\t}, [lastMessage, clear]);\n\n\t// Extract suggestions from message parts on streaming → ready transition\n\tuseEffect(() => {\n\t\tconst prevStatus = prevStatusRef.current;\n\t\tprevStatusRef.current = status;\n\n\t\tif (prevStatus === \"streaming\" && status === \"ready\" && isDynamicEnabled) {\n\t\t\tconst lastAssistant = [...messages]\n\t\t\t\t.reverse()\n\t\t\t\t.find((m) => m.role === \"assistant\");\n\t\t\tif (!lastAssistant) return;\n\n\t\t\tconsole.log(\"[WaniWani] Assistant parts:\", lastAssistant.parts);\n\n\t\t\tconst extracted = extractSuggestions(lastAssistant);\n\t\t\tconsole.log(\"[WaniWani] Extracted suggestions:\", extracted);\n\t\t\tif (extracted) {\n\t\t\t\tsetSuggestions(extracted);\n\t\t\t}\n\t\t}\n\t}, [status, isDynamicEnabled, messages]);\n\n\treturn { suggestions, isLoading: false, clear };\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nconst TYPE_SPEED_MS = 50;\nconst DELETE_SPEED_MS = 30;\nconst PAUSE_AFTER_TYPE_MS = 2000;\nconst PAUSE_AFTER_DELETE_MS = 500;\n\n/**\n * Returns a string that animates like someone typing and deleting the placeholder text.\n * Only animates when `active` is true (i.e. the input is empty).\n */\nexport function useTypingPlaceholder(text: string, active = true): string {\n\tconst [displayed, setDisplayed] = useState(\"\");\n\tconst timerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\tuseEffect(() => {\n\t\tif (!active) {\n\t\t\t// Reset so the animation restarts fresh when re-activated\n\t\t\tsetDisplayed(\"\");\n\t\t\treturn;\n\t\t}\n\n\t\tlet i = 0;\n\t\tlet deleting = false;\n\t\tlet disposed = false;\n\n\t\tconst tick = () => {\n\t\t\tif (disposed) return;\n\n\t\t\tif (!deleting) {\n\t\t\t\t// Typing forward\n\t\t\t\ti++;\n\t\t\t\tsetDisplayed(text.slice(0, i));\n\n\t\t\t\tif (i >= text.length) {\n\t\t\t\t\t// Finished typing — pause then start deleting\n\t\t\t\t\tdeleting = true;\n\t\t\t\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_TYPE_MS);\n\t\t\t\t} else {\n\t\t\t\t\ttimerRef.current = setTimeout(tick, TYPE_SPEED_MS);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Deleting\n\t\t\t\ti--;\n\t\t\t\tsetDisplayed(text.slice(0, i));\n\n\t\t\t\tif (i <= 0) {\n\t\t\t\t\t// Finished deleting — pause then start typing again\n\t\t\t\t\tdeleting = false;\n\t\t\t\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_DELETE_MS);\n\t\t\t\t} else {\n\t\t\t\t\ttimerRef.current = setTimeout(tick, DELETE_SPEED_MS);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Start after a small delay\n\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_DELETE_MS);\n\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\tclearTimeout(timerRef.current);\n\t\t};\n\t}, [text, active]);\n\n\treturn displayed;\n}\n","import type { ChatTheme } from \"./@types\";\n\nexport const DEFAULT_THEME: Required<ChatTheme> = {\n\tprimaryColor: \"#6366f1\",\n\tprimaryForeground: \"#1f2937\",\n\tbackgroundColor: \"#ffffff\",\n\ttextColor: \"#1f2937\",\n\tmutedColor: \"#6b7280\",\n\tborderColor: \"#e5e7eb\",\n\tassistantBubbleColor: \"#f3f4f6\",\n\tuserBubbleColor: \"#f4f4f4\",\n\tinputBackgroundColor: \"#f9fafb\",\n\tborderRadius: 16,\n\tmessageBorderRadius: 12,\n\tfontFamily: \"system-ui, -apple-system, 'Segoe UI', sans-serif\",\n\theaderBackgroundColor: \"#ffffff\",\n\theaderTextColor: \"#1f2937\",\n\tstatusColor: \"#22c55e\",\n\ttoolCardColor: \"#f4f4f5\",\n};\n\nexport const DARK_THEME: ChatTheme = {\n\tbackgroundColor: \"#212121\",\n\theaderBackgroundColor: \"#1e1e1e\",\n\theaderTextColor: \"#ececec\",\n\ttextColor: \"#ececec\",\n\tprimaryForeground: \"#ffffff\",\n\tmutedColor: \"#8e8ea0\",\n\tborderColor: \"#444444\",\n\tassistantBubbleColor: \"#2f2f2f\",\n\tuserBubbleColor: \"#303030\",\n\tinputBackgroundColor: \"#2f2f2f\",\n\tprimaryColor: \"#6366f1\",\n\tstatusColor: \"#22c55e\",\n\ttoolCardColor: \"#262626\",\n};\n\nconst CSS_VAR_MAP: Record<keyof ChatTheme, string[]> = {\n\tprimaryColor: [\"--ww-primary\", \"--ww-color-primary\"],\n\tprimaryForeground: [\"--ww-primary-fg\", \"--ww-color-primary-foreground\"],\n\tbackgroundColor: [\"--ww-bg\", \"--ww-color-background\"],\n\ttextColor: [\n\t\t\"--ww-text\",\n\t\t\"--ww-color-foreground\",\n\t\t\"--ww-color-accent-foreground\",\n\t],\n\tmutedColor: [\"--ww-muted\", \"--ww-color-muted-foreground\"],\n\tborderColor: [\"--ww-border\", \"--ww-color-border\"],\n\tassistantBubbleColor: [\"--ww-assistant-bubble\", \"--ww-color-accent\"],\n\tuserBubbleColor: [\"--ww-user-bubble\"],\n\tinputBackgroundColor: [\"--ww-input-bg\", \"--ww-color-input\"],\n\tborderRadius: [\"--ww-radius\"],\n\tmessageBorderRadius: [\"--ww-msg-radius\"],\n\tfontFamily: [\"--ww-font\"],\n\theaderBackgroundColor: [\"--ww-header-bg\"],\n\theaderTextColor: [\"--ww-header-text\"],\n\tstatusColor: [\"--ww-status\"],\n\ttoolCardColor: [\"--ww-tool-card\", \"--ww-color-tool-card\"],\n};\n\nexport function mergeTheme(userTheme?: ChatTheme): Required<ChatTheme> {\n\treturn { ...DEFAULT_THEME, ...userTheme };\n}\n\nexport function isDarkTheme(theme: Required<ChatTheme>): boolean {\n\tconst hex = theme.backgroundColor.replace(\"#\", \"\");\n\tconst r = parseInt(hex.substring(0, 2), 16);\n\tconst g = parseInt(hex.substring(2, 4), 16);\n\tconst b = parseInt(hex.substring(4, 6), 16);\n\treturn (r * 299 + g * 587 + b * 114) / 1000 < 128;\n}\n\nexport function themeToCSSProperties(\n\ttheme: Required<ChatTheme>,\n): Record<string, string> {\n\tconst vars: Record<string, string> = {};\n\tfor (const [key, cssVars] of Object.entries(CSS_VAR_MAP)) {\n\t\tconst value = theme[key as keyof ChatTheme];\n\t\tconst resolved = typeof value === \"number\" ? `${value}px` : String(value);\n\t\tfor (const cssVar of cssVars) {\n\t\t\tvars[cssVar] = resolved;\n\t\t}\n\t}\n\treturn vars;\n}\n","\"use client\";\n\nimport {\n\tforwardRef,\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport type { ChatCardProps, ChatHandle } from \"../@types\";\nimport {\n\tConversation,\n\tConversationContent,\n\tConversationScrollButton,\n} from \"../ai-elements/conversation\";\nimport {\n\tPromptInput,\n\tPromptInputAddAttachments,\n\tPromptInputSubmit,\n\tPromptInputTextarea,\n} from \"../ai-elements/prompt-input\";\nimport { MessageList } from \"../components/message-list\";\nimport { Suggestions } from \"../components/suggestions\";\nimport { useChatEngine } from \"../hooks/use-chat-engine\";\nimport { useSuggestions } from \"../hooks/use-suggestions\";\nimport { useTypingPlaceholder } from \"../hooks/use-typing-placeholder\";\nimport { cn } from \"../lib/utils\";\nimport { isDarkTheme, mergeTheme, themeToCSSProperties } from \"../theme\";\n\nexport const ChatCard = forwardRef<ChatHandle, ChatCardProps>(\n\tfunction ChatCard(props, ref) {\n\t\tconst {\n\t\t\ttheme: userTheme,\n\t\t\ttitle = \"Assistant\",\n\t\t\tsubtitle,\n\t\t\tshowStatus = true,\n\t\t\twidth = 500,\n\t\t\theight = 600,\n\t\t\tallowAttachments = false,\n\t\t\twelcomeMessage,\n\t\t\tplaceholder = \"Ask me anything...\",\n\t\t\ttriggerEvent = \"triggerDemoRequest\",\n\t\t\tresourceEndpoint,\n\t\t\tapi,\n\t\t} = props;\n\n\t\tconst effectiveResourceEndpoint =\n\t\t\tresourceEndpoint ?? (api ? `${api}/resource` : undefined);\n\n\t\tconst resolvedTheme = mergeTheme(userTheme);\n\t\tconst cssVars = themeToCSSProperties(resolvedTheme);\n\t\tconst isDark = isDarkTheme(resolvedTheme);\n\n\t\tconst engine = useChatEngine(props);\n\n\t\tconst animatedPlaceholder = useTypingPlaceholder(placeholder, !engine.text);\n\n\t\tconst [isHighlighted, setIsHighlighted] = useState(false);\n\t\tconst cardRef = useRef<HTMLDivElement>(null);\n\t\tconst highlightTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\t\tconst focusInput = useCallback(() => {\n\t\t\tconst container = cardRef.current;\n\t\t\tif (!container) return;\n\t\t\tcontainer.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\t\tconst textarea = container.querySelector(\"textarea\");\n\t\t\tif (textarea) {\n\t\t\t\tsetTimeout(() => textarea.focus(), 300);\n\t\t\t}\n\t\t\tsetIsHighlighted(true);\n\t\t\tclearTimeout(highlightTimerRef.current);\n\t\t\thighlightTimerRef.current = setTimeout(\n\t\t\t\t() => setIsHighlighted(false),\n\t\t\t\t2000,\n\t\t\t);\n\t\t}, []);\n\n\t\tconst suggestionsState = useSuggestions({\n\t\t\tmessages: engine.messages,\n\t\t\tstatus: engine.status,\n\t\t\tconfig: props.suggestions,\n\t\t});\n\n\t\tconst handleWidgetMessage = useCallback(\n\t\t\t(message: {\n\t\t\t\trole: string;\n\t\t\t\tcontent: Array<{ type: string; text?: string }>;\n\t\t\t}) => {\n\t\t\t\tconst text = message.content\n\t\t\t\t\t.map((c) => c.text ?? \"\")\n\t\t\t\t\t.join(\"\")\n\t\t\t\t\t.trim();\n\t\t\t\tif (text) engine.handleSubmit({ text, files: [] });\n\t\t\t},\n\t\t\t[engine.handleSubmit],\n\t\t);\n\n\t\tconst handleSuggestionSelect = useCallback(\n\t\t\t(suggestion: string) => {\n\t\t\t\tsuggestionsState.clear();\n\t\t\t\tengine.handleSubmit({ text: suggestion, files: [] });\n\t\t\t},\n\t\t\t[suggestionsState.clear, engine.handleSubmit],\n\t\t);\n\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tsendMessage: (text: string) => {\n\t\t\t\t\tengine.handleSubmit({ text, files: [] });\n\t\t\t\t\tfocusInput();\n\t\t\t\t},\n\t\t\t\tfocus: focusInput,\n\t\t\t}),\n\t\t\t[engine.handleSubmit, focusInput],\n\t\t);\n\n\t\t// Listen for custom trigger event (e.g. \"triggerDemoRequest\")\n\t\tuseEffect(() => {\n\t\t\tif (!triggerEvent) return;\n\t\t\tconst handler = (e: Event) => {\n\t\t\t\tconst detail = (e as CustomEvent).detail;\n\t\t\t\tconst message =\n\t\t\t\t\ttypeof detail?.message === \"string\" ? detail.message : undefined;\n\t\t\t\tif (message) {\n\t\t\t\t\tengine.handleSubmit({ text: message, files: [] });\n\t\t\t\t}\n\t\t\t\tfocusInput();\n\t\t\t};\n\t\t\twindow.addEventListener(triggerEvent, handler);\n\t\t\treturn () => window.removeEventListener(triggerEvent, handler);\n\t\t}, [triggerEvent, engine.handleSubmit, focusInput]);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={cardRef}\n\t\t\t\tstyle={{ ...cssVars, width, height }}\n\t\t\t\tdata-waniwani-chat=\"\"\n\t\t\t\tdata-waniwani-layout=\"card\"\n\t\t\t\t{...(isDark ? { \"data-waniwani-dark\": \"\" } : {})}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground ww:bg-background ww:rounded-[var(--ww-radius)] ww:border ww:border-border ww:shadow-md ww:overflow-hidden ww:transition-shadow ww:duration-300\",\n\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{/* Header */}\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"ww:shrink-0 ww:flex ww:items-center ww:gap-3 ww:px-4 ww:py-2 ww:border-b ww:border-border\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackgroundColor: resolvedTheme.headerBackgroundColor,\n\t\t\t\t\t\tcolor: resolvedTheme.headerTextColor,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{showStatus && (\n\t\t\t\t\t\t<span className=\"ww:size-2.5 ww:rounded-full ww:bg-status\" />\n\t\t\t\t\t)}\n\t\t\t\t\t<div className=\"ww:flex-1 ww:min-w-0\">\n\t\t\t\t\t\t<div className=\"ww:text-xs ww:font-semibold ww:truncate\">\n\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{subtitle && (\n\t\t\t\t\t\t\t<div className=\"ww:text-[11px] ww:text-muted-foreground ww:truncate\">\n\t\t\t\t\t\t\t\t{subtitle}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Messages */}\n\t\t\t\t<Conversation className=\"ww:flex-1 ww:min-h-0 ww:bg-background\">\n\t\t\t\t\t<ConversationContent>\n\t\t\t\t\t\t<MessageList\n\t\t\t\t\t\t\tmessages={engine.messages}\n\t\t\t\t\t\t\tstatus={engine.status}\n\t\t\t\t\t\t\twelcomeMessage={welcomeMessage}\n\t\t\t\t\t\t\tresourceEndpoint={effectiveResourceEndpoint}\n\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\tonFollowUp={handleWidgetMessage}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</ConversationContent>\n\t\t\t\t\t<ConversationScrollButton />\n\t\t\t\t</Conversation>\n\n\t\t\t\t{/* Suggestions */}\n\t\t\t\t<Suggestions\n\t\t\t\t\tsuggestions={suggestionsState.suggestions}\n\t\t\t\t\tisLoading={suggestionsState.isLoading}\n\t\t\t\t\tonSelect={handleSuggestionSelect}\n\t\t\t\t\tclassName=\"ww:border-t ww:border-border\"\n\t\t\t\t/>\n\n\t\t\t\t{/* Input */}\n\t\t\t\t<div className=\"ww:shrink-0 ww:border-t ww:border-border ww:bg-background\">\n\t\t\t\t\t<PromptInput\n\t\t\t\t\t\tonSubmit={engine.handleSubmit}\n\t\t\t\t\t\tglobalDrop={allowAttachments}\n\t\t\t\t\t\tmultiple={allowAttachments}\n\t\t\t\t\t\tclassName={cn(\"ww:rounded-none ww:border-0\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2\">\n\t\t\t\t\t\t\t{allowAttachments && <PromptInputAddAttachments />}\n\t\t\t\t\t\t\t<PromptInputTextarea\n\t\t\t\t\t\t\t\tonChange={engine.handleTextChange}\n\t\t\t\t\t\t\t\tvalue={engine.text}\n\t\t\t\t\t\t\t\tplaceholder={animatedPlaceholder}\n\t\t\t\t\t\t\t\tclassName=\"ww:min-h-0 ww:py-1.5 ww:px-2\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<PromptInputSubmit status={engine.status} />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</PromptInput>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t},\n);\n"],"mappings":"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","names":["forwardRef","useCallback","useEffect","useImperativeHandle","useRef","useState","ArrowDownIcon","useCallback","StickToBottom","useStickToBottomContext","clsx","extendTailwindMerge","twMerge","cn","inputs","jsx","Button","className","variant","size","type","props","cn","jsx","Conversation","className","props","StickToBottom","cn","ConversationContent","ConversationScrollButton","isAtBottom","scrollToBottom","useStickToBottomContext","handleScrollToBottom","useCallback","Button","ArrowDownIcon","ArrowUpIcon","LoaderIcon","PaperclipIcon","SquareIcon","XIcon","nanoid","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","jsx","jsxs","convertBlobUrlToDataUrl","url","blob","resolve","reader","LocalAttachmentsContext","createContext","usePromptInputAttachments","context","useContext","PromptInput","className","accept","multiple","globalDrop","maxFiles","maxFileSize","onSubmit","children","props","inputRef","useRef","formRef","items","setItems","useState","filesRef","useEffect","openFileDialog","useCallback","add","fileList","incoming","withinSize","f","valid","prev","capacity","capped","file","nanoid","remove","id","found","clear","handleChange","event","onDragOver","e","onDrop","handleSubmit","form","text","convertedFiles","_id","item","dataUrl","result","attachmentsCtx","useMemo","cn","PromptInputTextarea","onChange","onKeyDown","className","placeholder","props","attachments","usePromptInputAttachments","isComposing","setIsComposing","useState","handleKeyDown","useCallback","e","form","lastAttachment","handlePaste","event","items","files","item","file","jsx","cn","PromptInputSubmit","status","onStop","onClick","children","isGenerating","Icon","ArrowUpIcon","LoaderIcon","SquareIcon","handleClick","Button","PromptInputAddAttachments","jsxs","XIcon","PaperclipIcon","FileIcon","jsx","jsxs","Attachments","files","className","props","cn","file","i","AttachmentItem","FileIcon","jsx","Loader","className","size","props","cn","i","cjk","code","memo","Streamdown","jsx","Message","className","from","props","cn","MessageContent","children","streamdownPlugins","cjk","code","MessageResponse","memo","Streamdown","prevProps","nextProps","jsx","Reasoning","className","text","props","cn","BracesIcon","CheckIcon","ChevronDownIcon","ChevronRightIcon","ClipboardCopyIcon","ServerIcon","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","Fragment","jsx","jsxs","truncateJSON","data","maxLength","stringified","entries","parts","remaining","key","value","keyAbbrev","valStr","part","CopyButton","text","className","copied","setCopied","useState","timeoutRef","useRef","useEffect","handleCopy","useCallback","e","Button","cn","CheckIcon","ClipboardCopyIcon","CollapsibleJSON","label","props","expanded","setExpanded","fullJSON","useMemo","preview","v","ChevronRightIcon","ToolOpenContext","createContext","Tool","defaultOpen","children","open","setOpen","o","ToolHeader","title","state","toggle","useContext","isRunning","BracesIcon","ChevronDownIcon","ToolContent","className","children","props","open","useContext","ToolOpenContext","jsx","cn","ToolInput","input","CollapsibleJSON","getUiMeta","output","meta","ui","getResourceUri","uri","getAutoHeight","ToolOutput","errorText","jsxs","useCallback","useEffect","useMemo","useRef","useState","jsx","DEFAULT_RESOURCE_ENDPOINT","MAX_HEIGHT","DEFAULT_HEIGHT","PROTOCOL_VERSION","RESIZE_ANIMATION_MS","HANDSHAKE_TIMEOUT_MS","MAX_RETRIES","McpAppFrame","resourceUri","toolInput","toolResult","resourceEndpoint","isDark","className","autoHeight","onOpenLink","onFollowUp","iframeRef","useRef","toolInputRef","toolResultRef","lastSizeRef","animationRef","initializedRef","retryCountRef","height","setHeight","useState","width","setWidth","onOpenLinkRef","onFollowUpRef","clampHeight","useCallback","h","iframeSrc","useMemo","isDarkRef","useEffect","iframe","disposed","handshakeReceived","debug","args","handshakeTimer","url","postToIframe","msg","handleMessage","event","data","method","id","input","result","content","params","newHeight","newWidth","last","heightChanged","widthChanged","clamped","from","anim","cn","Component","jsx","jsxs","WidgetErrorBoundary","error","Fragment","jsx","jsxs","formatToolName","name","c","MessageList","messages","status","welcomeMessage","resourceEndpoint","isDark","onFollowUp","isLoading","lastMessage","hasMessages","showLoaderBubble","Message","MessageContent","MessageResponse","message","textParts","p","reasoningParts","fileParts","toolParts","isLastAssistant","hasTextContent","part","i","Reasoning","output","resourceUri","getResourceUri","autoHeight","getAutoHeight","Tool","ToolHeader","ToolContent","ToolInput","ToolOutput","WidgetErrorBoundary","McpAppFrame","Attachments","Loader","jsx","Suggestions","suggestions","isLoading","onSelect","className","props","cn","i","suggestion","index","useChat","DefaultChatTransport","useCallback","useRef","useState","useChatEngine","props","api","userHeaders","body","onMessageSent","onResponseReceived","transportRef","messages","sendMessage","status","error","text","setText","handleSubmit","message","hasText","hasFiles","handleTextChange","e","isLoading","lastMessage","hasMessages","showLoaderBubble","useCallback","useEffect","useRef","useState","extractSuggestions","message","part","p","data","isConfigObject","config","useSuggestions","options","messages","status","suggestions","setSuggestions","prevStatusRef","isDynamicEnabled","clear","lastMessage","prevStatus","lastAssistant","m","extracted","useEffect","useRef","useState","TYPE_SPEED_MS","DELETE_SPEED_MS","PAUSE_AFTER_TYPE_MS","PAUSE_AFTER_DELETE_MS","useTypingPlaceholder","text","active","displayed","setDisplayed","timerRef","i","deleting","disposed","tick","DEFAULT_THEME","DARK_THEME","CSS_VAR_MAP","mergeTheme","userTheme","isDarkTheme","theme","hex","r","g","b","themeToCSSProperties","vars","key","cssVars","value","resolved","cssVar","jsx","jsxs","ChatBar","forwardRef","props","ref","userTheme","width","expandedHeight","allowAttachments","welcomeMessage","placeholder","triggerEvent","resourceEndpoint","api","effectiveResourceEndpoint","resolvedTheme","mergeTheme","cssVars","themeToCSSProperties","isDark","isDarkTheme","engine","useChatEngine","suggestionsState","useSuggestions","handleWidgetMessage","useCallback","message","text","c","handleSuggestionSelect","suggestion","animatedPlaceholder","useTypingPlaceholder","isFocused","setIsFocused","useState","isHighlighted","setIsHighlighted","containerRef","useRef","highlightTimerRef","focusInput","container","textarea","useImperativeHandle","useEffect","handler","e","detail","isExpanded","handleClickOutside","handleFocus","cn","Conversation","ConversationContent","MessageList","ConversationScrollButton","Suggestions","PromptInput","PromptInputAddAttachments","PromptInputTextarea","PromptInputSubmit","forwardRef","useCallback","useEffect","useImperativeHandle","useRef","useState","jsx","jsxs","ChatCard","forwardRef","props","ref","userTheme","title","subtitle","showStatus","width","height","allowAttachments","welcomeMessage","placeholder","triggerEvent","resourceEndpoint","api","effectiveResourceEndpoint","resolvedTheme","mergeTheme","cssVars","themeToCSSProperties","isDark","isDarkTheme","engine","useChatEngine","animatedPlaceholder","useTypingPlaceholder","isHighlighted","setIsHighlighted","useState","cardRef","useRef","highlightTimerRef","focusInput","useCallback","container","textarea","suggestionsState","useSuggestions","handleWidgetMessage","message","text","c","handleSuggestionSelect","suggestion","useImperativeHandle","useEffect","handler","e","detail","cn","Conversation","ConversationContent","MessageList","ConversationScrollButton","Suggestions","PromptInput","PromptInputAddAttachments","PromptInputTextarea","PromptInputSubmit"]}
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { McpServer, ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
3
|
export { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
-
import { z } from 'zod';
|
|
4
4
|
import { ZodRawShapeCompat, ShapeOutput } from '@modelcontextprotocol/sdk/server/zod-compat.js';
|
|
5
5
|
export { ZodRawShapeCompat } from '@modelcontextprotocol/sdk/server/zod-compat.js';
|
|
6
6
|
|
|
@@ -372,6 +372,21 @@ type FlowConfig = {
|
|
|
372
372
|
title: string;
|
|
373
373
|
/** Description for the AI (explains when to use this flow) */
|
|
374
374
|
description: string;
|
|
375
|
+
/**
|
|
376
|
+
* Describe the flow's state fields so the AI can pre-fill answers
|
|
377
|
+
* from the user's message via `initialState`.
|
|
378
|
+
* Keys are the field names used in `interrupt({ field })`,
|
|
379
|
+
* values are Zod schemas with `.describe()`.
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```ts
|
|
383
|
+
* fields: {
|
|
384
|
+
* country: z.string().describe("Country the business is based in"),
|
|
385
|
+
* status: z.enum(["registered", "unregistered"]).describe("Business registration status"),
|
|
386
|
+
* }
|
|
387
|
+
* ```
|
|
388
|
+
*/
|
|
389
|
+
fields?: Record<string, z.ZodType>;
|
|
375
390
|
/** Optional tool annotations */
|
|
376
391
|
annotations?: {
|
|
377
392
|
readOnlyHint?: boolean;
|
package/dist/mcp/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function
|
|
2
|
-
`)}async function
|
|
3
|
-
${
|
|
1
|
+
function P(){return typeof window<"u"&&"openai"in window?"openai":"mcp-apps"}function J(){return P()==="openai"}function z(){return P()==="mcp-apps"}var S="__start__",y="__end__",b=Symbol.for("waniwani.flow.interrupt"),I=Symbol.for("waniwani.flow.widget");function F(e){return{__type:b,...e}}function O(e,t){return{__type:I,resource:e,...t}}function E(e){return typeof e=="object"&&e!==null&&"__type"in e&&e.__type===b}function A(e){return typeof e=="object"&&e!==null&&"__type"in e&&e.__type===I}import{z as f}from"zod";function B(e){let t=e.description??"",r=e._zod?.def;if(r?.type==="enum"&&r.entries){let i=Object.keys(r.entries).map(c=>`"${c}"`).join(" | ");return t?`${i} \u2014 ${t}`:i}return t}function Y(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 r=Object.entries(e.fields).map(([i,c])=>{let m=B(c);return m?`\`${i}\` (${m})`:`\`${i}\``}).join(", ");t.push(` Known fields: ${r}.`)}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. Do NOT call this tool again \u2014 the widget handles the callback.',' - `"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 x(e,t){return e.type==="direct"?e.to:e.condition(t)}async function k(e,t,r,i,c,m){let s=e,o={...t},u=50,g=0;for(;g++<u;){if(s===y)return{text:JSON.stringify({status:"complete",state:o}),data:{status:"complete",state:o}};let a=r.get(s);if(!a)return{text:JSON.stringify({status:"error",error:`Unknown node: "${s}"`}),data:{status:"error"}};try{let n=await a(o,m);if(E(n)){let l=o[n.field];if(l!=null&&l!==""){let p=i.get(s);if(!p)return{text:JSON.stringify({status:"error",error:`No outgoing edge from node "${s}"`}),data:{status:"error"}};s=await x(p,o);continue}return{text:JSON.stringify({status:"interrupt",step:s,question:n.question,field:n.field,suggestions:n.suggestions,...n.context?{context:n.context}:{},state:o}),data:{status:"interrupt",step:s,state:o}}}if(A(n)){let l=n.resource;return{text:JSON.stringify({status:"widget",step:s,widgetId:l.id,description:n.description,state:o}),data:{...n.data,__flow:{flowId:c,step:s,state:o}},widgetMeta:{"openai/outputTemplate":l.openaiUri,"openai/widgetAccessible":!0,"openai/resultCanProduceWidget":!0,ui:{resourceUri:l.mcpUri}}}}o={...o,...n};let d=i.get(s);if(!d)return{text:JSON.stringify({status:"error",error:`No outgoing edge from node "${s}"`}),data:{status:"error"}};s=await x(d,o)}catch(n){let d=n instanceof Error?n.message:String(n);return{text:JSON.stringify({status:"error",step:s,error:d,state:o}),data:{status:"error",error:d}}}}return{text:JSON.stringify({status:"error",error:"Flow exceeded maximum iterations (possible infinite loop)"}),data:{status:"error"}}}var L={action:f.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:f.string().optional().describe("Current step name (from the previous response)"),state:f.record(f.string(),f.unknown()).optional().describe("Flow state \u2014 pass back exactly as received"),answer:f.string().optional().describe("The user's answer (for interrupt steps)"),widgetResult:f.record(f.string(),f.unknown()).optional().describe("Data returned by a widget callback"),initialState:f.record(f.string(),f.unknown()).optional().describe(`Pre-filled answers extracted from the user's message (only for action: "start")`)};function H(e){let{config:t,nodes:r,edges:i}=e,c=Y(t),m=`${t.description}
|
|
3
|
+
${c}`;async function s(o,u){let g=o.state??{};if(o.action==="start"){let a=i.get(S);if(!a)return{text:JSON.stringify({status:"error",error:"No start edge"}),data:{status:"error"}};let n=o.initialState?{...g,...o.initialState}:g,d=await x(a,n);return k(d,n,r,i,t.id,u)}if(o.action==="continue"){if(!o.step)return{text:JSON.stringify({status:"error",error:'Missing "step" for continue action'}),data:{status:"error"}};let a={...g};if(o.answer){let l=r.get(o.step);if(l)try{let p=await l(a,u);E(p)&&p.field&&(a={...a,[p.field]:o.answer})}catch{}}let n=i.get(o.step);if(!n)return{text:JSON.stringify({status:"error",error:`No edge from step "${o.step}"`}),data:{status:"error"}};let d=await x(n,a);return k(d,a,r,i,t.id,u)}if(o.action==="widget_result"){if(!o.step)return{text:JSON.stringify({status:"error",error:'Missing "step" for widget_result action'}),data:{status:"error"}};let a={...g,...o.widgetResult??{}},n=i.get(o.step);if(!n)return{text:JSON.stringify({status:"error",error:`No edge from step "${o.step}"`}),data:{status:"error"}};let d=await x(n,a);return k(d,a,r,i,t.id,u)}return{text:JSON.stringify({status:"error",error:`Unknown action: "${o.action}"`}),data:{status:"error"}}}return{id:t.id,title:t.title,description:m,async register(o){o.registerTool(t.id,{title:t.title,description:m,inputSchema:L,annotations:t.annotations},(async(u,g)=>{let n=g._meta??{},d=await s(u,n);return{content:[{type:"text",text:d.text}],structuredContent:d.data,_meta:{...d.widgetMeta??{},...n}}}))}}}var T=class{nodes=new Map;edges=new Map;config;constructor(t){this.config=t}addNode(t,r,i){if(t===S||t===y)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;if(typeof r=="function")c=r;else{if(!i)throw new Error(`addNode("${t}", config, handler) requires a handler as the third argument`);c=i}return this.nodes.set(t,c),this}addEdge(t,r){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:r}),this}addConditionalEdge(t,r){if(this.edges.has(t))throw new Error(`Node "${t}" already has an outgoing edge.`);return this.edges.set(t,{type:"conditional",condition:r}),this}compile(){return this.validate(),H({config:this.config,nodes:new Map(this.nodes),edges:new Map(this.edges)})}validate(){if(!this.edges.has(S))throw new Error('Flow must have an entry point. Add an edge from START: .addEdge(START, "first_node")');let t=this.edges.get(S);if(t?.type==="direct"&&t.to!==y&&!this.nodes.has(t.to))throw new Error(`START edge references non-existent node: "${t.to}"`);for(let[r,i]of this.edges){if(r!==S&&!this.nodes.has(r))throw new Error(`Edge from non-existent node: "${r}"`);if(i.type==="direct"&&i.to!==y&&!this.nodes.has(i.to))throw new Error(`Edge from "${r}" references non-existent node: "${i.to}"`)}for(let[r]of this.nodes)if(!this.edges.has(r))throw new Error(`Node "${r}" has no outgoing edge. Add one with .addEdge("${r}", ...) or .addConditionalEdge("${r}", ...)`)}};function $(e){return new T(e)}var R="text/html+skybridge",C="text/html;profile=mcp-app",W=async(e,t)=>{let r=e.endsWith("/")?e.slice(0,-1):e;return await(await fetch(`${r}${t}`)).text()};function D(e){return{"openai/widgetDescription":e.description,"openai/widgetPrefersBorder":e.prefersBorder,"openai/widgetDomain":e.widgetDomain,...e.widgetCSP&&{"openai/widgetCSP":e.widgetCSP}}}function q(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 v(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 U(e){let{id:t,title:r,description:i,baseUrl:c,htmlPath:m,widgetDomain:s,prefersBorder:o=!0,autoHeight:u,widgetCSP:g}=e,a=`ui://widgets/apps-sdk/${t}.html`,n=`ui://widgets/ext-apps/${t}.html`,d=null,l=()=>(d||(d=W(c,m)),d),p=i;async function j(_){let M=await l();_.registerResource(`${t}-openai-widget`,a,{title:r,description:p,mimeType:R,_meta:{"openai/widgetDescription":p,"openai/widgetPrefersBorder":o}},async N=>({contents:[{uri:N.href,mimeType:R,text:M,_meta:D({description:p,prefersBorder:o,widgetDomain:s,widgetCSP:g})}]})),_.registerResource(`${t}-mcp-widget`,n,{title:r,description:p,mimeType:C,_meta:{ui:{prefersBorder:o}}},async N=>({contents:[{uri:N.href,mimeType:C,text:M,_meta:q({description:p,prefersBorder:o,widgetCSP:g})}]}))}return{id:t,title:r,description:i,openaiUri:a,mcpUri:n,autoHeight:u,register:j}}function G(e,t){let{resource:r,description:i,inputSchema:c,annotations:m}=e,s=e.id??r?.id,o=e.title??r?.title;if(!s)throw new Error("createTool: `id` is required when no resource is provided");if(!o)throw new Error("createTool: `title` is required when no resource is provided");let u=r?v({openaiTemplateUri:r.openaiUri,mcpTemplateUri:r.mcpUri,invoking:e.invoking??"Loading...",invoked:e.invoked??"Loaded",autoHeight:r.autoHeight}):void 0;return{id:s,title:o,description:i,async register(g){g.registerTool(s,{title:o,description:i,inputSchema:c,annotations:m,...u&&{_meta:u}},(async(a,n)=>{let l=n._meta??{},p=await t(a,{extra:{_meta:l}});return r&&p.data?{content:[{type:"text",text:p.text}],structuredContent:p.data,_meta:{...u,...l}}:{content:[{type:"text",text:p.text}]}}))}}}async function Z(e,t){await Promise.all(t.map(r=>r.register(e)))}export{y as END,S as START,T as StateGraph,$ as createFlow,U as createResource,G as createTool,P as detectPlatform,F as interrupt,z as isMCPApps,J as isOpenAI,Z as registerTools,O 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/flows/state-graph.ts","../../src/mcp/server/flows/create-flow.ts","../../src/mcp/server/resources/meta.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 { 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\t/** Resource to display when this node returns a WidgetSignal */\n\tresource?: RegisteredResource;\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/** 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 type {\n\tEdge,\n\tFlowConfig,\n\tMcpServer,\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\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};\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\nfunction buildFlowProtocol(_config: FlowConfig): string {\n\treturn [\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.',\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. Do NOT call this tool again — the widget handles the callback.',\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 skip steps or invent state values.\",\n\t].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\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\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\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};\n\nexport function compileFlow<TState extends Record<string, unknown>>(\n\tinput: CompileInput<TState>,\n): RegisteredFlow {\n\tconst { config, nodes, edges } = input;\n\tconst protocol = buildFlowProtocol(config);\n\tconst fullDescription = `${config.description}\\n${protocol}`;\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\t\t\tconst firstNode = await resolveNextNode(startEdge, state);\n\t\t\treturn executeFrom(firstNode, state, nodes, edges, config.id, meta);\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(nextNode, updatedState, nodes, edges, config.id, meta);\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\tconst updatedState = {\n\t\t\t\t...state,\n\t\t\t\t...(args.widgetResult ?? {}),\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(nextNode, updatedState, nodes, edges, config.id, meta);\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},\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\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: result.text }],\n\t\t\t\t\t\tstructuredContent: result.data,\n\t\t\t\t\t\t_meta: {\n\t\t\t\t\t\t\t...(result.widgetMeta ?? {}),\n\t\t\t\t\t\t\t..._meta,\n\t\t\t\t\t\t},\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 {\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 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) and a handler.\n\t */\n\taddNode(name: string, config: NodeConfig, handler: NodeHandler<TState>): this;\n\taddNode(\n\t\tname: string,\n\t\tconfigOrHandler: NodeConfig | 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\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}\n\n\t\tthis.nodes.set(name, handler);\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\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 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 {\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","buildFlowProtocol","_config","resolveNextNode","edge","state","executeFrom","startNodeName","initialState","nodes","edges","flowId","meta","currentNode","MAX_ITERATIONS","iterations","END","handler","result","isInterrupt","isWidget","resource","error","message","inputSchema","z","compileFlow","input","config","protocol","fullDescription","handleToolCall","args","startEdge","START","firstNode","updatedState","nextNode","server","extra","_meta","StateGraph","config","name","configOrHandler","maybeHandler","START","END","handler","from","to","condition","compileFlow","startEdge","edge","createFlow","config","StateGraph","MIME_TYPE_OPENAI","MIME_TYPE_MCP","fetchHtml","baseUrl","path","normalizedBase","buildOpenAIResourceMeta","config","buildMcpAppsResourceMeta","csp","buildToolMeta","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/flows/state-graph.ts","../../src/mcp/server/flows/create-flow.ts","../../src/mcp/server/resources/meta.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\t/** Resource to display when this node returns a WidgetSignal */\n\tresource?: RegisteredResource;\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 type {\n\tEdge,\n\tFlowConfig,\n\tMcpServer,\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\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. Do NOT call this tool again — the widget handles the callback.',\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\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\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, edges } = input;\n\tconst protocol = buildFlowProtocol(config);\n\tconst fullDescription = `${config.description}\\n${protocol}`;\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(firstNode, startState, nodes, edges, config.id, meta);\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(nextNode, updatedState, nodes, edges, config.id, meta);\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\tconst updatedState = {\n\t\t\t\t...state,\n\t\t\t\t...(args.widgetResult ?? {}),\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(nextNode, updatedState, nodes, edges, config.id, meta);\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},\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\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: result.text }],\n\t\t\t\t\t\tstructuredContent: result.data,\n\t\t\t\t\t\t_meta: {\n\t\t\t\t\t\t\t...(result.widgetMeta ?? {}),\n\t\t\t\t\t\t\t..._meta,\n\t\t\t\t\t\t},\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 {\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 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) and a handler.\n\t */\n\taddNode(name: string, config: NodeConfig, handler: NodeHandler<TState>): this;\n\taddNode(\n\t\tname: string,\n\t\tconfigOrHandler: NodeConfig | 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\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}\n\n\t\tthis.nodes.set(name, handler);\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\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 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 {\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","describeZodField","schema","desc","def","vals","v","buildFlowProtocol","config","lines","fieldList","key","info","resolveNextNode","edge","state","executeFrom","startNodeName","initialState","nodes","edges","flowId","meta","currentNode","MAX_ITERATIONS","iterations","END","handler","result","isInterrupt","existingValue","isWidget","resource","error","message","inputSchema","z","compileFlow","input","protocol","fullDescription","handleToolCall","args","startEdge","START","startState","firstNode","updatedState","nextNode","server","extra","_meta","StateGraph","config","name","configOrHandler","maybeHandler","START","END","handler","from","to","condition","compileFlow","startEdge","edge","createFlow","config","StateGraph","MIME_TYPE_OPENAI","MIME_TYPE_MCP","fetchHtml","baseUrl","path","normalizedBase","buildOpenAIResourceMeta","config","buildMcpAppsResourceMeta","csp","buildToolMeta","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"]}
|