@waniwani/sdk 0.1.1 → 0.1.2
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 -2
- package/dist/chat/index.js +1 -1
- package/dist/chat/index.js.map +1 -1
- package/package.json +1 -1
package/dist/chat/index.d.ts
CHANGED
|
@@ -36,8 +36,11 @@ interface ChatTheme {
|
|
|
36
36
|
toolCardColor?: string;
|
|
37
37
|
}
|
|
38
38
|
interface SuggestionsConfig {
|
|
39
|
-
/**
|
|
40
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Initial suggestions to show before the user sends their first message.
|
|
41
|
+
* Defaults to an empty array.
|
|
42
|
+
*/
|
|
43
|
+
initial?: string[];
|
|
41
44
|
}
|
|
42
45
|
interface ChatBaseProps {
|
|
43
46
|
/** WaniWani project API key */
|
package/dist/chat/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{forwardRef as Mo,useCallback as ve,useEffect as pt,useImperativeHandle as ko,useRef as mt,useState as dt}from"react";import{ArrowDownIcon as xt}from"lucide-react";import{useCallback as yt}from"react";import{StickToBottom as Ue,useStickToBottomContext as Ct}from"use-stick-to-bottom";import{clsx as gt}from"clsx";import{twMerge as ht}from"tailwind-merge";function l(...t){return ht(gt(t))}import{jsx as bt}from"react/jsx-runtime";var V=({className:t,variant:e="default",size:o="default",type:n="button",...r})=>bt("button",{type:n,className:l("inline-flex cursor-pointer items-center justify-center rounded-md font-medium transition-colors disabled:pointer-events-none disabled:opacity-50",e==="default"&&"bg-primary text-primary-foreground hover:bg-primary/90",e==="outline"&&"border border-border bg-background hover:bg-accent hover:text-accent-foreground",e==="ghost"&&"hover:bg-accent hover:text-accent-foreground",o==="default"&&"h-9 px-4 py-2 text-sm",o==="sm"&&"h-8 px-3 text-xs",o==="icon"&&"size-9",o==="icon-sm"&&"size-7",t),...r});import{jsx as oe}from"react/jsx-runtime";var re=({className:t,...e})=>oe(Ue,{className:l("relative flex-1 overflow-y-hidden",t),initial:"smooth",resize:"smooth",role:"log",...e}),ne=({className:t,...e})=>oe(Ue.Content,{className:l("flex flex-col gap-8 p-4",t),...e}),se=({className:t,...e})=>{let{isAtBottom:o,scrollToBottom:n}=Ct(),r=yt(()=>{n()},[n]);return!o&&oe(V,{className:l("absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full",t),onClick:r,size:"icon",variant:"outline",...e,children:oe(xt,{className:"size-4"})})};import{ArrowUpIcon as vt,LoaderIcon as Tt,PaperclipIcon as wt,SquareIcon as Pt,XIcon as St}from"lucide-react";import{nanoid as Mt}from"nanoid";import{createContext as kt,useCallback as q,useContext as Et,useEffect as Se,useMemo as It,useRef as Me,useState as Oe}from"react";import{jsx as O,jsxs as We}from"react/jsx-runtime";var Rt=async t=>{try{let o=await(await fetch(t)).blob();return new Promise(n=>{let r=new FileReader;r.onloadend=()=>n(r.result),r.onerror=()=>n(null),r.readAsDataURL(o)})}catch{return null}},ze=kt(null),_e=()=>{let t=Et(ze);if(!t)throw new Error("usePromptInputAttachments must be used within a PromptInput");return t},ae=({className:t,accept:e,multiple:o,globalDrop:n,maxFiles:r,maxFileSize:s,onSubmit:a,children:u,...C})=>{let m=Me(null),c=Me(null),[g,T]=Oe([]),v=Me(g);Se(()=>{v.current=g},[g]);let S=q(()=>{m.current?.click()},[]),w=q(f=>{let h=[...f];if(h.length===0)return;let M=b=>s?b.size<=s:!0,N=h.filter(M);T(b=>{let I=typeof r=="number"?Math.max(0,r-b.length):void 0,R=typeof I=="number"?N.slice(0,I):N;return[...b,...R.map(k=>({filename:k.name,id:Mt(),mediaType:k.type,type:"file",url:URL.createObjectURL(k)}))]})},[r,s]),d=q(f=>{T(h=>{let M=h.find(N=>N.id===f);return M?.url&&URL.revokeObjectURL(M.url),h.filter(N=>N.id!==f)})},[]),i=q(()=>{T(f=>{for(let h of f)h.url&&URL.revokeObjectURL(h.url);return[]})},[]);Se(()=>()=>{for(let f of v.current)f.url&&URL.revokeObjectURL(f.url)},[]);let p=q(f=>{f.currentTarget.files&&w(f.currentTarget.files),f.currentTarget.value=""},[w]);Se(()=>{if(!n)return;let f=M=>{M.dataTransfer?.types?.includes("Files")&&M.preventDefault()},h=M=>{M.dataTransfer?.types?.includes("Files")&&M.preventDefault(),M.dataTransfer?.files&&M.dataTransfer.files.length>0&&w(M.dataTransfer.files)};return document.addEventListener("dragover",f),document.addEventListener("drop",h),()=>{document.removeEventListener("dragover",f),document.removeEventListener("drop",h)}},[w,n]);let y=q(async f=>{f.preventDefault();let h=f.currentTarget,N=new FormData(h).get("message")||"";h.reset();let b=await Promise.all(g.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:b,text:N},f);I instanceof Promise&&await I,i()}catch{}},[g,a,i]),L=It(()=>({add:w,clear:i,files:g,openFileDialog:S,remove:d}),[g,w,d,i,S]);return We(ze.Provider,{value:L,children:[O("input",{accept:e,"aria-label":"Upload files",className:"hidden",multiple:o,onChange:p,ref:m,title:"Upload files",type:"file"}),O("form",{className:l("flex w-full flex-col rounded-lg border border-border bg-background",t),onSubmit:y,ref:c,...C,children:u})]})};var ie=({onChange:t,onKeyDown:e,className:o,placeholder:n="What would you like to know?",...r})=>{let s=_e(),[a,u]=Oe(!1),C=q(c=>{if(e?.(c),!c.defaultPrevented){if(c.key==="Enter"){if(a||c.nativeEvent.isComposing||c.shiftKey)return;c.preventDefault();let{form:g}=c.currentTarget;if(g?.querySelector('button[type="submit"]')?.disabled)return;g?.requestSubmit()}if(c.key==="Backspace"&&c.currentTarget.value===""&&s.files.length>0){c.preventDefault();let g=s.files.at(-1);g&&s.remove(g.id)}}},[e,a,s]),m=q(c=>{let g=c.clipboardData?.items;if(!g)return;let T=[];for(let v of g)if(v.kind==="file"){let S=v.getAsFile();S&&T.push(S)}T.length>0&&(c.preventDefault(),s.add(T))},[s]);return O("textarea",{className:l("field-sizing-content max-h-48 min-h-16 w-full resize-none border-0 bg-transparent px-3 py-3 text-sm outline-none placeholder:text-muted-foreground",o),name:"message",onCompositionEnd:()=>u(!1),onCompositionStart:()=>u(!0),onKeyDown:C,onPaste:m,placeholder:n,onChange:t,...r})},le=({className:t,status:e,onStop:o,onClick:n,children:r,...s})=>{let a=e==="submitted"||e==="streaming",u=O(vt,{className:"size-4"});e==="submitted"?u=O(Tt,{className:"size-4 animate-spin"}):e==="streaming"&&(u=O(Pt,{className:"size-4"}));let C=q(m=>{if(a&&o){m.preventDefault(),o();return}n?.(m)},[a,o,n]);return O(V,{"aria-label":a?"Stop":"Submit",className:l("bg-foreground text-background hover:bg-foreground",t),onClick:C,size:"icon-sm",type:a&&o?"button":"submit",variant:"ghost",...s,children:r??u})},ue=({className:t,children:e,...o})=>{let n=_e();return n.files.length>0?We(V,{className:l("group relative",t),onClick:()=>n.clear(),size:"icon-sm",type:"button",variant:"ghost","aria-label":"Remove all attachments",...o,children:[O("span",{className:"flex size-5 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground transition-opacity group-hover:opacity-0",children:n.files.length}),O(St,{className:"absolute size-4 opacity-0 transition-opacity group-hover:opacity-100"})]}):O(V,{className:l(t),onClick:()=>n.openFileDialog(),size:"icon-sm",type:"button",variant:"ghost",...o,children:e??O(wt,{className:"size-4"})})};import{FileIcon as Lt}from"lucide-react";import{jsx as Y,jsxs as At}from"react/jsx-runtime";var $e=({files:t,className:e,...o})=>t.length===0?null:Y("div",{className:l("flex flex-wrap gap-1.5",e),...o,children:t.map((n,r)=>Y(Nt,{file:n},r))});function Nt({file:t}){return t.mediaType?.startsWith("image/")&&t.url?Y("img",{src:t.url,alt:t.filename??"attachment",className:"h-16 max-w-32 rounded object-cover"}):At("span",{className:"inline-flex items-center gap-1.5 rounded bg-background/20 px-2 py-1 text-xs",children:[Y(Lt,{className:"size-3 shrink-0"}),Y("span",{className:"max-w-24 truncate",children:t.filename??"file"})]})}import{jsx as je}from"react/jsx-runtime";var ke=({className:t,size:e=5,...o})=>je("div",{className:l("flex items-center gap-1",t),...o,children:[0,1,2].map(n=>je("div",{className:"rounded-full bg-muted-foreground/60",style:{width:e,height:e,animation:"ww-pulse 1.4s ease-in-out infinite",animationDelay:`${n*.2}s`}},n))});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 Ee}from"react/jsx-runtime";var ce=({className:t,from:e,...o})=>Ee("div",{className:l("group flex w-full max-w-[95%] flex-col gap-2",e==="user"?"is-user ml-auto justify-end":"is-assistant",t),...o}),pe=({children:t,className:e,...o})=>Ee("div",{className:l("flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-base","group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-user-bubble group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-primary-foreground","group-[.is-assistant]:text-foreground",e),...o,children:t}),Ut={cjk:Ht,code:Bt},me=Dt(({className:t,...e})=>Ee(Ft,{className:l("size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",t),plugins:Ut,...e}),(t,e)=>t.children===e.children);me.displayName="MessageResponse";import{jsx as Ot}from"react/jsx-runtime";function qe({className:t,text:e,...o}){return e?Ot("pre",{className:l("mb-2 overflow-x-auto whitespace-pre-wrap break-words text-xs font-mono text-muted-foreground",t),...o,children:e}):null}import{BracesIcon as zt,CheckIcon as _t,ChevronDownIcon as Wt,ChevronRightIcon as $t,ClipboardCopyIcon as jt,ServerIcon as br}from"lucide-react";import{createContext as qt,useCallback as Kt,useContext as Ve,useEffect as Vt,useMemo as Jt,useRef as Gt,useState as Ie}from"react";import{Fragment as Ke,jsx as P,jsxs as J}from"react/jsx-runtime";function Xt(t,e=80){if(t==null)return String(t);if(typeof t!="object")return String(t).slice(0,e);if(Array.isArray(t))return`Array(${t.length})`;let o=JSON.stringify(t);if(o.length<=e)return o;let n=Object.entries(t),r=[],s=e-2;for(let[a,u]of n){if(s<=8)break;let C=a.length>4?`${a.slice(0,4)}\u2026`:a,m;typeof u=="string"?m=u.length>2?`'${u.slice(0,1)}\u2026`:`'${u}'`:Array.isArray(u)?m=`Array(${u.length})`:typeof u=="object"&&u!==null?m="{\u2026}":m=String(u);let c=`${C}\u2009${m}`;r.push(c),s-=c.length+3}return`{${r.join(", ")}}`}function Yt({text:t,className:e}){let[o,n]=Ie(!1),r=Gt(null);Vt(()=>()=>{r.current&&clearTimeout(r.current)},[]);let s=Kt(async a=>{a.stopPropagation();try{await navigator.clipboard.writeText(t),n(!0),r.current&&clearTimeout(r.current),r.current=setTimeout(()=>n(!1),2e3)}catch{}},[t]);return P(V,{variant:"ghost",size:"sm",onClick:s,className:l("h-auto gap-1 px-1.5 py-0.5 text-xs text-muted-foreground hover:text-foreground",e),children:o?J(Ke,{children:[P(_t,{className:"size-3.5"}),P("span",{children:"Copied"})]}):J(Ke,{children:[P(jt,{className:"size-3.5"}),P("span",{children:"Copy"})]})})}function Je({data:t,label:e,className:o,...n}){let[r,s]=Ie(!1),a=Jt(()=>JSON.stringify(t,null,2),[t]),u=Xt(t);return J("div",{className:l("rounded-lg bg-tool-card",o),...n,children:[J("div",{className:"flex items-center justify-between px-3 pt-2.5 pb-1.5",children:[P("span",{className:"text-xs font-medium text-muted-foreground",children:e}),P(Yt,{text:a})]}),J("button",{type:"button",onClick:()=>s(C=>!C),className:"flex w-full items-start gap-2 px-3 pb-3 text-left",children:[P($t,{className:l("mt-0.5 size-3.5 shrink-0 text-muted-foreground transition-transform duration-150",r&&"rotate-90")}),r?P("pre",{className:"overflow-x-auto text-xs font-mono text-foreground whitespace-pre-wrap break-all",children:P("code",{children:a})}):P("span",{className:"truncate text-xs font-mono text-foreground/80",children:u})]})]})}var Re=qt({open:!1,toggle:()=>{}});function Ge({className:t,defaultOpen:e=!1,children:o,...n}){let[r,s]=Ie(e);return P(Re.Provider,{value:{open:r,toggle:()=>s(a=>!a)},children:P("div",{className:l("mb-4 w-full",t),"data-state":r?"open":"closed",...n,children:o})})}function Xe({className:t,title:e,state:o,...n}){let{open:r,toggle:s}=Ve(Re),a=o==="input-available"||o==="input-streaming";return J("button",{type:"button",onClick:s,className:l("flex w-full items-center justify-between gap-3 py-1.5",t),"aria-expanded":r,...n,children:[J("div",{className:"flex min-w-0 items-center gap-2",children:[P(zt,{className:"size-4 shrink-0 text-muted-foreground"}),P("span",{className:"truncate text-sm font-medium",children:e}),a&&P("span",{className:"size-2 shrink-0 rounded-full bg-primary animate-pulse"})]}),P(Wt,{className:l("size-4 shrink-0 text-muted-foreground transition-transform duration-200",r&&"rotate-180")})]})}function Ye({className:t,children:e,...o}){let{open:n}=Ve(Re);return P("div",{className:l("grid transition-[grid-template-rows,opacity] duration-200 ease-out",n?"grid-rows-[1fr] opacity-100":"grid-rows-[0fr] opacity-0"),children:P("div",{className:"min-h-0 overflow-hidden",children:P("div",{className:l("mt-2 space-y-3 rounded-lg border border-border bg-background p-3",t),...o,children:e})})})}function Ze({className:t,input:e,...o}){return P(Je,{data:e,label:"Request",className:t,...o})}function Qe(t){if(typeof t!="object"||t===null)return;let e=t._meta;if(typeof e!="object"||e===null)return;let o=e.ui;if(!(typeof o!="object"||o===null))return o}function et(t){let e=Qe(t)?.resourceUri;return typeof e=="string"?e:void 0}function tt(t){return Qe(t)?.autoHeight===!0}function ot({className:t,output:e,errorText:o,...n}){return e||o?o?J("div",{className:l("space-y-2",t),...n,children:[P("h4",{className:"text-xs font-medium uppercase tracking-wide text-muted-foreground",children:"Error"}),P("div",{className:"rounded-lg bg-destructive/10 p-3 text-xs text-destructive",children:o})]}):P(Je,{data:e,label:"Response",className:t,...n}):null}import{useCallback as Zt,useEffect as rt,useMemo as Qt,useRef as W,useState as nt}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,Le=3;function Ne({resourceUri:t,toolInput:e,toolResult:o,resourceEndpoint:n=eo,isDark:r=!1,className:s,autoHeight:a=!0,onOpenLink:u,onFollowUp:C}){let m=W(null),c=W(e),g=W(o),T=W({width:0,height:0}),v=W(null),S=W(!1),w=W(0),[d,i]=nt(oo),[p,y]=nt(void 0),L=W(u),f=W(C);c.current=e,g.current=o,L.current=u,f.current=C;let h=Zt(b=>a?Math.max(b,0):Math.min(Math.max(b,50),to),[a]),M=Qt(()=>`${n}?uri=${encodeURIComponent(t)}`,[n,t]),N=W(r);return N.current=r,rt(()=>{if(!S.current)return;let b=m.current;b?.contentWindow&&b.contentWindow.postMessage({jsonrpc:"2.0",method:"ui/notifications/host-context-changed",params:{theme:r?"dark":"light"}},"*")},[r]),rt(()=>{let b=m.current;if(!b)return;let I=!1,R=!1,k=(...E)=>console.debug("[McpAppFrame]",...E);k("effect mounted, waiting for handshake");let A=setTimeout(()=>{if(I||R)return;if(w.current>=Le){k("handshake failed after",Le,"retries, giving up");return}w.current+=1,k("handshake timeout, reloading iframe (retry",w.current,"of",Le,")");let E=new URL(b.src);E.searchParams.set("_retry",String(w.current)),b.src=E.toString()},so),x=E=>{k("\u2192 send",E.method??`response:${E.id}`,E),b.contentWindow?.postMessage(E,"*")},H=E=>{if(I||E.source!==b.contentWindow)return;let D=E.data;if(!D||typeof D!="object"||D.jsonrpc!=="2.0")return;let j=D.method,F=D.id;if(k("\u2190 recv",j??`response:${F}`,D),j==="ui/initialize"&&F!=null){R=!0,clearTimeout(A),k("handshake started"),x({jsonrpc:"2.0",id:F,result:{protocolVersion:D.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"),S.current=!0;let z=c.current,_=g.current;x({jsonrpc:"2.0",method:"ui/notifications/tool-input",params:{arguments:z}});let K=_.content??[{type:"text",text:JSON.stringify(_)}];x({jsonrpc:"2.0",method:"ui/notifications/tool-result",params:{content:K,structuredContent:_.structuredContent}});return}if(j==="ui/notifications/size-changed"){let z=D.params,_=typeof z?.height=="number"?z.height:void 0,K=typeof z?.width=="number"?z.width:void 0,G=T.current,Te=_!==void 0&&_!==G.height,we=K!==void 0&&K!==G.width;if(k("size-changed",{newHeight:_,newWidth:K,lastHeight:G.height,lastWidth:G.width,heightChanged:Te,widthChanged:we}),!Te&&!we)return;if(Te&&_!==void 0){G.height=_;let Pe=h(_),Fe=b.getBoundingClientRect().height;if(v.current&&(v.current.cancel(),v.current=null),i(Pe),b.animate&&Math.abs(Fe-Pe)>2){let te=b.animate([{height:`${Fe}px`},{height:`${Pe}px`}],{duration:no,easing:"ease-out",fill:"forwards"});v.current=te,te.onfinish=()=>{v.current===te&&(te.cancel(),v.current=null)}}}we&&a&&K!==void 0&&(G.width=K,y(K));return}if(j==="ui/open-link"&&F!=null){let z=D.params?.url;typeof z=="string"&&(L.current?L.current(z):window.open(z,"_blank","noopener,noreferrer")),x({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/message"&&F!=null){f.current&&D.params&&f.current(D.params),x({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/request-display-mode"&&F!=null){x({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/resource-teardown"&&F!=null){x({jsonrpc:"2.0",id:F,result:{}});return}j==="ping"&&F!=null&&x({jsonrpc:"2.0",id:F,result:{}})};return window.addEventListener("message",H),()=>{k("effect cleanup (disposed)"),I=!0,clearTimeout(A),window.removeEventListener("message",H)}},[a,h]),ao("iframe",{ref:m,src:M,sandbox:"allow-scripts allow-forms allow-same-origin",className:l("rounded-md border border-border",s),style:{height:d,minWidth:p?`min(${p}px, 100%)`:void 0,width:"100%",border:"none",colorScheme:"auto"},title:"MCP App"})}import{Component as io}from"react";import{jsx as st,jsxs as lo}from"react/jsx-runtime";var de=class extends io{state={hasError:!1};static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e){console.warn("[WaniWani] Widget failed to render:",e.message)}render(){return this.state.hasError?lo("div",{className:"flex items-center justify-between rounded-md border border-border bg-muted/50 px-4 py-3 text-sm text-muted-foreground",children:[st("span",{children:"Widget failed to load"}),st("button",{type:"button",onClick:()=>this.setState({hasError:!1}),className:"text-xs font-medium text-primary hover:underline",children:"Retry"})]}):this.props.children}};import{Fragment as co,jsx as B,jsxs as X}from"react/jsx-runtime";function uo(t){return t.replace(/[-_]/g," ").replace(/^\w/,e=>e.toUpperCase())}function fe({messages:t,status:e,welcomeMessage:o,resourceEndpoint:n,isDark:r,onFollowUp:s}){let a=e==="submitted"||e==="streaming",u=t[t.length-1],C=t.length>0,m=a&&(!C||u.role==="user");return X(co,{children:[o&&B(ce,{from:"assistant",children:B(pe,{children:B(me,{children:o})})}),t.map(c=>{let g=c.parts.filter(i=>i.type==="text"),T=c.parts.filter(i=>i.type==="reasoning"),v=c.parts.filter(i=>i.type==="file"),S=c.parts.filter(i=>"toolCallId"in i),w=c===u&&c.role==="assistant",d=g.length>0;return X(ce,{from:c.role,children:[T.map((i,p)=>B(qe,{text:i.text},`reasoning-${c.id}-${p}`)),S.map(i=>{let p="output"in i?i.output:void 0,y=p!==void 0?et(p):void 0,L=p!==void 0?tt(p):!1;return X("div",{children:[X(Ge,{defaultOpen:i.state==="output-available",children:[B(Xe,{title:i.title??uo(i.toolName),state:i.state}),X(Ye,{children:[B(Ze,{input:i.input}),p!==void 0&&B(ot,{output:p,errorText:"errorText"in i?i.errorText:void 0})]})]}),y&&p!==void 0&&B(de,{children:B(Ne,{resourceUri:y,toolInput:i.input??{},toolResult:{content:p.content,structuredContent:p.structuredContent},resourceEndpoint:n,isDark:r,autoHeight:L,onFollowUp:s})})]},i.toolCallId)}),X(pe,{children:[v.length>0&&B($e,{files:v}),d?g.map((i,p)=>B(me,{children:i.type==="text"?i.text:""},`${c.id}-${p}`)):w&&a&&B(ke,{})]})]},c.id)}),m&&B(ce,{from:"assistant",children:B(pe,{children:B(ke,{})})})]})}import{jsx as Ae}from"react/jsx-runtime";function ge({suggestions:t,isLoading:e,onSelect:o,className:n,...r}){return t.length===0&&!e?null:Ae("div",{className:l("flex flex-wrap gap-2 px-3 py-2",n),...r,children:e?[0,1,2].map(s=>Ae("div",{className:"h-7 rounded-full bg-accent animate-pulse",style:{width:`${60+s*20}px`}},s)):t.map((s,a)=>Ae("button",{type:"button",onClick:()=>o(s),className:l("rounded-full border border-border bg-background px-3 py-1 text-xs","text-foreground hover:bg-accent hover:border-primary/30","transition-all duration-200 ease-out cursor-pointer","animate-[ww-fade-in_0.2s_ease-out_both]"),style:{animationDelay:`${a*50}ms`},children:s},s))})}import{useChat as po}from"@ai-sdk/react";import{DefaultChatTransport as mo}from"ai";import{useCallback as at,useRef as fo,useState as go}from"react";function he(t){let{api:e="https://app.waniwani.ai/api/chat",headers:o,body:n,onMessageSent:r,onResponseReceived:s}=t,a=fo(new mo({api:e,headers:{...o},body:n})),{messages:u,sendMessage:C,status:m}=po({transport:a.current,onFinish(){s?.()},onError(p){console.warn("[WaniWani] Chat error:",p.message)}}),[c,g]=go(""),T=at(p=>{let y=!!p.text?.trim(),L=!!p.files?.length;(y||L)&&(C({text:p.text||"",files:p.files}),r?.(p.text||""),g(""))},[C,r]),v=at(p=>{g(p.target.value)},[]),S=m==="submitted"||m==="streaming",w=u[u.length-1],d=u.length>0,i=S&&(!d||w.role==="user");return{messages:u,status:m,text:c,setText:g,handleSubmit:T,handleTextChange:v,isLoading:S,showLoaderBubble:i,lastMessage:w,hasMessages:d,sendMessage:C}}import{useCallback as ho,useEffect as be,useRef as it,useState as lt}from"react";function bo(t){try{let e=new URL(t);return e.pathname="/api/mcp/suggestions",e.toString()}catch{return"/api/mcp/suggestions"}}function xe(t){let{messages:e,status:o,initialSuggestions:n,suggestions:r,api:s="https://app.waniwani.ai/api/chat",apiKey:a,headers:u}=t,[C,m]=lt([]),[c,g]=lt(!1),T=it(o),v=it(null),S=!!r,w=typeof r=="object"?r.count??3:3,d=e.some(L=>L.role==="user"),i=bo(s),p=ho(()=>{m([]),v.current?.abort(),v.current=null},[]);be(()=>{!d&&n?.length&&m(n)},[d,n]);let y=e[e.length-1];return be(()=>{y?.role==="user"&&p()},[y,p]),be(()=>{let L=T.current;if(T.current=o,L==="streaming"&&o==="ready"&&S){let f=new AbortController;v.current?.abort(),v.current=f,g(!0),fetch(i,{method:"POST",headers:{"Content-Type":"application/json",...a?{Authorization:`Bearer ${a}`}:{},...u},body:JSON.stringify({messages:e,count:w}),signal:f.signal}).then(h=>{if(!h.ok)throw new Error(`Suggestions API error: ${h.status}`);return h.json()}).then(h=>{f.signal.aborted||m(h.suggestions??[])}).catch(h=>{h.name!=="AbortError"&&console.warn("[WaniWani] Failed to fetch suggestions:",h)}).finally(()=>{f.signal.aborted||g(!1)})}},[o,S,i,a,e,w,u]),be(()=>()=>{v.current?.abort()},[]),{suggestions:C,isLoading:c,clear:p}}import{useEffect as xo,useRef as yo,useState as Co}from"react";var vo=50,To=30,wo=2e3,ut=500;function ye(t,e=!0){let[o,n]=Co(""),r=yo(void 0);return xo(()=>{if(!e){n("");return}let s=0,a=!1,u=!1,C=()=>{u||(a?(s--,n(t.slice(0,s)),s<=0?(a=!1,r.current=setTimeout(C,ut)):r.current=setTimeout(C,To)):(s++,n(t.slice(0,s)),s>=t.length?(a=!0,r.current=setTimeout(C,wo)):r.current=setTimeout(C,vo)))};return r.current=setTimeout(C,ut),()=>{u=!0,clearTimeout(r.current)}},[t,e]),o}var ct={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"},Po={backgroundColor:"#212121",headerBackgroundColor:"#1e1e1e",headerTextColor:"#ececec",textColor:"#ececec",primaryForeground:"#ffffff",mutedColor:"#8e8ea0",borderColor:"#303030",assistantBubbleColor:"#2f2f2f",userBubbleColor:"#303030",inputBackgroundColor:"#2f2f2f",primaryColor:"#6366f1",statusColor:"#22c55e",toolCardColor:"#262626"},So={primaryColor:["--ww-primary","--color-primary"],primaryForeground:["--ww-primary-fg","--color-primary-foreground"],backgroundColor:["--ww-bg","--color-background"],textColor:["--ww-text","--color-foreground","--color-accent-foreground"],mutedColor:["--ww-muted","--color-muted-foreground"],borderColor:["--ww-border","--color-border"],assistantBubbleColor:["--ww-assistant-bubble","--color-accent"],userBubbleColor:["--ww-user-bubble"],inputBackgroundColor:["--ww-input-bg","--color-input"],borderRadius:["--ww-radius","--radius"],messageBorderRadius:["--ww-msg-radius"],fontFamily:["--ww-font"],headerBackgroundColor:["--ww-header-bg"],headerTextColor:["--ww-header-text"],statusColor:["--ww-status"],toolCardColor:["--ww-tool-card","--color-tool-card"]};function Z(t){return{...ct,...t}}function Ce(t){let e=t.backgroundColor.replace("#",""),o=parseInt(e.substring(0,2),16),n=parseInt(e.substring(2,4),16),r=parseInt(e.substring(4,6),16);return(o*299+n*587+r*114)/1e3<128}function Q(t){let e={};for(let[o,n]of Object.entries(So)){let r=t[o],s=typeof r=="number"?`${r}px`:String(r);for(let a of n)e[a]=s}return e}import{jsx as $,jsxs as He}from"react/jsx-runtime";var Be=Mo(function(e,o){let{theme:n,width:r=600,expandedHeight:s=400,allowAttachments:a=!1,welcomeMessage:u,placeholder:C="Ask me anything...",triggerEvent:m="triggerDemoRequest",resourceEndpoint:c,api:g}=e,T=c??(g?`${g}/resource`:void 0),v=Z(n),S=Q(v),w=Ce(v),d=he(e),i=xe({messages:d.messages,status:d.status,initialSuggestions:e.initialSuggestions,suggestions:e.suggestions,api:e.api,apiKey:e.apiKey,headers:e.headers}),p=ve(x=>{let H=x.content.map(E=>E.text??"").join("").trim();H&&d.handleSubmit({text:H,files:[]})},[d.handleSubmit]),y=ve(x=>{i.clear(),d.handleSubmit({text:x,files:[]})},[i.clear,d.handleSubmit]),L=ye(C,!d.text),[f,h]=dt(!1),[M,N]=dt(!1),b=mt(null),I=mt(void 0),R=ve(()=>{let x=b.current;if(!x)return;x.scrollIntoView({behavior:"smooth",block:"center"});let H=x.querySelector("textarea");H&&setTimeout(()=>H.focus(),300),h(!0),N(!0),clearTimeout(I.current),I.current=setTimeout(()=>N(!1),2e3)},[]);ko(o,()=>({sendMessage:x=>{d.handleSubmit({text:x,files:[]}),R()},focus:R}),[d.handleSubmit,R]),pt(()=>{if(!m)return;let x=H=>{let E=H.detail,D=typeof E?.message=="string"?E.message:void 0;D&&d.handleSubmit({text:D,files:[]}),R()};return window.addEventListener(m,x),()=>window.removeEventListener(m,x)},[m,d.handleSubmit,R]);let k=f;pt(()=>{if(!f)return;let x=H=>{b.current&&!b.current.contains(H.target)&&h(!1)};return document.addEventListener("mousedown",x),()=>document.removeEventListener("mousedown",x)},[f]);let A=ve(()=>{h(!0)},[]);return He("div",{ref:b,style:{...S,width:r},"data-waniwani-chat":"","data-waniwani-layout":"bar",...w?{"data-waniwani-dark":""}:{},className:"flex flex-col font-[family-name:var(--ww-font)] text-foreground",children:[$("div",{className:l("overflow-hidden bg-background/80 backdrop-blur-xl transition-all duration-300 ease-out",k?"opacity-100 translate-y-0":"opacity-0 translate-y-2 pointer-events-none 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:He(re,{className:"flex-1",style:{height:s},children:[$(ne,{children:$(fe,{messages:d.messages,status:d.status,welcomeMessage:u,resourceEndpoint:T,isDark:w,onFollowUp:p})}),$(se,{})]})}),$(ge,{suggestions:i.suggestions,isLoading:i.isLoading,onSelect:y}),$("div",{className:"shrink-0",children:$(ae,{onSubmit:d.handleSubmit,globalDrop:a,multiple:a,className:l("rounded-[var(--ww-radius)] shadow-sm transition-all duration-300 ease-out",M&&"ring-2 ring-blue-400/70 ring-offset-2 ring-offset-background"),children:He("div",{className:"flex items-center gap-1 px-3 py-2",children:[a&&$(ue,{}),$(ie,{onChange:d.handleTextChange,value:d.text,placeholder:L,onFocus:A,className:"min-h-0 py-1.5 px-2"}),$(le,{status:d.status})]})})})]})});import{forwardRef as Eo,useCallback as De,useEffect as Io,useImperativeHandle as Ro,useRef as ft,useState as Lo}from"react";import{jsx as U,jsxs as ee}from"react/jsx-runtime";var No=Eo(function(e,o){let{theme:n,title:r="Assistant",subtitle:s,showStatus:a=!0,width:u=500,height:C=600,allowAttachments:m=!1,welcomeMessage:c,placeholder:g="Ask me anything...",triggerEvent:T="triggerDemoRequest",resourceEndpoint:v,api:S}=e,w=v??(S?`${S}/resource`:void 0),d=Z(n),i=Q(d),p=Ce(d),y=he(e),L=ye(g,!y.text),[f,h]=Lo(!1),M=ft(null),N=ft(void 0),b=De(()=>{let A=M.current;if(!A)return;A.scrollIntoView({behavior:"smooth",block:"center"});let x=A.querySelector("textarea");x&&setTimeout(()=>x.focus(),300),h(!0),clearTimeout(N.current),N.current=setTimeout(()=>h(!1),2e3)},[]),I=xe({messages:y.messages,status:y.status,initialSuggestions:e.initialSuggestions,suggestions:e.suggestions,api:e.api,apiKey:e.apiKey,headers:e.headers}),R=De(A=>{let x=A.content.map(H=>H.text??"").join("").trim();x&&y.handleSubmit({text:x,files:[]})},[y.handleSubmit]),k=De(A=>{I.clear(),y.handleSubmit({text:A,files:[]})},[I.clear,y.handleSubmit]);return Ro(o,()=>({sendMessage:A=>{y.handleSubmit({text:A,files:[]}),b()},focus:b}),[y.handleSubmit,b]),Io(()=>{if(!T)return;let A=x=>{let H=x.detail,E=typeof H?.message=="string"?H.message:void 0;E&&y.handleSubmit({text:E,files:[]}),b()};return window.addEventListener(T,A),()=>window.removeEventListener(T,A)},[T,y.handleSubmit,b]),ee("div",{ref:M,style:{...i,width:u,height:C},"data-waniwani-chat":"","data-waniwani-layout":"card",...p?{"data-waniwani-dark":""}:{},className:l("flex flex-col font-[family-name:var(--ww-font)] text-foreground bg-background rounded-[var(--ww-radius)] border border-border shadow-md overflow-hidden transition-shadow duration-300",f&&"ring-2 ring-blue-400/70 ring-offset-2 ring-offset-background"),children:[ee("div",{className:"shrink-0 flex items-center gap-3 px-4 py-2 border-b border-border",style:{backgroundColor:d.headerBackgroundColor,color:d.headerTextColor},children:[a&&U("span",{className:"size-2.5 rounded-full bg-status"}),ee("div",{className:"flex-1 min-w-0",children:[U("div",{className:"text-xs font-semibold truncate",children:r}),s&&U("div",{className:"text-[11px] text-muted-foreground truncate",children:s})]})]}),ee(re,{className:"flex-1 min-h-0 bg-background",children:[U(ne,{children:U(fe,{messages:y.messages,status:y.status,welcomeMessage:c,resourceEndpoint:w,isDark:p,onFollowUp:R})}),U(se,{})]}),U(ge,{suggestions:I.suggestions,isLoading:I.isLoading,onSelect:k,className:"border-t border-border"}),U("div",{className:"shrink-0 border-t border-border bg-background",children:U(ae,{onSubmit:y.handleSubmit,globalDrop:m,multiple:m,className:l("rounded-none border-0"),children:ee("div",{className:"flex items-center gap-1 px-3 py-2",children:[m&&U(ue,{}),U(ie,{onChange:y.handleTextChange,value:y.text,placeholder:L,className:"min-h-0 py-1.5 px-2"}),U(le,{status:y.status})]})})})]})});export{Be as ChatBar,No as ChatCard,Be as ChatWidget,Po as DARK_THEME,ct as DEFAULT_THEME,Ne as McpAppFrame,Z as mergeTheme,Q as themeToCSSProperties};
|
|
2
|
+
import{forwardRef as So,useCallback as Ce,useEffect as ut,useImperativeHandle as Eo,useRef as ct,useState as pt}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 mt}from"clsx";import{twMerge as ft}from"tailwind-merge";function i(...e){return ft(mt(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("inline-flex cursor-pointer items-center justify-center rounded-md font-medium transition-colors disabled:pointer-events-none disabled:opacity-50",t==="default"&&"bg-primary text-primary-foreground hover:bg-primary/90",t==="outline"&&"border border-border bg-background hover:bg-accent hover:text-accent-foreground",t==="ghost"&&"hover:bg-accent hover:text-accent-foreground",o==="default"&&"h-9 px-4 py-2 text-sm",o==="sm"&&"h-8 px-3 text-xs",o==="icon"&&"size-9",o==="icon-sm"&&"size-7",e),...n});import{jsx as oe}from"react/jsx-runtime";var re=({className:e,...t})=>oe(Fe,{className:i("relative flex-1 overflow-y-hidden",e),initial:"smooth",resize:"smooth",role:"log",...t}),ne=({className:e,...t})=>oe(Fe.Content,{className:i("flex flex-col gap-8 p-4",e),...t}),se=({className:e,...t})=>{let{isAtBottom:o,scrollToBottom:r}=xt(),n=bt(()=>{r()},[r]);return!o&&oe(J,{className:i("absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full",e),onClick:n,size:"icon",variant:"outline",...t,children:oe(ht,{className:"size-4"})})};import{ArrowUpIcon as yt,LoaderIcon as Ct,PaperclipIcon as vt,SquareIcon as Tt,XIcon as wt}from"lucide-react";import{nanoid as Pt}from"nanoid";import{createContext as Mt,useCallback as q,useContext as kt,useEffect as Pe,useMemo as St,useRef as Me,useState as Ue}from"react";import{jsx as O,jsxs as _e}from"react/jsx-runtime";var Et=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=Mt(null),ze=()=>{let e=kt(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:l,...f})=>{let p=Me(null),u=Me(null),[d,C]=Ue([]),v=Me(d);Pe(()=>{v.current=d},[d]);let E=q(()=>{p.current?.click()},[]),P=q(x=>{let T=[...x];if(T.length===0)return;let M=h=>s?h.size<=s:!0,N=T.filter(M);C(h=>{let I=typeof n=="number"?Math.max(0,n-h.length):void 0,R=typeof I=="number"?N.slice(0,I):N;return[...h,...R.map(k=>({filename:k.name,id:Pt(),mediaType:k.type,type:"file",url:URL.createObjectURL(k)}))]})},[n,s]),g=q(x=>{C(T=>{let M=T.find(N=>N.id===x);return M?.url&&URL.revokeObjectURL(M.url),T.filter(N=>N.id!==x)})},[]),c=q(()=>{C(x=>{for(let T of x)T.url&&URL.revokeObjectURL(T.url);return[]})},[]);Pe(()=>()=>{for(let x of v.current)x.url&&URL.revokeObjectURL(x.url)},[]);let m=q(x=>{x.currentTarget.files&&P(x.currentTarget.files),x.currentTarget.value=""},[P]);Pe(()=>{if(!r)return;let x=M=>{M.dataTransfer?.types?.includes("Files")&&M.preventDefault()},T=M=>{M.dataTransfer?.types?.includes("Files")&&M.preventDefault(),M.dataTransfer?.files&&M.dataTransfer.files.length>0&&P(M.dataTransfer.files)};return document.addEventListener("dragover",x),document.addEventListener("drop",T),()=>{document.removeEventListener("dragover",x),document.removeEventListener("drop",T)}},[P,r]);let y=q(async x=>{x.preventDefault();let T=x.currentTarget,N=new FormData(T).get("message")||"";T.reset();let h=await Promise.all(d.map(async({id:I,...R})=>{if(R.url?.startsWith("blob:")){let k=await Et(R.url);return{...R,url:k??R.url}}return R}));try{let I=a({files:h,text:N},x);I instanceof Promise&&await I,c()}catch{}},[d,a,c]),D=St(()=>({add:P,clear:c,files:d,openFileDialog:E,remove:g}),[d,P,g,c,E]);return _e(Oe.Provider,{value:D,children:[O("input",{accept:t,"aria-label":"Upload files",className:"hidden",multiple:o,onChange:m,ref:p,title:"Upload files",type:"file"}),O("form",{className:i("flex w-full flex-col rounded-lg border border-border bg-background",e),onSubmit:y,ref:u,...f,children:l})]})};var ie=({onChange:e,onKeyDown:t,className:o,placeholder:r="What would you like to know?",...n})=>{let s=ze(),[a,l]=Ue(!1),f=q(u=>{if(t?.(u),!u.defaultPrevented){if(u.key==="Enter"){if(a||u.nativeEvent.isComposing||u.shiftKey)return;u.preventDefault();let{form:d}=u.currentTarget;if(d?.querySelector('button[type="submit"]')?.disabled)return;d?.requestSubmit()}if(u.key==="Backspace"&&u.currentTarget.value===""&&s.files.length>0){u.preventDefault();let d=s.files.at(-1);d&&s.remove(d.id)}}},[t,a,s]),p=q(u=>{let d=u.clipboardData?.items;if(!d)return;let C=[];for(let v of d)if(v.kind==="file"){let E=v.getAsFile();E&&C.push(E)}C.length>0&&(u.preventDefault(),s.add(C))},[s]);return O("textarea",{className:i("field-sizing-content max-h-48 min-h-16 w-full resize-none border-0 bg-transparent px-3 py-3 text-sm outline-none placeholder:text-muted-foreground",o),name:"message",onCompositionEnd:()=>l(!1),onCompositionStart:()=>l(!0),onKeyDown:f,onPaste:p,placeholder:r,onChange:e,...n})},le=({className:e,status:t,onStop:o,onClick:r,children:n,...s})=>{let a=t==="submitted"||t==="streaming",l=O(yt,{className:"size-4"});t==="submitted"?l=O(Ct,{className:"size-4 animate-spin"}):t==="streaming"&&(l=O(Tt,{className:"size-4"}));let f=q(p=>{if(a&&o){p.preventDefault(),o();return}r?.(p)},[a,o,r]);return O(J,{"aria-label":a?"Stop":"Submit",className:i("bg-foreground text-background hover:bg-foreground",e),onClick:f,size:"icon-sm",type:a&&o?"button":"submit",variant:"ghost",...s,children:n??l})},ue=({className:e,children:t,...o})=>{let r=ze();return r.files.length>0?_e(J,{className:i("group relative",e),onClick:()=>r.clear(),size:"icon-sm",type:"button",variant:"ghost","aria-label":"Remove all attachments",...o,children:[O("span",{className:"flex size-5 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground transition-opacity group-hover:opacity-0",children:r.files.length}),O(wt,{className:"absolute size-4 opacity-0 transition-opacity 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:"size-4"})})};import{FileIcon as It}from"lucide-react";import{jsx as Y,jsxs as Nt}from"react/jsx-runtime";var We=({files:e,className:t,...o})=>e.length===0?null:Y("div",{className:i("flex flex-wrap gap-1.5",t),...o,children:e.map((r,n)=>Y(Rt,{file:r},n))});function Rt({file:e}){return e.mediaType?.startsWith("image/")&&e.url?Y("img",{src:e.url,alt:e.filename??"attachment",className:"h-16 max-w-32 rounded object-cover"}):Nt("span",{className:"inline-flex items-center gap-1.5 rounded bg-background/20 px-2 py-1 text-xs",children:[Y(It,{className:"size-3 shrink-0"}),Y("span",{className:"max-w-24 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("flex items-center gap-1",e),...o,children:[0,1,2].map(r=>$e("div",{className:"rounded-full 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 Lt}from"@streamdown/cjk";import{code as At}from"@streamdown/code";import{memo as Ht}from"react";import{Streamdown as Bt}from"streamdown";import{jsx as Se}from"react/jsx-runtime";var ce=({className:e,from:t,...o})=>Se("div",{className:i("group flex w-full max-w-[95%] flex-col gap-2",t==="user"?"is-user ml-auto justify-end":"is-assistant",e),...o}),pe=({children:e,className:t,...o})=>Se("div",{className:i("flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-base","group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-user-bubble group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-primary-foreground","group-[.is-assistant]:text-foreground",t),...o,children:e}),Dt={cjk:Lt,code:At},de=Ht(({className:e,...t})=>Se(Bt,{className:i("size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",e),plugins:Dt,...t}),(e,t)=>e.children===t.children);de.displayName="MessageResponse";import{jsx as Ft}from"react/jsx-runtime";function je({className:e,text:t,...o}){return t?Ft("pre",{className:i("mb-2 overflow-x-auto whitespace-pre-wrap break-words text-xs font-mono text-muted-foreground",e),...o,children:t}):null}import{BracesIcon as Ut,CheckIcon as Ot,ChevronDownIcon as zt,ChevronRightIcon as _t,ClipboardCopyIcon as Wt,ServerIcon as xr}from"lucide-react";import{createContext as $t,useCallback as jt,useContext as Ve,useEffect as qt,useMemo as Vt,useRef as Jt,useState as Ee}from"react";import{Fragment as qe,jsx as w,jsxs as K}from"react/jsx-runtime";function Kt(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,l]of r){if(s<=8)break;let f=a.length>4?`${a.slice(0,4)}\u2026`:a,p;typeof l=="string"?p=l.length>2?`'${l.slice(0,1)}\u2026`:`'${l}'`:Array.isArray(l)?p=`Array(${l.length})`:typeof l=="object"&&l!==null?p="{\u2026}":p=String(l);let u=`${f}\u2009${p}`;n.push(u),s-=u.length+3}return`{${n.join(", ")}}`}function Gt({text:e,className:t}){let[o,r]=Ee(!1),n=Jt(null);qt(()=>()=>{n.current&&clearTimeout(n.current)},[]);let s=jt(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 w(J,{variant:"ghost",size:"sm",onClick:s,className:i("h-auto gap-1 px-1.5 py-0.5 text-xs text-muted-foreground hover:text-foreground",t),children:o?K(qe,{children:[w(Ot,{className:"size-3.5"}),w("span",{children:"Copied"})]}):K(qe,{children:[w(Wt,{className:"size-3.5"}),w("span",{children:"Copy"})]})})}function Je({data:e,label:t,className:o,...r}){let[n,s]=Ee(!1),a=Vt(()=>JSON.stringify(e,null,2),[e]),l=Kt(e);return K("div",{className:i("rounded-lg bg-tool-card",o),...r,children:[K("div",{className:"flex items-center justify-between px-3 pt-2.5 pb-1.5",children:[w("span",{className:"text-xs font-medium text-muted-foreground",children:t}),w(Gt,{text:a})]}),K("button",{type:"button",onClick:()=>s(f=>!f),className:"flex w-full items-start gap-2 px-3 pb-3 text-left",children:[w(_t,{className:i("mt-0.5 size-3.5 shrink-0 text-muted-foreground transition-transform duration-150",n&&"rotate-90")}),n?w("pre",{className:"overflow-x-auto text-xs font-mono text-foreground whitespace-pre-wrap break-all",children:w("code",{children:a})}):w("span",{className:"truncate text-xs font-mono text-foreground/80",children:l})]})]})}var Ie=$t({open:!1,toggle:()=>{}});function Ke({className:e,defaultOpen:t=!1,children:o,...r}){let[n,s]=Ee(t);return w(Ie.Provider,{value:{open:n,toggle:()=>s(a=>!a)},children:w("div",{className:i("mb-4 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("flex w-full items-center justify-between gap-3 py-1.5",e),"aria-expanded":n,...r,children:[K("div",{className:"flex min-w-0 items-center gap-2",children:[w(Ut,{className:"size-4 shrink-0 text-muted-foreground"}),w("span",{className:"truncate text-sm font-medium",children:t}),a&&w("span",{className:"size-2 shrink-0 rounded-full bg-primary animate-pulse"})]}),w(zt,{className:i("size-4 shrink-0 text-muted-foreground transition-transform duration-200",n&&"rotate-180")})]})}function Xe({className:e,children:t,...o}){let{open:r}=Ve(Ie);return w("div",{className:i("grid transition-[grid-template-rows,opacity] duration-200 ease-out",r?"grid-rows-[1fr] opacity-100":"grid-rows-[0fr] opacity-0"),children:w("div",{className:"min-h-0 overflow-hidden",children:w("div",{className:i("mt-2 space-y-3 rounded-lg border border-border bg-background p-3",e),...o,children:t})})})}function Ye({className:e,input:t,...o}){return w(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("space-y-2",e),...r,children:[w("h4",{className:"text-xs font-medium uppercase tracking-wide text-muted-foreground",children:"Error"}),w("div",{className:"rounded-lg bg-destructive/10 p-3 text-xs text-destructive",children:o})]}):w(Je,{data:t,label:"Response",className:e,...r}):null}import{useCallback as Xt,useEffect as ot,useMemo as Yt,useRef as W,useState as rt}from"react";import{jsx as no}from"react/jsx-runtime";var Zt="/api/mcp/resource",Qt=500,eo=0,to="2026-01-26",oo=300,ro=3e3,Re=3;function Ne({resourceUri:e,toolInput:t,toolResult:o,resourceEndpoint:r=Zt,isDark:n=!1,className:s,autoHeight:a=!0,onOpenLink:l,onFollowUp:f}){let p=W(null),u=W(t),d=W(o),C=W({width:0,height:0}),v=W(null),E=W(!1),P=W(0),[g,c]=rt(eo),[m,y]=rt(void 0),D=W(l),x=W(f);u.current=t,d.current=o,D.current=l,x.current=f;let T=Xt(h=>a?Math.max(h,0):Math.min(Math.max(h,50),Qt),[a]),M=Yt(()=>`${r}?uri=${encodeURIComponent(e)}`,[r,e]),N=W(n);return N.current=n,ot(()=>{if(!E.current)return;let h=p.current;h?.contentWindow&&h.contentWindow.postMessage({jsonrpc:"2.0",method:"ui/notifications/host-context-changed",params:{theme:n?"dark":"light"}},"*")},[n]),ot(()=>{let h=p.current;if(!h)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(h.src);S.searchParams.set("_retry",String(P.current)),h.src=S.toString()},ro),b=S=>{k("\u2192 send",S.method??`response:${S.id}`,S),h.contentWindow?.postMessage(S,"*")},A=S=>{if(I||S.source!==h.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"),b({jsonrpc:"2.0",id:F,result:{protocolVersion:B.params?.protocolVersion??to,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=u.current,_=d.current;b({jsonrpc:"2.0",method:"ui/notifications/tool-input",params:{arguments:z}});let V=_.content??[{type:"text",text:JSON.stringify(_)}];b({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=C.current,ve=_!==void 0&&_!==G.height,Te=V!==void 0&&V!==G.width;if(k("size-changed",{newHeight:_,newWidth:V,lastHeight:G.height,lastWidth:G.width,heightChanged:ve,widthChanged:Te}),!ve&&!Te)return;if(ve&&_!==void 0){G.height=_;let we=T(_),De=h.getBoundingClientRect().height;if(v.current&&(v.current.cancel(),v.current=null),c(we),h.animate&&Math.abs(De-we)>2){let te=h.animate([{height:`${De}px`},{height:`${we}px`}],{duration:oo,easing:"ease-out",fill:"forwards"});v.current=te,te.onfinish=()=>{v.current===te&&(te.cancel(),v.current=null)}}}Te&&a&&V!==void 0&&(G.width=V,y(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")),b({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/message"&&F!=null){x.current&&B.params&&x.current(B.params),b({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/request-display-mode"&&F!=null){b({jsonrpc:"2.0",id:F,result:{}});return}if(j==="ui/resource-teardown"&&F!=null){b({jsonrpc:"2.0",id:F,result:{}});return}j==="ping"&&F!=null&&b({jsonrpc:"2.0",id:F,result:{}})};return window.addEventListener("message",A),()=>{k("effect cleanup (disposed)"),I=!0,clearTimeout(L),window.removeEventListener("message",A)}},[a,T]),no("iframe",{ref:p,src:M,sandbox:"allow-scripts allow-forms allow-same-origin",className:i("rounded-md border border-border",s),style:{height:g,minWidth:m?`min(${m}px, 100%)`:void 0,width:"100%",border:"none",colorScheme:"auto"},title:"MCP App"})}import{Component as so}from"react";import{jsx as nt,jsxs as ao}from"react/jsx-runtime";var me=class extends so{state={hasError:!1};static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(t){console.warn("[WaniWani] Widget failed to render:",t.message)}render(){return this.state.hasError?ao("div",{className:"flex items-center justify-between rounded-md border border-border bg-muted/50 px-4 py-3 text-sm text-muted-foreground",children:[nt("span",{children:"Widget failed to load"}),nt("button",{type:"button",onClick:()=>this.setState({hasError:!1}),className:"text-xs font-medium text-primary hover:underline",children:"Retry"})]}):this.props.children}};import{Fragment as lo,jsx as H,jsxs as X}from"react/jsx-runtime";function io(e){return e.replace(/[-_]/g," ").replace(/^\w/,t=>t.toUpperCase())}function fe({messages:e,status:t,welcomeMessage:o,resourceEndpoint:r,isDark:n,onFollowUp:s}){let a=t==="submitted"||t==="streaming",l=e[e.length-1],f=e.length>0,p=a&&(!f||l.role==="user");return X(lo,{children:[o&&H(ce,{from:"assistant",children:H(pe,{children:H(de,{children:o})})}),e.map(u=>{let d=u.parts.filter(c=>c.type==="text"),C=u.parts.filter(c=>c.type==="reasoning"),v=u.parts.filter(c=>c.type==="file"),E=u.parts.filter(c=>"toolCallId"in c),P=u===l&&u.role==="assistant",g=d.length>0;return X(ce,{from:u.role,children:[C.map((c,m)=>H(je,{text:c.text},`reasoning-${u.id}-${m}`)),E.map(c=>{let m="output"in c?c.output:void 0,y=m!==void 0?Qe(m):void 0,D=m!==void 0?et(m):!1;return X("div",{children:[X(Ke,{defaultOpen:c.state==="output-available",children:[H(Ge,{title:c.title??io(c.toolName),state:c.state}),X(Xe,{children:[H(Ye,{input:c.input}),m!==void 0&&H(tt,{output:m,errorText:"errorText"in c?c.errorText:void 0})]})]}),y&&m!==void 0&&H(me,{children:H(Ne,{resourceUri:y,toolInput:c.input??{},toolResult:{content:m.content,structuredContent:m.structuredContent},resourceEndpoint:r,isDark:n,autoHeight:D,onFollowUp:s})})]},c.toolCallId)}),X(pe,{children:[v.length>0&&H(We,{files:v}),g?d.map((c,m)=>H(de,{children:c.type==="text"?c.text:""},`${u.id}-${m}`)):P&&a&&H(ke,{})]})]},u.id)}),p&&H(ce,{from:"assistant",children:H(pe,{children:H(ke,{})})})]})}import{jsx as Le}from"react/jsx-runtime";function ge({suggestions:e,isLoading:t,onSelect:o,className:r,...n}){return e.length===0&&!t?null:Le("div",{className:i("flex flex-wrap gap-2 px-3 py-2",r),...n,children:t?[0,1,2].map(s=>Le("div",{className:"h-7 rounded-full bg-accent animate-pulse",style:{width:`${60+s*20}px`}},s)):e.map((s,a)=>Le("button",{type:"button",onClick:()=>o(s),className:i("rounded-full border border-border bg-background px-3 py-1 text-xs","text-foreground hover:bg-accent hover:border-primary/30","transition-all duration-200 ease-out cursor-pointer","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 he(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:l,sendMessage:f,status:p}=uo({transport:a.current,onFinish(){s?.()},onError(m){console.warn("[WaniWani] Chat error:",m.message)}}),[u,d]=mo(""),C=st(m=>{let y=!!m.text?.trim(),D=!!m.files?.length;(y||D)&&(f({text:m.text||"",files:m.files}),n?.(m.text||""),d(""))},[f,n]),v=st(m=>{d(m.target.value)},[]),E=p==="submitted"||p==="streaming",P=l[l.length-1],g=l.length>0,c=E&&(!g||P.role==="user");return{messages:l,status:p,text:u,setText:d,handleSubmit:C,handleTextChange:v,isLoading:E,showLoaderBubble:c,lastMessage:P,hasMessages:g,sendMessage:f}}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 be(e){let{messages:t,status:o,config:r}=e,[n,s]=ho((xo(r)&&r.initial?r.initial:[])??[]),a=go(o),l=!!r,f=fo(()=>{s([])},[]),p=t[t.length-1];return at(()=>{p?.role==="user"&&f()},[p,f]),at(()=>{let u=a.current;if(a.current=o,u==="streaming"&&o==="ready"&&l){let d=[...t].reverse().find(v=>v.role==="assistant");if(!d)return;console.log("[WaniWani] Assistant parts:",d.parts);let C=bo(d);console.log("[WaniWani] Extracted suggestions:",C),C&&s(C)}},[o,l,t]),{suggestions:n,isLoading:!1,clear:f}}import{useEffect as yo,useRef as Co,useState as vo}from"react";var To=50,wo=30,Po=2e3,it=500;function xe(e,t=!0){let[o,r]=vo(""),n=Co(void 0);return yo(()=>{if(!t){r("");return}let s=0,a=!1,l=!1,f=()=>{l||(a?(s--,r(e.slice(0,s)),s<=0?(a=!1,n.current=setTimeout(f,it)):n.current=setTimeout(f,wo)):(s++,r(e.slice(0,s)),s>=e.length?(a=!0,n.current=setTimeout(f,Po)):n.current=setTimeout(f,To)))};return n.current=setTimeout(f,it),()=>{l=!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"},Mo={backgroundColor:"#212121",headerBackgroundColor:"#1e1e1e",headerTextColor:"#ececec",textColor:"#ececec",primaryForeground:"#ffffff",mutedColor:"#8e8ea0",borderColor:"#303030",assistantBubbleColor:"#2f2f2f",userBubbleColor:"#303030",inputBackgroundColor:"#2f2f2f",primaryColor:"#6366f1",statusColor:"#22c55e",toolCardColor:"#262626"},ko={primaryColor:["--ww-primary","--color-primary"],primaryForeground:["--ww-primary-fg","--color-primary-foreground"],backgroundColor:["--ww-bg","--color-background"],textColor:["--ww-text","--color-foreground","--color-accent-foreground"],mutedColor:["--ww-muted","--color-muted-foreground"],borderColor:["--ww-border","--color-border"],assistantBubbleColor:["--ww-assistant-bubble","--color-accent"],userBubbleColor:["--ww-user-bubble"],inputBackgroundColor:["--ww-input-bg","--color-input"],borderRadius:["--ww-radius","--radius"],messageBorderRadius:["--ww-msg-radius"],fontFamily:["--ww-font"],headerBackgroundColor:["--ww-header-bg"],headerTextColor:["--ww-header-text"],statusColor:["--ww-status"],toolCardColor:["--ww-tool-card","--color-tool-card"]};function Z(e){return{...lt,...e}}function ye(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(ko)){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=So(function(t,o){let{theme:r,width:n=600,expandedHeight:s=400,allowAttachments:a=!1,welcomeMessage:l,placeholder:f="Ask me anything...",triggerEvent:p="triggerDemoRequest",resourceEndpoint:u,api:d}=t,C=u??(d?`${d}/resource`:void 0),v=Z(r),E=Q(v),P=ye(v),g=he(t),c=be({messages:g.messages,status:g.status,config:t.suggestions}),m=Ce(b=>{let A=b.content.map(S=>S.text??"").join("").trim();A&&g.handleSubmit({text:A,files:[]})},[g.handleSubmit]),y=Ce(b=>{c.clear(),g.handleSubmit({text:b,files:[]})},[c.clear,g.handleSubmit]),D=xe(f,!g.text),[x,T]=pt(!1),[M,N]=pt(!1),h=ct(null),I=ct(void 0),R=Ce(()=>{let b=h.current;if(!b)return;b.scrollIntoView({behavior:"smooth",block:"center"});let A=b.querySelector("textarea");A&&setTimeout(()=>A.focus(),300),T(!0),N(!0),clearTimeout(I.current),I.current=setTimeout(()=>N(!1),2e3)},[]);Eo(o,()=>({sendMessage:b=>{g.handleSubmit({text:b,files:[]}),R()},focus:R}),[g.handleSubmit,R]),ut(()=>{if(!p)return;let b=A=>{let S=A.detail,B=typeof S?.message=="string"?S.message:void 0;B&&g.handleSubmit({text:B,files:[]}),R()};return window.addEventListener(p,b),()=>window.removeEventListener(p,b)},[p,g.handleSubmit,R]);let k=x;ut(()=>{if(!x)return;let b=A=>{h.current&&!h.current.contains(A.target)&&T(!1)};return document.addEventListener("mousedown",b),()=>document.removeEventListener("mousedown",b)},[x]);let L=Ce(()=>{T(!0)},[]);return Ae("div",{ref:h,style:{...E,width:n},"data-waniwani-chat":"","data-waniwani-layout":"bar",...P?{"data-waniwani-dark":""}:{},className:"flex flex-col font-[family-name:var(--ww-font)] text-foreground",children:[$("div",{className:i("overflow-hidden bg-background/80 backdrop-blur-xl transition-all duration-300 ease-out",k?"opacity-100 translate-y-0":"opacity-0 translate-y-2 pointer-events-none 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:"flex-1",style:{height:s},children:[$(ne,{children:$(fe,{messages:g.messages,status:g.status,welcomeMessage:l,resourceEndpoint:C,isDark:P,onFollowUp:m})}),$(se,{})]})}),$(ge,{suggestions:c.suggestions,isLoading:c.isLoading,onSelect:y}),$("div",{className:"shrink-0",children:$(ae,{onSubmit:g.handleSubmit,globalDrop:a,multiple:a,className:i("rounded-[var(--ww-radius)] shadow-sm transition-all duration-300 ease-out",M&&"ring-2 ring-blue-400/70 ring-offset-2 ring-offset-background"),children:Ae("div",{className:"flex items-center gap-1 px-3 py-2",children:[a&&$(ue,{}),$(ie,{onChange:g.handleTextChange,value:g.text,placeholder:D,onFocus:L,className:"min-h-0 py-1.5 px-2"}),$(le,{status:g.status})]})})})]})});import{forwardRef as Io,useCallback as Be,useEffect as Ro,useImperativeHandle as No,useRef as dt,useState as Lo}from"react";import{jsx as U,jsxs as ee}from"react/jsx-runtime";var Ao=Io(function(t,o){let{theme:r,title:n="Assistant",subtitle:s,showStatus:a=!0,width:l=500,height:f=600,allowAttachments:p=!1,welcomeMessage:u,placeholder:d="Ask me anything...",triggerEvent:C="triggerDemoRequest",resourceEndpoint:v,api:E}=t,P=v??(E?`${E}/resource`:void 0),g=Z(r),c=Q(g),m=ye(g),y=he(t),D=xe(d,!y.text),[x,T]=Lo(!1),M=dt(null),N=dt(void 0),h=Be(()=>{let L=M.current;if(!L)return;L.scrollIntoView({behavior:"smooth",block:"center"});let b=L.querySelector("textarea");b&&setTimeout(()=>b.focus(),300),T(!0),clearTimeout(N.current),N.current=setTimeout(()=>T(!1),2e3)},[]),I=be({messages:y.messages,status:y.status,config:t.suggestions}),R=Be(L=>{let b=L.content.map(A=>A.text??"").join("").trim();b&&y.handleSubmit({text:b,files:[]})},[y.handleSubmit]),k=Be(L=>{I.clear(),y.handleSubmit({text:L,files:[]})},[I.clear,y.handleSubmit]);return No(o,()=>({sendMessage:L=>{y.handleSubmit({text:L,files:[]}),h()},focus:h}),[y.handleSubmit,h]),Ro(()=>{if(!C)return;let L=b=>{let A=b.detail,S=typeof A?.message=="string"?A.message:void 0;S&&y.handleSubmit({text:S,files:[]}),h()};return window.addEventListener(C,L),()=>window.removeEventListener(C,L)},[C,y.handleSubmit,h]),ee("div",{ref:M,style:{...c,width:l,height:f},"data-waniwani-chat":"","data-waniwani-layout":"card",...m?{"data-waniwani-dark":""}:{},className:i("flex flex-col font-[family-name:var(--ww-font)] text-foreground bg-background rounded-[var(--ww-radius)] border border-border shadow-md overflow-hidden transition-shadow duration-300",x&&"ring-2 ring-blue-400/70 ring-offset-2 ring-offset-background"),children:[ee("div",{className:"shrink-0 flex items-center gap-3 px-4 py-2 border-b border-border",style:{backgroundColor:g.headerBackgroundColor,color:g.headerTextColor},children:[a&&U("span",{className:"size-2.5 rounded-full bg-status"}),ee("div",{className:"flex-1 min-w-0",children:[U("div",{className:"text-xs font-semibold truncate",children:n}),s&&U("div",{className:"text-[11px] text-muted-foreground truncate",children:s})]})]}),ee(re,{className:"flex-1 min-h-0 bg-background",children:[U(ne,{children:U(fe,{messages:y.messages,status:y.status,welcomeMessage:u,resourceEndpoint:P,isDark:m,onFollowUp:R})}),U(se,{})]}),U(ge,{suggestions:I.suggestions,isLoading:I.isLoading,onSelect:k,className:"border-t border-border"}),U("div",{className:"shrink-0 border-t border-border bg-background",children:U(ae,{onSubmit:y.handleSubmit,globalDrop:p,multiple:p,className:i("rounded-none border-0"),children:ee("div",{className:"flex items-center gap-1 px-3 py-2",children:[p&&U(ue,{}),U(ie,{onChange:y.handleTextChange,value:y.text,placeholder:D,className:"min-h-0 py-1.5 px-2"}),U(le,{status:y.status})]})})})]})});export{He as ChatBar,Ao as ChatCard,He as ChatWidget,Mo 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\tinitialSuggestions: props.initialSuggestions,\n\t\t\tsuggestions: props.suggestions,\n\t\t\tapi: props.api,\n\t\t\tapiKey: props.apiKey,\n\t\t\theaders: props.headers,\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=\"flex flex-col font-[family-name:var(--ww-font)] 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\"overflow-hidden bg-background/80 backdrop-blur-xl transition-all duration-300 ease-out\",\n\t\t\t\t\t\tisExpanded\n\t\t\t\t\t\t\t? \"opacity-100 translate-y-0\"\n\t\t\t\t\t\t\t: \"opacity-0 translate-y-2 pointer-events-none 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 className=\"flex-1\" style={{ height: expandedHeight }}>\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=\"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\"rounded-[var(--ww-radius)] shadow-sm transition-all duration-300 ease-out\",\n\t\t\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\t\t\"ring-2 ring-blue-400/70 ring-offset-2 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=\"flex items-center gap-1 px-3 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=\"min-h-0 py-1.5 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(\"relative flex-1 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(\"flex flex-col gap-8 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\"absolute bottom-4 left-[50%] translate-x-[-50%] 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=\"size-4\" />\n\t\t\t</Button>\n\t\t)\n\t);\n};\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\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\"inline-flex cursor-pointer items-center justify-center rounded-md font-medium transition-colors disabled:pointer-events-none disabled:opacity-50\",\n\t\t\tvariant === \"default\" &&\n\t\t\t\t\"bg-primary text-primary-foreground hover:bg-primary/90\",\n\t\t\tvariant === \"outline\" &&\n\t\t\t\t\"border border-border bg-background hover:bg-accent hover:text-accent-foreground\",\n\t\t\tvariant === \"ghost\" && \"hover:bg-accent hover:text-accent-foreground\",\n\t\t\tsize === \"default\" && \"h-9 px-4 py-2 text-sm\",\n\t\t\tsize === \"sm\" && \"h-8 px-3 text-xs\",\n\t\t\tsize === \"icon\" && \"size-9\",\n\t\t\tsize === \"icon-sm\" && \"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=\"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\"flex w-full flex-col rounded-lg border border-border 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 className={cn(\"flex flex-wrap gap-1 px-3 pt-3\", className)} {...props} />\n);\n\nexport type PromptInputBodyProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputBody = ({\n\tclassName,\n\t...props\n}: PromptInputBodyProps) => (\n\t<div className={cn(\"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\"flex items-center justify-between gap-1 px-3 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(\"flex min-w-0 items-center 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\"field-sizing-content max-h-48 min-h-16 w-full resize-none border-0 bg-transparent px-3 py-3 text-sm outline-none 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=\"size-4\" />;\n\tif (status === \"submitted\") {\n\t\tIcon = <LoaderIcon className=\"size-4 animate-spin\" />;\n\t} else if (status === \"streaming\") {\n\t\tIcon = <SquareIcon className=\"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\"bg-foreground text-background 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(\"group 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=\"flex size-5 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground transition-opacity 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=\"absolute size-4 opacity-0 transition-opacity 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=\"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 className={cn(\"flex flex-wrap gap-1.5\", className)} {...props}>\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=\"h-16 max-w-32 rounded object-cover\"\n\t\t\t/>\n\t\t);\n\t}\n\n\treturn (\n\t\t<span className=\"inline-flex items-center gap-1.5 rounded bg-background/20 px-2 py-1 text-xs\">\n\t\t\t<FileIcon className=\"size-3 shrink-0\" />\n\t\t\t<span className=\"max-w-24 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(\"flex items-center 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=\"rounded-full 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\"group flex w-full max-w-[95%] flex-col gap-2\",\n\t\t\tfrom === \"user\" ? \"is-user ml-auto 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\"flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-base\",\n\t\t\t\"group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-user-bubble group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-primary-foreground\",\n\t\t\t\"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\"size-full [&>*:first-child]:mt-0 [&>*: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\"mb-2 overflow-x-auto whitespace-pre-wrap break-words text-xs font-mono 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\"h-auto gap-1 px-1.5 py-0.5 text-xs text-muted-foreground 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=\"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=\"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(\"rounded-lg bg-tool-card\", className)} {...props}>\n\t\t\t<div className=\"flex items-center justify-between px-3 pt-2.5 pb-1.5\">\n\t\t\t\t<span className=\"text-xs font-medium 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=\"flex w-full items-start gap-2 px-3 pb-3 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\"mt-0.5 size-3.5 shrink-0 text-muted-foreground transition-transform duration-150\",\n\t\t\t\t\t\texpanded && \"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=\"overflow-x-auto text-xs font-mono text-foreground whitespace-pre-wrap 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=\"truncate text-xs font-mono 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(\"mb-4 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\"flex w-full items-center justify-between gap-3 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=\"flex min-w-0 items-center gap-2\">\n\t\t\t\t<BracesIcon className=\"size-4 shrink-0 text-muted-foreground\" />\n\t\t\t\t<span className=\"truncate text-sm font-medium\">{title}</span>\n\t\t\t\t{isRunning && (\n\t\t\t\t\t<span className=\"size-2 shrink-0 rounded-full bg-primary 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\"size-4 shrink-0 text-muted-foreground transition-transform duration-200\",\n\t\t\t\t\topen && \"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\"flex items-center gap-3 rounded-lg border border-border px-3 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=\"size-8 shrink-0 rounded-full object-cover\"\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex size-8 shrink-0 items-center justify-center rounded-full border border-border bg-muted\">\n\t\t\t\t\t<ServerIcon className=\"size-4 text-muted-foreground\" />\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"flex min-w-0 flex-col\">\n\t\t\t\t{serverName && (\n\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">{serverName}</span>\n\t\t\t\t)}\n\t\t\t\t<span className=\"truncate text-sm font-semibold\">{toolName}</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\"grid transition-[grid-template-rows,opacity] duration-200 ease-out\",\n\t\t\t\topen ? \"grid-rows-[1fr] opacity-100\" : \"grid-rows-[0fr] opacity-0\",\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"min-h-0 overflow-hidden\">\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"mt-2 space-y-3 rounded-lg border border-border bg-background 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(\"space-y-2\", className)} {...props}>\n\t\t\t\t<h4 className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n\t\t\t\t\tError\n\t\t\t\t</h4>\n\t\t\t\t<div className=\"rounded-lg bg-destructive/10 p-3 text-xs 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(\"rounded-md border 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=\"flex items-center justify-between rounded-md border border-border bg-muted/50 px-4 py-3 text-sm 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=\"text-xs font-medium text-primary 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 className={cn(\"flex flex-wrap gap-2 px-3 py-2\", className)} {...props}>\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=\"h-7 rounded-full bg-accent 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\"rounded-full border border-border bg-background px-3 py-1 text-xs\",\n\t\t\t\t\t\t\t\t\"text-foreground hover:bg-accent hover:border-primary/30\",\n\t\t\t\t\t\t\t\t\"transition-all duration-200 ease-out cursor-pointer\",\n\t\t\t\t\t\t\t\t\"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\tinitialSuggestions?: string[];\n\tsuggestions?: boolean | SuggestionsConfig;\n\t/** Chat API endpoint — used to derive the suggestions URL */\n\tapi?: string;\n\tapiKey?: string;\n\theaders?: Record<string, string>;\n}\n\n/**\n * Derive the suggestions API URL from the chat API endpoint.\n * \"/api/chat\" → \"/api/mcp/suggestions\"\n * \"https://app.waniwani.ai/api/chat\" → \"https://app.waniwani.ai/api/mcp/suggestions\"\n */\nfunction deriveSuggestionsUrl(api: string): string {\n\ttry {\n\t\tconst url = new URL(api);\n\t\turl.pathname = \"/api/mcp/suggestions\";\n\t\treturn url.toString();\n\t} catch {\n\t\t// Relative URL — use same origin\n\t\treturn \"/api/mcp/suggestions\";\n\t}\n}\n\nexport function useSuggestions(options: UseSuggestionsOptions) {\n\tconst {\n\t\tmessages,\n\t\tstatus,\n\t\tinitialSuggestions,\n\t\tsuggestions: suggestionsConfig,\n\t\tapi = \"https://app.waniwani.ai/api/chat\",\n\t\tapiKey,\n\t\theaders: userHeaders,\n\t} = options;\n\n\tconst [suggestions, setSuggestions] = useState<string[]>([]);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst prevStatusRef = useRef<ChatStatus>(status);\n\tconst abortRef = useRef<AbortController | null>(null);\n\n\tconst isEnabled = Boolean(suggestionsConfig);\n\tconst count =\n\t\ttypeof suggestionsConfig === \"object\" ? (suggestionsConfig.count ?? 3) : 3;\n\n\tconst hasUserMessages = messages.some((m) => m.role === \"user\");\n\tconst suggestionsUrl = deriveSuggestionsUrl(api);\n\n\tconst clear = useCallback(() => {\n\t\tsetSuggestions([]);\n\t\tabortRef.current?.abort();\n\t\tabortRef.current = null;\n\t}, []);\n\n\t// Show initial suggestions when no user messages exist\n\tuseEffect(() => {\n\t\tif (!hasUserMessages && initialSuggestions?.length) {\n\t\t\tsetSuggestions(initialSuggestions);\n\t\t}\n\t}, [hasUserMessages, initialSuggestions]);\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// Fetch AI suggestions on streaming → idle 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 controller = new AbortController();\n\t\t\tabortRef.current?.abort();\n\t\t\tabortRef.current = controller;\n\n\t\t\tsetIsLoading(true);\n\n\t\t\tfetch(suggestionsUrl, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),\n\t\t\t\t\t...userHeaders,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({ messages, count }),\n\t\t\t\tsignal: controller.signal,\n\t\t\t})\n\t\t\t\t.then((res) => {\n\t\t\t\t\tif (!res.ok) throw new Error(`Suggestions API error: ${res.status}`);\n\t\t\t\t\treturn res.json();\n\t\t\t\t})\n\t\t\t\t.then((data) => {\n\t\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\t\tsetSuggestions(data.suggestions ?? []);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tif (err.name !== \"AbortError\") {\n\t\t\t\t\t\tconsole.warn(\"[WaniWani] Failed to fetch suggestions:\", err);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.finally(() => {\n\t\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\t\tsetIsLoading(false);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}\n\t}, [status, isEnabled, suggestionsUrl, apiKey, messages, count, userHeaders]);\n\n\t// Cleanup on unmount\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tabortRef.current?.abort();\n\t\t};\n\t}, []);\n\n\treturn { suggestions, isLoading, 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: \"#303030\",\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\", \"--color-primary\"],\n\tprimaryForeground: [\"--ww-primary-fg\", \"--color-primary-foreground\"],\n\tbackgroundColor: [\"--ww-bg\", \"--color-background\"],\n\ttextColor: [\"--ww-text\", \"--color-foreground\", \"--color-accent-foreground\"],\n\tmutedColor: [\"--ww-muted\", \"--color-muted-foreground\"],\n\tborderColor: [\"--ww-border\", \"--color-border\"],\n\tassistantBubbleColor: [\"--ww-assistant-bubble\", \"--color-accent\"],\n\tuserBubbleColor: [\"--ww-user-bubble\"],\n\tinputBackgroundColor: [\"--ww-input-bg\", \"--color-input\"],\n\tborderRadius: [\"--ww-radius\", \"--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\", \"--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\tinitialSuggestions: props.initialSuggestions,\n\t\t\tsuggestions: props.suggestions,\n\t\t\tapi: props.api,\n\t\t\tapiKey: props.apiKey,\n\t\t\theaders: props.headers,\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\"flex flex-col font-[family-name:var(--ww-font)] text-foreground bg-background rounded-[var(--ww-radius)] border border-border shadow-md overflow-hidden transition-shadow duration-300\",\n\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\"ring-2 ring-blue-400/70 ring-offset-2 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=\"shrink-0 flex items-center gap-3 px-4 py-2 border-b 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 && <span className=\"size-2.5 rounded-full bg-status\" />}\n\t\t\t\t\t<div className=\"flex-1 min-w-0\">\n\t\t\t\t\t\t<div className=\"text-xs font-semibold truncate\">{title}</div>\n\t\t\t\t\t\t{subtitle && (\n\t\t\t\t\t\t\t<div className=\"text-[11px] text-muted-foreground 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=\"flex-1 min-h-0 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=\"border-t border-border\"\n\t\t\t\t/>\n\n\t\t\t\t{/* Input */}\n\t\t\t\t<div className=\"shrink-0 border-t border-border 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(\"rounded-none border-0\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"flex items-center gap-1 px-3 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=\"min-h-0 py-1.5 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","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","deriveSuggestionsUrl","api","url","useSuggestions","options","messages","status","initialSuggestions","suggestionsConfig","apiKey","userHeaders","suggestions","setSuggestions","isLoading","setIsLoading","prevStatusRef","abortRef","isEnabled","count","hasUserMessages","m","suggestionsUrl","clear","lastMessage","prevStatus","controller","res","data","err","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=\"flex flex-col font-[family-name:var(--ww-font)] 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\"overflow-hidden bg-background/80 backdrop-blur-xl transition-all duration-300 ease-out\",\n\t\t\t\t\t\tisExpanded\n\t\t\t\t\t\t\t? \"opacity-100 translate-y-0\"\n\t\t\t\t\t\t\t: \"opacity-0 translate-y-2 pointer-events-none 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 className=\"flex-1\" style={{ height: expandedHeight }}>\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=\"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\"rounded-[var(--ww-radius)] shadow-sm transition-all duration-300 ease-out\",\n\t\t\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\t\t\"ring-2 ring-blue-400/70 ring-offset-2 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=\"flex items-center gap-1 px-3 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=\"min-h-0 py-1.5 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(\"relative flex-1 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(\"flex flex-col gap-8 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\"absolute bottom-4 left-[50%] translate-x-[-50%] 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=\"size-4\" />\n\t\t\t</Button>\n\t\t)\n\t);\n};\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\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\"inline-flex cursor-pointer items-center justify-center rounded-md font-medium transition-colors disabled:pointer-events-none disabled:opacity-50\",\n\t\t\tvariant === \"default\" &&\n\t\t\t\t\"bg-primary text-primary-foreground hover:bg-primary/90\",\n\t\t\tvariant === \"outline\" &&\n\t\t\t\t\"border border-border bg-background hover:bg-accent hover:text-accent-foreground\",\n\t\t\tvariant === \"ghost\" && \"hover:bg-accent hover:text-accent-foreground\",\n\t\t\tsize === \"default\" && \"h-9 px-4 py-2 text-sm\",\n\t\t\tsize === \"sm\" && \"h-8 px-3 text-xs\",\n\t\t\tsize === \"icon\" && \"size-9\",\n\t\t\tsize === \"icon-sm\" && \"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=\"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\"flex w-full flex-col rounded-lg border border-border 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 className={cn(\"flex flex-wrap gap-1 px-3 pt-3\", className)} {...props} />\n);\n\nexport type PromptInputBodyProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputBody = ({\n\tclassName,\n\t...props\n}: PromptInputBodyProps) => (\n\t<div className={cn(\"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\"flex items-center justify-between gap-1 px-3 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(\"flex min-w-0 items-center 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\"field-sizing-content max-h-48 min-h-16 w-full resize-none border-0 bg-transparent px-3 py-3 text-sm outline-none 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=\"size-4\" />;\n\tif (status === \"submitted\") {\n\t\tIcon = <LoaderIcon className=\"size-4 animate-spin\" />;\n\t} else if (status === \"streaming\") {\n\t\tIcon = <SquareIcon className=\"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\"bg-foreground text-background 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(\"group 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=\"flex size-5 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground transition-opacity 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=\"absolute size-4 opacity-0 transition-opacity 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=\"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 className={cn(\"flex flex-wrap gap-1.5\", className)} {...props}>\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=\"h-16 max-w-32 rounded object-cover\"\n\t\t\t/>\n\t\t);\n\t}\n\n\treturn (\n\t\t<span className=\"inline-flex items-center gap-1.5 rounded bg-background/20 px-2 py-1 text-xs\">\n\t\t\t<FileIcon className=\"size-3 shrink-0\" />\n\t\t\t<span className=\"max-w-24 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(\"flex items-center 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=\"rounded-full 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\"group flex w-full max-w-[95%] flex-col gap-2\",\n\t\t\tfrom === \"user\" ? \"is-user ml-auto 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\"flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-base\",\n\t\t\t\"group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-user-bubble group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-primary-foreground\",\n\t\t\t\"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\"size-full [&>*:first-child]:mt-0 [&>*: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\"mb-2 overflow-x-auto whitespace-pre-wrap break-words text-xs font-mono 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\"h-auto gap-1 px-1.5 py-0.5 text-xs text-muted-foreground 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=\"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=\"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(\"rounded-lg bg-tool-card\", className)} {...props}>\n\t\t\t<div className=\"flex items-center justify-between px-3 pt-2.5 pb-1.5\">\n\t\t\t\t<span className=\"text-xs font-medium 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=\"flex w-full items-start gap-2 px-3 pb-3 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\"mt-0.5 size-3.5 shrink-0 text-muted-foreground transition-transform duration-150\",\n\t\t\t\t\t\texpanded && \"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=\"overflow-x-auto text-xs font-mono text-foreground whitespace-pre-wrap 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=\"truncate text-xs font-mono 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(\"mb-4 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\"flex w-full items-center justify-between gap-3 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=\"flex min-w-0 items-center gap-2\">\n\t\t\t\t<BracesIcon className=\"size-4 shrink-0 text-muted-foreground\" />\n\t\t\t\t<span className=\"truncate text-sm font-medium\">{title}</span>\n\t\t\t\t{isRunning && (\n\t\t\t\t\t<span className=\"size-2 shrink-0 rounded-full bg-primary 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\"size-4 shrink-0 text-muted-foreground transition-transform duration-200\",\n\t\t\t\t\topen && \"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\"flex items-center gap-3 rounded-lg border border-border px-3 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=\"size-8 shrink-0 rounded-full object-cover\"\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex size-8 shrink-0 items-center justify-center rounded-full border border-border bg-muted\">\n\t\t\t\t\t<ServerIcon className=\"size-4 text-muted-foreground\" />\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"flex min-w-0 flex-col\">\n\t\t\t\t{serverName && (\n\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">{serverName}</span>\n\t\t\t\t)}\n\t\t\t\t<span className=\"truncate text-sm font-semibold\">{toolName}</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\"grid transition-[grid-template-rows,opacity] duration-200 ease-out\",\n\t\t\t\topen ? \"grid-rows-[1fr] opacity-100\" : \"grid-rows-[0fr] opacity-0\",\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"min-h-0 overflow-hidden\">\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"mt-2 space-y-3 rounded-lg border border-border bg-background 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(\"space-y-2\", className)} {...props}>\n\t\t\t\t<h4 className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n\t\t\t\t\tError\n\t\t\t\t</h4>\n\t\t\t\t<div className=\"rounded-lg bg-destructive/10 p-3 text-xs 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(\"rounded-md border 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=\"flex items-center justify-between rounded-md border border-border bg-muted/50 px-4 py-3 text-sm 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=\"text-xs font-medium text-primary 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 className={cn(\"flex flex-wrap gap-2 px-3 py-2\", className)} {...props}>\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=\"h-7 rounded-full bg-accent 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\"rounded-full border border-border bg-background px-3 py-1 text-xs\",\n\t\t\t\t\t\t\t\t\"text-foreground hover:bg-accent hover:border-primary/30\",\n\t\t\t\t\t\t\t\t\"transition-all duration-200 ease-out cursor-pointer\",\n\t\t\t\t\t\t\t\t\"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: \"#303030\",\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\", \"--color-primary\"],\n\tprimaryForeground: [\"--ww-primary-fg\", \"--color-primary-foreground\"],\n\tbackgroundColor: [\"--ww-bg\", \"--color-background\"],\n\ttextColor: [\"--ww-text\", \"--color-foreground\", \"--color-accent-foreground\"],\n\tmutedColor: [\"--ww-muted\", \"--color-muted-foreground\"],\n\tborderColor: [\"--ww-border\", \"--color-border\"],\n\tassistantBubbleColor: [\"--ww-assistant-bubble\", \"--color-accent\"],\n\tuserBubbleColor: [\"--ww-user-bubble\"],\n\tinputBackgroundColor: [\"--ww-input-bg\", \"--color-input\"],\n\tborderRadius: [\"--ww-radius\", \"--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\", \"--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\"flex flex-col font-[family-name:var(--ww-font)] text-foreground bg-background rounded-[var(--ww-radius)] border border-border shadow-md overflow-hidden transition-shadow duration-300\",\n\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\"ring-2 ring-blue-400/70 ring-offset-2 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=\"shrink-0 flex items-center gap-3 px-4 py-2 border-b 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 && <span className=\"size-2.5 rounded-full bg-status\" />}\n\t\t\t\t\t<div className=\"flex-1 min-w-0\">\n\t\t\t\t\t\t<div className=\"text-xs font-semibold truncate\">{title}</div>\n\t\t\t\t\t\t{subtitle && (\n\t\t\t\t\t\t\t<div className=\"text-[11px] text-muted-foreground 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=\"flex-1 min-h-0 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=\"border-t border-border\"\n\t\t\t\t/>\n\n\t\t\t\t{/* Input */}\n\t\t\t\t<div className=\"shrink-0 border-t border-border 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(\"rounded-none border-0\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"flex items-center gap-1 px-3 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=\"min-h-0 py-1.5 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","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","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"]}
|