@waniwani/sdk 0.0.9 → 0.0.12
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 +60 -16
- package/dist/chat/index.js +1 -42
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/server/index.d.ts +58 -0
- package/dist/chat/server/index.js +2 -0
- package/dist/chat/server/index.js.map +1 -0
- package/dist/chat/styles.css +2 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/package.json +58 -4
- package/dist/chat/embed.js +0 -150
- package/dist/chat/embed.js.map +0 -1
package/dist/chat/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
1
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
3
|
|
|
3
4
|
interface ChatTheme {
|
|
@@ -25,44 +26,87 @@ interface ChatTheme {
|
|
|
25
26
|
messageBorderRadius?: number;
|
|
26
27
|
/** Font family */
|
|
27
28
|
fontFamily?: string;
|
|
29
|
+
/** Header background color (card layout). Falls back to backgroundColor. */
|
|
30
|
+
headerBackgroundColor?: string;
|
|
31
|
+
/** Header text color. Falls back to textColor. */
|
|
32
|
+
headerTextColor?: string;
|
|
33
|
+
/** Status dot color. Defaults to green (#22c55e). */
|
|
34
|
+
statusColor?: string;
|
|
35
|
+
/** Tool call JSON section background. Defaults to light gray / #262626 in dark. */
|
|
36
|
+
toolCardColor?: string;
|
|
28
37
|
}
|
|
29
|
-
interface
|
|
38
|
+
interface ChatBaseProps {
|
|
30
39
|
/** WaniWani project API key */
|
|
31
40
|
apiKey?: string;
|
|
32
41
|
/** Chat API endpoint URL. Defaults to WaniWani hosted endpoint */
|
|
33
42
|
api?: string;
|
|
34
43
|
/** Initial greeting shown before user types */
|
|
35
44
|
welcomeMessage?: string;
|
|
36
|
-
/** Header title */
|
|
37
|
-
title?: string;
|
|
38
|
-
/** Header subtitle */
|
|
39
|
-
subtitle?: string;
|
|
40
45
|
/** Theme overrides */
|
|
41
46
|
theme?: ChatTheme;
|
|
42
47
|
/** Additional headers to send with chat API requests */
|
|
43
48
|
headers?: Record<string, string>;
|
|
44
49
|
/** Additional body fields to send with each chat request */
|
|
45
50
|
body?: Record<string, unknown>;
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
/** Chat panel height in pixels */
|
|
49
|
-
height?: number;
|
|
51
|
+
/** Enable file attachments in the input. Defaults to false. */
|
|
52
|
+
allowAttachments?: boolean;
|
|
50
53
|
/** Callback fired when a message is sent */
|
|
51
54
|
onMessageSent?: (message: string) => void;
|
|
52
55
|
/** Callback fired when a response is received */
|
|
53
56
|
onResponseReceived?: () => void;
|
|
57
|
+
/** Endpoint URL for fetching MCP app resources (HTML widgets). Defaults to "/api/mcp/resource" */
|
|
58
|
+
resourceEndpoint?: string;
|
|
59
|
+
}
|
|
60
|
+
interface ChatBarProps extends ChatBaseProps {
|
|
61
|
+
/** Chat bar width in pixels. Defaults to 600. */
|
|
62
|
+
width?: number;
|
|
63
|
+
/** Max height of the expanded messages panel in pixels. Defaults to 400. */
|
|
64
|
+
expandedHeight?: number;
|
|
65
|
+
}
|
|
66
|
+
interface ChatCardProps extends ChatBaseProps {
|
|
67
|
+
/** Title shown in the card header. Defaults to "Assistant". */
|
|
68
|
+
title?: string;
|
|
69
|
+
/** Subtitle or status text shown under the title. */
|
|
70
|
+
subtitle?: string;
|
|
71
|
+
/** Show the status dot in the header. Defaults to true. */
|
|
72
|
+
showStatus?: boolean;
|
|
73
|
+
/** Card width in pixels. Defaults to 400. */
|
|
74
|
+
width?: number;
|
|
75
|
+
/** Card height in pixels. Defaults to 600. */
|
|
76
|
+
height?: number;
|
|
54
77
|
}
|
|
55
|
-
interface
|
|
56
|
-
/**
|
|
57
|
-
|
|
78
|
+
interface ChatHandle {
|
|
79
|
+
/** Programmatically send a user message into the chat */
|
|
80
|
+
sendMessage: (text: string) => void;
|
|
81
|
+
}
|
|
82
|
+
/** @deprecated Use ChatBarProps instead */
|
|
83
|
+
type ChatWidgetProps = ChatBarProps;
|
|
84
|
+
|
|
85
|
+
declare const ChatBar: react.ForwardRefExoticComponent<ChatBarProps & react.RefAttributes<ChatHandle>>;
|
|
86
|
+
|
|
87
|
+
interface McpAppFrameProps {
|
|
88
|
+
resourceUri: string;
|
|
89
|
+
toolInput: Record<string, unknown>;
|
|
90
|
+
toolResult: {
|
|
91
|
+
content?: Array<{
|
|
92
|
+
type: string;
|
|
93
|
+
text?: string;
|
|
94
|
+
}>;
|
|
95
|
+
structuredContent?: Record<string, unknown>;
|
|
96
|
+
};
|
|
97
|
+
resourceEndpoint?: string;
|
|
98
|
+
isDark?: boolean;
|
|
99
|
+
className?: string;
|
|
100
|
+
/** When true, the iframe height auto-adapts to its content. Set via `_meta.ui.autoHeight` in the tool result. */
|
|
101
|
+
autoHeight?: boolean;
|
|
58
102
|
}
|
|
103
|
+
declare function McpAppFrame({ resourceUri, toolInput, toolResult, resourceEndpoint, isDark, className, autoHeight, }: McpAppFrameProps): react_jsx_runtime.JSX.Element;
|
|
59
104
|
|
|
60
|
-
declare
|
|
61
|
-
_shadowRoot?: ShadowRoot;
|
|
62
|
-
}): react_jsx_runtime.JSX.Element;
|
|
105
|
+
declare const ChatCard: react.ForwardRefExoticComponent<ChatCardProps & react.RefAttributes<ChatHandle>>;
|
|
63
106
|
|
|
64
107
|
declare const DEFAULT_THEME: Required<ChatTheme>;
|
|
108
|
+
declare const DARK_THEME: ChatTheme;
|
|
65
109
|
declare function mergeTheme(userTheme?: ChatTheme): Required<ChatTheme>;
|
|
66
110
|
declare function themeToCSSProperties(theme: Required<ChatTheme>): Record<string, string>;
|
|
67
111
|
|
|
68
|
-
export { type
|
|
112
|
+
export { ChatBar, type ChatBarProps, type ChatBaseProps, ChatCard, type ChatCardProps, type ChatHandle, type ChatTheme, ChatBar as ChatWidget, type ChatWidgetProps, DARK_THEME, DEFAULT_THEME, McpAppFrame, type McpAppFrameProps, mergeTheme, themeToCSSProperties };
|
package/dist/chat/index.js
CHANGED
|
@@ -1,44 +1,3 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{useChat as xe}from"@ai-sdk/react";import{DefaultChatTransport as ve}from"ai";import{useCallback as Ce,useEffect as j,useRef as S,useState as ke}from"react";import{useCallback as f,useRef as se}from"react";function H(r){let e=se(r);e.current=r;let a=f((l,s)=>{let{apiKey:u,baseUrl:c="https://app.waniwani.ai"}=e.current;u&&fetch(`${c}/api/mcp/events`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${u}`},body:JSON.stringify({event:l,properties:s})}).catch(()=>{})},[]),n=f(()=>{a("tool.called",{name:"chat.opened",type:"other"})},[a]),t=f(()=>{a("tool.called",{name:"chat.message_sent",type:"other"})},[a]),i=f(()=>{a("tool.called",{name:"chat.response_received",type:"other"})},[a]);return{trackChatOpened:n,trackMessageSent:t,trackResponseReceived:i}}function B(){return`
|
|
3
|
-
/* WaniWani Chat Widget Reset & Styles */
|
|
4
|
-
*,
|
|
5
|
-
*::before,
|
|
6
|
-
*::after {
|
|
7
|
-
box-sizing: border-box;
|
|
8
|
-
margin: 0;
|
|
9
|
-
padding: 0;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
@keyframes ww-fade-in {
|
|
13
|
-
from { opacity: 0; }
|
|
14
|
-
to { opacity: 1; }
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
@keyframes ww-bounce {
|
|
18
|
-
0%, 60%, 100% { transform: translateY(0); }
|
|
19
|
-
30% { transform: translateY(-4px); }
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
@keyframes ww-pulse {
|
|
23
|
-
0%, 100% { opacity: 0.4; }
|
|
24
|
-
50% { opacity: 1; }
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/* Scrollbar */
|
|
28
|
-
.ww-scrollbar::-webkit-scrollbar {
|
|
29
|
-
width: 4px;
|
|
30
|
-
}
|
|
31
|
-
.ww-scrollbar::-webkit-scrollbar-track {
|
|
32
|
-
background: transparent;
|
|
33
|
-
}
|
|
34
|
-
.ww-scrollbar::-webkit-scrollbar-thumb {
|
|
35
|
-
background: var(--ww-border);
|
|
36
|
-
border-radius: 4px;
|
|
37
|
-
}
|
|
38
|
-
.ww-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
39
|
-
background: var(--ww-muted);
|
|
40
|
-
}
|
|
41
|
-
`.trim()}var W={primaryColor:"#6366f1",primaryForeground:"#ffffff",backgroundColor:"#ffffff",textColor:"#1f2937",mutedColor:"#6b7280",borderColor:"#e5e7eb",assistantBubbleColor:"#f3f4f6",userBubbleColor:"#6366f1",inputBackgroundColor:"#f9fafb",borderRadius:16,messageBorderRadius:12,fontFamily:"system-ui, -apple-system, 'Segoe UI', sans-serif"},oe={primaryColor:"--ww-primary",primaryForeground:"--ww-primary-fg",backgroundColor:"--ww-bg",textColor:"--ww-text",mutedColor:"--ww-muted",borderColor:"--ww-border",assistantBubbleColor:"--ww-assistant-bubble",userBubbleColor:"--ww-user-bubble",inputBackgroundColor:"--ww-input-bg",borderRadius:"--ww-radius",messageBorderRadius:"--ww-msg-radius",fontFamily:"--ww-font"};function b(r){return{...W,...r}}function y(r){let e={};for(let[a,n]of Object.entries(oe)){let t=r[a];e[n]=typeof t=="number"?`${t}px`:String(t)}return e}import{jsx as x,jsxs as ie}from"react/jsx-runtime";function L(r){let{title:e,subtitle:a}=r;return x("div",{style:{display:"flex",alignItems:"center",padding:"14px 16px",borderBottom:"1px solid var(--ww-border)",backgroundColor:"var(--ww-primary)",color:"var(--ww-primary-fg)",borderRadius:"var(--ww-radius) var(--ww-radius) 0 0"},children:ie("div",{style:{display:"flex",flexDirection:"column",gap:"2px"},children:[x("span",{style:{fontWeight:600,fontSize:"15px"},children:e}),a&&x("span",{style:{fontSize:"12px",opacity:.85},children:a})]})})}import{useCallback as le,useEffect as pe,useRef as de}from"react";import{jsx as w,jsxs as _}from"react/jsx-runtime";var A={xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor","aria-hidden":"true",role:"img"};function U(r){let e=r.size??20;return _("svg",{...A,width:e,height:e,children:[w("title",{children:"Send"}),w("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5"})]})}function F(r){let e=r.size??14;return _("svg",{...A,width:e,height:e,children:[w("title",{children:"Tool"}),w("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M11.42 15.17 17.25 21A2.652 2.652 0 0 0 21 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 1 1-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 0 0 4.486-6.336l-3.276 3.277a3.004 3.004 0 0 1-2.25-2.25l3.276-3.276a4.5 4.5 0 0 0-6.336 4.486c.048.58.024 1.194-.14 1.743"})]})}import{jsx as v,jsxs as ue}from"react/jsx-runtime";function $(r){let{value:e,onChange:a,onSend:n,disabled:t}=r,i=de(null),l=le(s=>{s.key==="Enter"&&!s.shiftKey&&(s.preventDefault(),!t&&e.trim()&&n())},[t,e,n]);return pe(()=>{let s=i.current;s&&(s.style.height="auto",s.style.height=`${Math.min(s.scrollHeight,120)}px`)},[]),ue("div",{style:{display:"flex",alignItems:"flex-end",gap:"8px",padding:"12px 16px",borderTop:"1px solid var(--ww-border)",backgroundColor:"var(--ww-bg)"},children:[v("textarea",{ref:i,value:e,onChange:s=>a(s.target.value),onKeyDown:l,disabled:t,placeholder:"Type a message...",rows:1,style:{flex:1,resize:"none",border:"1px solid var(--ww-border)",borderRadius:"8px",padding:"8px 12px",fontSize:"14px",lineHeight:1.5,fontFamily:"var(--ww-font)",backgroundColor:"var(--ww-input-bg)",color:"var(--ww-text)",outline:"none",maxHeight:"120px",transition:"border-color 0.15s"},onFocus:s=>{s.currentTarget.style.borderColor="var(--ww-primary)"},onBlur:s=>{s.currentTarget.style.borderColor="var(--ww-border)"}}),v("button",{type:"button",onClick:n,disabled:t||!e.trim(),style:{display:"flex",alignItems:"center",justifyContent:"center",width:"36px",height:"36px",borderRadius:"8px",border:"none",backgroundColor:t||!e.trim()?"var(--ww-border)":"var(--ww-primary)",color:t||!e.trim()?"var(--ww-muted)":"var(--ww-primary-fg)",cursor:t||!e.trim()?"not-allowed":"pointer",transition:"background-color 0.15s, color 0.15s",flexShrink:0},"aria-label":"Send message",children:v(U,{size:18})})]})}import{useEffect as he,useRef as be}from"react";import{Fragment as V,jsx as o}from"react/jsx-runtime";function N(r){let e=r.text.split(/\n\n+/);return o(V,{children:e.map((a,n)=>o(ce,{block:a.trim()},n))})}function ce(r){let{block:e}=r;if(e.startsWith("```")){let n=e.split(`
|
|
42
|
-
`).slice(1,-1).join(`
|
|
43
|
-
`);return o("pre",{style:{backgroundColor:"var(--ww-border)",borderRadius:"6px",padding:"8px 12px",overflowX:"auto",fontSize:"13px",lineHeight:1.5,margin:"4px 0"},children:o("code",{children:n})})}if(/^[-*]\s/.test(e)){let a=e.split(/\n/).filter(n=>/^[-*]\s/.test(n));return o("ul",{style:{paddingLeft:"20px",margin:"4px 0"},children:a.map((n,t)=>o("li",{children:o(K,{text:n.replace(/^[-*]\s/,"")})},t))})}return o("p",{style:{margin:"4px 0",lineHeight:1.5},children:o(K,{text:e})})}function K(r){let e=fe(r.text);return o(V,{children:e.map((a,n)=>o(ge,{part:a},n))})}function ge(r){let{part:e}=r;return e.type==="text"?o("span",{children:e.value}):e.type==="bold"?o("strong",{style:{fontWeight:600},children:e.value}):e.type==="italic"?o("em",{children:e.value}):e.type==="code"?o("code",{style:{backgroundColor:"var(--ww-border)",borderRadius:"3px",padding:"1px 4px",fontSize:"0.9em"},children:e.value}):e.type==="link"?o("a",{href:e.href,target:"_blank",rel:"noopener noreferrer",style:{color:"var(--ww-primary)",textDecoration:"underline"},children:e.value}):null}function fe(r){let e=[],a=/`([^`]+)`|\*\*([^*]+)\*\*|\*([^*]+)\*|\[([^\]]+)\]\(([^)]+)\)/g,n=0,t;for(t=a.exec(r);t!==null;)t.index>n&&e.push({type:"text",value:r.slice(n,t.index)}),t[1]!=null?e.push({type:"code",value:t[1]}):t[2]!=null?e.push({type:"bold",value:t[2]}):t[3]!=null?e.push({type:"italic",value:t[3]}):t[4]!=null&&t[5]!=null&&e.push({type:"link",value:t[4],href:t[5]}),n=t.index+t[0].length,t=a.exec(r);return n<r.length&&e.push({type:"text",value:r.slice(n)}),e}import{jsx as p,jsxs as me}from"react/jsx-runtime";function O(r){let{message:e}=r,a=e.role==="user";return p("div",{style:{display:"flex",justifyContent:a?"flex-end":"flex-start",marginBottom:"8px",animation:"ww-fade-in 0.2s ease-out"},children:p("div",{style:{maxWidth:"85%",padding:"10px 14px",borderRadius:a?"var(--ww-msg-radius) var(--ww-msg-radius) 4px var(--ww-msg-radius)":"var(--ww-msg-radius) var(--ww-msg-radius) var(--ww-msg-radius) 4px",backgroundColor:a?"var(--ww-user-bubble)":"var(--ww-assistant-bubble)",color:a?"var(--ww-primary-fg)":"var(--ww-text)",fontSize:"14px",lineHeight:1.5,wordBreak:"break-word"},children:e.parts.map((n,t)=>p(we,{part:n,isUser:a},t))})})}function we(r){let{part:e,isUser:a}=r;if(e.type==="text"&&e.text)return a?p("span",{children:e.text}):p(N,{text:e.text});if(e.type.startsWith("tool-")||e.type==="tool-invocation"){let n=e.toolName??e.type.replace("tool-",""),t=e.state==="input-streaming"||e.state==="input-available";return me("div",{style:{display:"flex",alignItems:"center",gap:"6px",padding:"4px 0",fontSize:"12px",color:"var(--ww-muted)",fontStyle:"italic"},children:[p(F,{size:12}),p("span",{children:t?`Using ${n}...`:`Used ${n}`}),t&&p("span",{style:{animation:"ww-pulse 1.5s ease-in-out infinite"},children:"..."})]})}return null}import{jsx as d,jsxs as q}from"react/jsx-runtime";function G(r){let{messages:e,status:a,welcomeMessage:n}=r,t=be(null);he(()=>{t.current&&(t.current.scrollTop=t.current.scrollHeight)},[]);let i=a==="submitted"||a==="streaming";return q("div",{ref:t,className:"ww-scrollbar",style:{flex:1,overflowY:"auto",padding:"16px",display:"flex",flexDirection:"column"},children:[n&&e.length===0&&d("div",{style:{display:"flex",justifyContent:"flex-start",marginBottom:"8px"},children:d("div",{style:{maxWidth:"85%",padding:"10px 14px",borderRadius:"var(--ww-msg-radius) var(--ww-msg-radius) var(--ww-msg-radius) 4px",backgroundColor:"var(--ww-assistant-bubble)",color:"var(--ww-text)",fontSize:"14px",lineHeight:1.5},children:n})}),e.map(l=>d(O,{message:l},l.id)),i&&d("div",{style:{display:"flex",justifyContent:"flex-start",marginBottom:"8px"},children:q("div",{style:{padding:"10px 14px",borderRadius:"var(--ww-msg-radius) var(--ww-msg-radius) var(--ww-msg-radius) 4px",backgroundColor:"var(--ww-assistant-bubble)",display:"flex",gap:"4px",alignItems:"center"},children:[d(C,{delay:"0s"}),d(C,{delay:"0.15s"}),d(C,{delay:"0.3s"})]})})]})}function C(r){return d("span",{style:{width:"6px",height:"6px",borderRadius:"50%",backgroundColor:"var(--ww-muted)",animation:"ww-bounce 1.2s ease-in-out infinite",animationDelay:r.delay}})}import{jsx as k,jsxs as ye}from"react/jsx-runtime";function Y(r){let{messages:e,input:a,onInputChange:n,onSend:t,status:i,title:l,subtitle:s,welcomeMessage:u,width:c,height:m}=r;return ye("div",{className:"ww-panel",style:{width:`${c}px`,height:`${m}px`,display:"flex",flexDirection:"column",backgroundColor:"var(--ww-bg)",borderRadius:"var(--ww-radius)",border:"1px solid var(--ww-border)",boxShadow:"0 20px 60px -12px rgba(0, 0, 0, 0.15), 0 8px 20px -8px rgba(0, 0, 0, 0.1)",overflow:"hidden",fontFamily:"var(--ww-font)"},children:[k(L,{title:l,subtitle:s}),k(G,{messages:e,status:i,welcomeMessage:u}),k($,{value:a,onChange:n,onSend:t,disabled:i!=="ready"})]})}import{jsx as J}from"react/jsx-runtime";var Se="https://app.waniwani.ai/api/chat";function Me(r){let{apiKey:e,api:a=Se,welcomeMessage:n,title:t="Chat",subtitle:i,theme:l,headers:s,body:u,width:c=400,height:m=600,onMessageSent:M,onResponseReceived:X,_shadowRoot:h}=r,Z=b(l),Q=y(Z),{trackChatOpened:R,trackMessageSent:T,trackResponseReceived:ee}=H({apiKey:e}),te=S(new ve({api:a,headers:{...s,...e?{"x-waniwani-api-key":e}:{}},body:u})),{messages:re,sendMessage:P,status:ae}=xe({transport:te.current,onFinish(){ee(),X?.()}}),[g,I]=ke(""),ne=Ce(()=>{g.trim()&&(P({text:g}),T(),M?.(g),I(""))},[g,P,T,M]),E=S(!1);j(()=>{if(h&&!E.current){E.current=!0;let D=document.createElement("style");D.textContent=B(),h.appendChild(D)}},[h]);let z=S(!1);return j(()=>{z.current||(z.current=!0,R())},[R]),J("div",{style:Q,"data-waniwani-chat":"",children:J(Y,{messages:re,input:g,onInputChange:I,onSend:ne,status:ae,title:t,subtitle:i,welcomeMessage:n,width:c,height:m})})}export{Me as ChatWidget,W as DEFAULT_THEME,b as mergeTheme,y as themeToCSSProperties};
|
|
2
|
+
import{forwardRef as St,useCallback as Bt,useEffect as Dt,useImperativeHandle as Ft,useRef as Ut,useState as Ot}from"react";import{ArrowDownIcon as Ue}from"lucide-react";import{useCallback as Oe}from"react";import{StickToBottom as de,useStickToBottomContext as ze}from"use-stick-to-bottom";import{clsx as Be}from"clsx";import{twMerge as De}from"tailwind-merge";function i(...e){return De(Be(e))}import{jsx as Fe}from"react/jsx-runtime";var S=({className:e,variant:t="default",size:o="default",type:r="button",...n})=>Fe("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 W}from"react/jsx-runtime";var V=({className:e,...t})=>W(de,{className:i("relative flex-1 overflow-y-hidden",e),initial:"smooth",resize:"smooth",role:"log",...t}),q=({className:e,...t})=>W(de.Content,{className:i("flex flex-col gap-8 p-4",e),...t}),J=({className:e,...t})=>{let{isAtBottom:o,scrollToBottom:r}=ze(),n=Oe(()=>{r()},[r]);return!o&&W(S,{className:i("absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full",e),onClick:n,size:"icon",variant:"outline",...t,children:W(Ue,{className:"size-4"})})};import{ArrowUpIcon as je,LoaderIcon as $e,PaperclipIcon as _e,SquareIcon as We,XIcon as Ve}from"lucide-react";import{nanoid as qe}from"nanoid";import{createContext as Je,useCallback as R,useContext as Ke,useEffect as ne,useMemo as Ge,useRef as se,useState as fe}from"react";import{jsx as N,jsxs as be}from"react/jsx-runtime";var Xe=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}},ge=Je(null),he=()=>{let e=Ke(ge);if(!e)throw new Error("usePromptInputAttachments must be used within a PromptInput");return e},K=({className:e,accept:t,multiple:o,globalDrop:r,maxFiles:n,maxFileSize:l,onSubmit:a,children:p,...v})=>{let u=se(null),f=se(null),[g,m]=fe([]),w=se(g);ne(()=>{w.current=g},[g]);let P=R(()=>{u.current?.click()},[]),h=R(c=>{let y=[...c];if(y.length===0)return;let C=L=>l?L.size<=l:!0,T=y.filter(C);m(L=>{let A=typeof n=="number"?Math.max(0,n-L.length):void 0,D=typeof A=="number"?T.slice(0,A):T;return[...L,...D.map(U=>({filename:U.name,id:qe(),mediaType:U.type,type:"file",url:URL.createObjectURL(U)}))]})},[n,l]),s=R(c=>{m(y=>{let C=y.find(T=>T.id===c);return C?.url&&URL.revokeObjectURL(C.url),y.filter(T=>T.id!==c)})},[]),d=R(()=>{m(c=>{for(let y of c)y.url&&URL.revokeObjectURL(y.url);return[]})},[]);ne(()=>()=>{for(let c of w.current)c.url&&URL.revokeObjectURL(c.url)},[]);let b=R(c=>{c.currentTarget.files&&h(c.currentTarget.files),c.currentTarget.value=""},[h]);ne(()=>{if(!r)return;let c=C=>{C.dataTransfer?.types?.includes("Files")&&C.preventDefault()},y=C=>{C.dataTransfer?.types?.includes("Files")&&C.preventDefault(),C.dataTransfer?.files&&C.dataTransfer.files.length>0&&h(C.dataTransfer.files)};return document.addEventListener("dragover",c),document.addEventListener("drop",y),()=>{document.removeEventListener("dragover",c),document.removeEventListener("drop",y)}},[h,r]);let M=R(async c=>{c.preventDefault();let y=c.currentTarget,T=new FormData(y).get("message")||"";y.reset();let L=await Promise.all(g.map(async({id:A,...D})=>{if(D.url?.startsWith("blob:")){let U=await Xe(D.url);return{...D,url:U??D.url}}return D}));try{let A=a({files:L,text:T},c);A instanceof Promise&&await A,d()}catch{}},[g,a,d]),k=Ge(()=>({add:h,clear:d,files:g,openFileDialog:P,remove:s}),[g,h,s,d,P]);return be(ge.Provider,{value:k,children:[N("input",{accept:t,"aria-label":"Upload files",className:"hidden",multiple:o,onChange:b,ref:u,title:"Upload files",type:"file"}),N("form",{className:i("flex w-full flex-col rounded-lg border border-border bg-background",e),onSubmit:M,ref:f,...v,children:p})]})};var G=({onChange:e,onKeyDown:t,className:o,placeholder:r="What would you like to know?",...n})=>{let l=he(),[a,p]=fe(!1),v=R(f=>{if(t?.(f),!f.defaultPrevented){if(f.key==="Enter"){if(a||f.nativeEvent.isComposing||f.shiftKey)return;f.preventDefault();let{form:g}=f.currentTarget;if(g?.querySelector('button[type="submit"]')?.disabled)return;g?.requestSubmit()}if(f.key==="Backspace"&&f.currentTarget.value===""&&l.files.length>0){f.preventDefault();let g=l.files.at(-1);g&&l.remove(g.id)}}},[t,a,l]),u=R(f=>{let g=f.clipboardData?.items;if(!g)return;let m=[];for(let w of g)if(w.kind==="file"){let P=w.getAsFile();P&&m.push(P)}m.length>0&&(f.preventDefault(),l.add(m))},[l]);return N("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:()=>p(!1),onCompositionStart:()=>p(!0),onKeyDown:v,onPaste:u,placeholder:r,onChange:e,...n})},X=({className:e,status:t,onStop:o,onClick:r,children:n,...l})=>{let a=t==="submitted"||t==="streaming",p=N(je,{className:"size-4"});t==="submitted"?p=N($e,{className:"size-4 animate-spin"}):t==="streaming"&&(p=N(We,{className:"size-4"}));let v=R(u=>{if(a&&o){u.preventDefault(),o();return}r?.(u)},[a,o,r]);return N(S,{"aria-label":a?"Stop":"Submit",className:i("bg-foreground text-background hover:bg-foreground",e),onClick:v,size:"icon-sm",type:a&&o?"button":"submit",variant:"ghost",...l,children:n??p})},Q=({className:e,children:t,...o})=>{let r=he();return r.files.length>0?be(S,{className:i("group relative",e),onClick:()=>r.clear(),size:"icon-sm",type:"button",variant:"ghost","aria-label":"Remove all attachments",...o,children:[N("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}),N(Ve,{className:"absolute size-4 opacity-0 transition-opacity group-hover:opacity-100"})]}):N(S,{className:i(e),onClick:()=>r.openFileDialog(),size:"icon-sm",type:"button",variant:"ghost",...o,children:t??N(_e,{className:"size-4"})})};import{FileIcon as Qe}from"lucide-react";import{jsx as O,jsxs as Ze}from"react/jsx-runtime";var xe=({files:e,className:t,...o})=>e.length===0?null:O("div",{className:i("flex flex-wrap gap-1.5",t),...o,children:e.map((r,n)=>O(Ye,{file:r},n))});function Ye({file:e}){return e.mediaType?.startsWith("image/")&&e.url?O("img",{src:e.url,alt:e.filename??"attachment",className:"h-16 max-w-32 rounded object-cover"}):Ze("span",{className:"inline-flex items-center gap-1.5 rounded bg-background/20 px-2 py-1 text-xs",children:[O(Qe,{className:"size-3 shrink-0"}),O("span",{className:"max-w-24 truncate",children:e.filename??"file"})]})}import{jsx as Ce}from"react/jsx-runtime";var ae=({className:e,size:t=5,...o})=>Ce("div",{className:i("flex items-center gap-1",e),...o,children:[0,1,2].map(r=>Ce("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 et}from"@streamdown/cjk";import{code as tt}from"@streamdown/code";import{memo as ot}from"react";import{Streamdown as rt}from"streamdown";import{jsx as ie}from"react/jsx-runtime";var Y=({className:e,from:t,...o})=>ie("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}),Z=({children:e,className:t,...o})=>ie("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}),nt={cjk:et,code:tt},ee=ot(({className:e,...t})=>ie(rt,{className:i("size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",e),plugins:nt,...t}),(e,t)=>e.children===t.children);ee.displayName="MessageResponse";import{jsx as st}from"react/jsx-runtime";function ve({className:e,text:t,...o}){return t?st("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 at,CheckIcon as it,ChevronDownIcon as lt,ChevronRightIcon as ut,ClipboardCopyIcon as pt,ServerIcon as No}from"lucide-react";import{createContext as ct,useCallback as mt,useContext as Te,useEffect as dt,useMemo as ft,useRef as gt,useState as le}from"react";import{Fragment as ye,jsx as x,jsxs as B}from"react/jsx-runtime";function ht(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=[],l=t-2;for(let[a,p]of r){if(l<=8)break;let v=a.length>4?`${a.slice(0,4)}\u2026`:a,u;typeof p=="string"?u=p.length>2?`'${p.slice(0,1)}\u2026`:`'${p}'`:Array.isArray(p)?u=`Array(${p.length})`:typeof p=="object"&&p!==null?u="{\u2026}":u=String(p);let f=`${v}\u2009${u}`;n.push(f),l-=f.length+3}return`{${n.join(", ")}}`}function bt({text:e,className:t}){let[o,r]=le(!1),n=gt(null);dt(()=>()=>{n.current&&clearTimeout(n.current)},[]);let l=mt(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 x(S,{variant:"ghost",size:"sm",onClick:l,className:i("h-auto gap-1 px-1.5 py-0.5 text-xs text-muted-foreground hover:text-foreground",t),children:o?B(ye,{children:[x(it,{className:"size-3.5"}),x("span",{children:"Copied"})]}):B(ye,{children:[x(pt,{className:"size-3.5"}),x("span",{children:"Copy"})]})})}function we({data:e,label:t,className:o,...r}){let[n,l]=le(!1),a=ft(()=>JSON.stringify(e,null,2),[e]),p=ht(e);return B("div",{className:i("rounded-lg bg-tool-card",o),...r,children:[B("div",{className:"flex items-center justify-between px-3 pt-2.5 pb-1.5",children:[x("span",{className:"text-xs font-medium text-muted-foreground",children:t}),x(bt,{text:a})]}),B("button",{type:"button",onClick:()=>l(v=>!v),className:"flex w-full items-start gap-2 px-3 pb-3 text-left",children:[x(ut,{className:i("mt-0.5 size-3.5 shrink-0 text-muted-foreground transition-transform duration-150",n&&"rotate-90")}),n?x("pre",{className:"overflow-x-auto text-xs font-mono text-foreground whitespace-pre-wrap break-all",children:x("code",{children:a})}):x("span",{className:"truncate text-xs font-mono text-foreground/80",children:p})]})]})}var ue=ct({open:!1,toggle:()=>{}});function Pe({className:e,defaultOpen:t=!1,children:o,...r}){let[n,l]=le(t);return x(ue.Provider,{value:{open:n,toggle:()=>l(a=>!a)},children:x("div",{className:i("mb-4 w-full",e),"data-state":n?"open":"closed",...r,children:o})})}function Me({className:e,title:t,state:o,...r}){let{open:n,toggle:l}=Te(ue),a=o==="input-available"||o==="input-streaming";return B("button",{type:"button",onClick:l,className:i("flex w-full items-center justify-between gap-3 py-1.5",e),"aria-expanded":n,...r,children:[B("div",{className:"flex min-w-0 items-center gap-2",children:[x(at,{className:"size-4 shrink-0 text-muted-foreground"}),x("span",{className:"truncate text-sm font-medium",children:t}),a&&x("span",{className:"size-2 shrink-0 rounded-full bg-primary animate-pulse"})]}),x(lt,{className:i("size-4 shrink-0 text-muted-foreground transition-transform duration-200",n&&"rotate-180")})]})}function ke({className:e,children:t,...o}){let{open:r}=Te(ue);return x("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:x("div",{className:"min-h-0 overflow-hidden",children:x("div",{className:i("mt-2 space-y-3 rounded-lg border border-border bg-background p-3",e),...o,children:t})})})}function Ie({className:e,input:t,...o}){return x(we,{data:t,label:"Request",className:e,...o})}function Ne(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 Ee(e){let t=Ne(e)?.resourceUri;return typeof t=="string"?t:void 0}function Le(e){return Ne(e)?.autoHeight===!0}function Re({className:e,output:t,errorText:o,...r}){return t||o?o?B("div",{className:i("space-y-2",e),...r,children:[x("h4",{className:"text-xs font-medium uppercase tracking-wide text-muted-foreground",children:"Error"}),x("div",{className:"rounded-lg bg-destructive/10 p-3 text-xs text-destructive",children:o})]}):x(we,{data:t,label:"Response",className:e,...r}):null}import{useCallback as xt,useEffect as He,useMemo as Ct,useRef as z,useState as vt}from"react";import{jsx as Mt}from"react/jsx-runtime";var yt="/api/mcp/resource",Tt=500,wt=300,Pt="2026-01-26";function pe({resourceUri:e,toolInput:t,toolResult:o,resourceEndpoint:r=yt,isDark:n=!1,className:l,autoHeight:a=!0}){let p=z(null),v=z(t),u=z(o),f=z(!1),[g,m]=vt(wt);v.current=t,u.current=o;let w=xt(s=>a?Math.max(s,0):Math.min(Math.max(s,50),Tt),[a]),P=Ct(()=>`${r}?uri=${encodeURIComponent(e)}`,[r,e]),h=z(n);return h.current=n,He(()=>{let s=p.current;if(!s)return;let d=!1,b=k=>{s.contentWindow?.postMessage(k,"*")},M=k=>{if(d||k.source!==s.contentWindow)return;let c=k.data;if(!c||typeof c!="object"||c.jsonrpc!=="2.0")return;let y=c.method,C=c.id;if(y==="ui/initialize"&&C!=null){b({jsonrpc:"2.0",id:C,result:{protocolVersion:c.params?.protocolVersion??Pt,hostInfo:{name:"WaniWani Chat",version:"1.0.0"},hostCapabilities:{},hostContext:{theme:h.current?"dark":"light",autoHeight:a}}});return}if(y==="ui/notifications/initialized"){let T=v.current,L=u.current;b({jsonrpc:"2.0",method:"ui/notifications/tool-input",params:{arguments:T}});let A=L.content??[{type:"text",text:JSON.stringify(L)}];b({jsonrpc:"2.0",method:"ui/notifications/tool-result",params:{content:A,structuredContent:L.structuredContent}});return}if(y==="ui/notifications/size-changed"){if(f.current)return;let T=c.params?.height;typeof T=="number"&&!d&&m(w(T));return}if(y==="ui/open-link"&&C!=null){let T=c.params?.url;typeof T=="string"&&window.open(T,"_blank","noopener,noreferrer"),b({jsonrpc:"2.0",id:C,result:{}});return}y==="ping"&&C!=null&&b({jsonrpc:"2.0",id:C,result:{}})};return window.addEventListener("message",M),()=>{d=!0,window.removeEventListener("message",M)}},[a,w]),He(()=>{if(!a)return;let s=p.current;if(!s)return;let d,b=!1,M=()=>{if(!b)try{let k=s.contentDocument?.body;if(!k)return;d=new ResizeObserver(()=>{if(b)return;let c=s.contentDocument?.defaultView?.getComputedStyle(k),y=Number.parseInt(c?.marginTop??"0",10)||0,C=Number.parseInt(c?.marginBottom??"0",10)||0,T=Math.max(k.scrollHeight,k.offsetHeight)+y+C;T>0&&m(T)}),d.observe(k)}catch{}};return s.addEventListener("load",M),M(),()=>{b=!0,d?.disconnect(),s.removeEventListener("load",M)}},[a]),Mt("iframe",{ref:p,src:P,sandbox:"allow-scripts allow-forms allow-same-origin",className:i("w-full rounded-md border border-border",l),style:{height:g||void 0,border:"none",colorScheme:"auto"},title:"MCP App"})}import{Fragment as It,jsx as I,jsxs as F}from"react/jsx-runtime";function kt(e){return e.replace(/[-_]/g," ").replace(/^\w/,t=>t.toUpperCase())}function te({messages:e,status:t,welcomeMessage:o,resourceEndpoint:r,isDark:n}){let l=t==="submitted"||t==="streaming",a=e[e.length-1],p=e.length>0,v=l&&(!p||a.role==="user");return F(It,{children:[o&&I(Y,{from:"assistant",children:I(Z,{children:I(ee,{children:o})})}),e.map(u=>{let f=u.parts.filter(s=>s.type==="text"),g=u.parts.filter(s=>s.type==="reasoning"),m=u.parts.filter(s=>s.type==="file"),w=u.parts.filter(s=>"toolCallId"in s),P=u===a&&u.role==="assistant",h=f.length>0;return F(Y,{from:u.role,children:[g.map((s,d)=>I(ve,{text:s.text},`reasoning-${u.id}-${d}`)),w.map(s=>{let d="output"in s?s.output:void 0,b=d!==void 0?Ee(d):void 0,M=d!==void 0?Le(d):!1;return F("div",{children:[F(Pe,{defaultOpen:s.state==="output-available",children:[I(Me,{title:s.title??kt(s.toolName),state:s.state}),F(ke,{children:[I(Ie,{input:s.input}),d!==void 0&&I(Re,{output:d,errorText:"errorText"in s?s.errorText:void 0})]})]}),b&&d!==void 0&&I(pe,{resourceUri:b,toolInput:s.input??{},toolResult:{content:d.content,structuredContent:d.structuredContent},resourceEndpoint:r,isDark:n,autoHeight:M})]},s.toolCallId)}),F(Z,{children:[m.length>0&&I(xe,{files:m}),h?f.map((s,d)=>I(ee,{children:s.type==="text"?s.text:""},`${u.id}-${d}`)):P&&l&&I(ae,{})]})]},u.id)}),v&&I(Y,{from:"assistant",children:I(Z,{children:I(ae,{})})})]})}import{useChat as Nt}from"@ai-sdk/react";import{DefaultChatTransport as Et}from"ai";import{useCallback as Ae,useRef as Lt,useState as Rt}from"react";function oe(e){let{api:t="https://app.waniwani.ai/api/chat",headers:o,body:r,onMessageSent:n,onResponseReceived:l}=e,a=Lt(new Et({api:t,headers:{...o},body:r})),{messages:p,sendMessage:v,status:u}=Nt({transport:a.current,onFinish(){l?.()}}),[f,g]=Rt(""),m=Ae(b=>{let M=!!b.text?.trim(),k=!!b.files?.length;(M||k)&&(v({text:b.text||"",files:b.files}),n?.(b.text||""),g(""))},[v,n]),w=Ae(b=>{g(b.target.value)},[]),P=u==="submitted"||u==="streaming",h=p[p.length-1],s=p.length>0,d=P&&(!s||h.role==="user");return{messages:p,status:u,text:f,setText:g,handleSubmit:m,handleTextChange:w,isLoading:P,showLoaderBubble:d,lastMessage:h,hasMessages:s,sendMessage:v}}var Se={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"},Ht={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"},At={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 j(e){return{...Se,...e}}function re(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 $(e){let t={};for(let[o,r]of Object.entries(At)){let n=e[o],l=typeof n=="number"?`${n}px`:String(n);for(let a of r)t[a]=l}return t}import{jsx as H,jsxs as ce}from"react/jsx-runtime";var me=St(function(t,o){let{theme:r,width:n=600,expandedHeight:l=400,allowAttachments:a=!1,welcomeMessage:p,resourceEndpoint:v}=t,u=j(r),f=$(u),g=re(u),m=oe(t);Ft(o,()=>({sendMessage:b=>{m.handleSubmit({text:b,files:[]})}}),[m.handleSubmit]);let[w,P]=Ot(!1),h=Ut(null),s=w;Dt(()=>{if(!w)return;let b=M=>{h.current&&!h.current.contains(M.target)&&P(!1)};return document.addEventListener("mousedown",b),()=>document.removeEventListener("mousedown",b)},[w]);let d=Bt(()=>{P(!0)},[]);return ce("div",{ref:h,style:{...f,width:n},"data-waniwani-chat":"","data-waniwani-layout":"bar",...g?{"data-waniwani-dark":""}:{},className:"flex flex-col font-[family-name:var(--ww-font)] text-foreground",children:[H("div",{className:i("overflow-hidden bg-background/80 backdrop-blur-xl transition-all duration-300 ease-out",s?"opacity-100 translate-y-0":"opacity-0 translate-y-2 pointer-events-none max-h-0"),style:{...s?{maxHeight:l}: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:ce(V,{className:"flex-1",style:{height:l},children:[H(q,{children:H(te,{messages:m.messages,status:m.status,welcomeMessage:p,resourceEndpoint:v,isDark:g})}),H(J,{})]})}),H("div",{className:"shrink-0",children:H(K,{onSubmit:m.handleSubmit,globalDrop:a,multiple:a,className:i("rounded-[var(--ww-radius)] shadow-sm transition-all duration-300 ease-out"),children:ce("div",{className:"flex items-center gap-1 px-3 py-2",children:[a&&H(Q,{}),H(G,{onChange:m.handleTextChange,value:m.text,placeholder:"Ask anything...",onFocus:d,className:"min-h-0 py-1.5 px-2"}),H(X,{status:m.status})]})})})]})});import{forwardRef as zt,useImperativeHandle as jt}from"react";import{jsx as E,jsxs as _}from"react/jsx-runtime";var $t=zt(function(t,o){let{theme:r,title:n="Assistant",subtitle:l,showStatus:a=!0,width:p=500,height:v=600,allowAttachments:u=!1,welcomeMessage:f,resourceEndpoint:g}=t,m=j(r),w=$(m),P=re(m),h=oe(t);return jt(o,()=>({sendMessage:s=>{h.handleSubmit({text:s,files:[]})}}),[h.handleSubmit]),_("div",{style:{...w,width:p,height:v},"data-waniwani-chat":"","data-waniwani-layout":"card",...P?{"data-waniwani-dark":""}:{},className:"flex flex-col font-[family-name:var(--ww-font)] text-foreground bg-background rounded-[var(--ww-radius)] border border-border shadow-md overflow-hidden",children:[_("div",{className:"shrink-0 flex items-center gap-3 px-4 py-2 border-b border-border",style:{backgroundColor:m.headerBackgroundColor,color:m.headerTextColor},children:[a&&E("span",{className:"size-2.5 rounded-full bg-status"}),_("div",{className:"flex-1 min-w-0",children:[E("div",{className:"text-xs font-semibold truncate",children:n}),l&&E("div",{className:"text-[11px] text-muted-foreground truncate",children:l})]})]}),_(V,{className:"flex-1 min-h-0 bg-background",children:[E(q,{children:E(te,{messages:h.messages,status:h.status,welcomeMessage:f,resourceEndpoint:g,isDark:P})}),E(J,{})]}),E("div",{className:"shrink-0 border-t border-border bg-background",children:E(K,{onSubmit:h.handleSubmit,globalDrop:u,multiple:u,className:i("rounded-none border-0"),children:_("div",{className:"flex items-center gap-1 px-3 py-2",children:[u&&E(Q,{}),E(G,{onChange:h.handleTextChange,value:h.text,placeholder:"Ask anything...",className:"min-h-0 py-1.5 px-2"}),E(X,{status:h.status})]})})})]})});export{me as ChatBar,$t as ChatCard,me as ChatWidget,Ht as DARK_THEME,Se as DEFAULT_THEME,pe as McpAppFrame,j as mergeTheme,$ as themeToCSSProperties};
|
|
44
3
|
//# sourceMappingURL=index.js.map
|
package/dist/chat/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/chat/components/chat-widget.tsx","../../src/chat/hooks/use-chat-tracking.ts","../../src/chat/styles.ts","../../src/chat/theme.ts","../../src/chat/components/chat-header.tsx","../../src/chat/components/chat-input.tsx","../../src/chat/icons.tsx","../../src/chat/components/chat-messages.tsx","../../src/chat/components/chat-markdown.tsx","../../src/chat/components/chat-message.tsx","../../src/chat/components/chat-panel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport } from \"ai\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { ChatWidgetProps } from \"../@types\";\nimport { useChatTracking } from \"../hooks/use-chat-tracking\";\nimport { buildStyleSheet } from \"../styles\";\nimport { mergeTheme, themeToCSSProperties } from \"../theme\";\nimport { ChatPanel } from \"./chat-panel\";\n\nconst DEFAULT_API = \"https://app.waniwani.ai/api/chat\";\n\nexport function ChatWidget(\n\tprops: ChatWidgetProps & { _shadowRoot?: ShadowRoot },\n) {\n\tconst {\n\t\tapiKey,\n\t\tapi = DEFAULT_API,\n\t\twelcomeMessage,\n\t\ttitle = \"Chat\",\n\t\tsubtitle,\n\t\ttheme: userTheme,\n\t\theaders: userHeaders,\n\t\tbody,\n\t\twidth = 400,\n\t\theight = 600,\n\t\tonMessageSent,\n\t\tonResponseReceived,\n\t\t_shadowRoot,\n\t} = props;\n\n\tconst resolvedTheme = mergeTheme(userTheme);\n\tconst cssVars = themeToCSSProperties(resolvedTheme);\n\n\tconst { trackChatOpened, trackMessageSent, trackResponseReceived } =\n\t\tuseChatTracking({ apiKey });\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\t...(apiKey ? { \"x-waniwani-api-key\": apiKey } : {}),\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\ttrackResponseReceived();\n\t\t\tonResponseReceived?.();\n\t\t},\n\t});\n\n\tconst [input, setInput] = useState(\"\");\n\n\tconst handleSend = useCallback(() => {\n\t\tif (!input.trim()) return;\n\t\tsendMessage({ text: input });\n\t\ttrackMessageSent();\n\t\tonMessageSent?.(input);\n\t\tsetInput(\"\");\n\t}, [input, sendMessage, trackMessageSent, onMessageSent]);\n\n\t// Inject stylesheet into shadow root for embed mode\n\tconst styleInjected = useRef(false);\n\tuseEffect(() => {\n\t\tif (_shadowRoot && !styleInjected.current) {\n\t\t\tstyleInjected.current = true;\n\t\t\tconst style = document.createElement(\"style\");\n\t\t\tstyle.textContent = buildStyleSheet();\n\t\t\t_shadowRoot.appendChild(style);\n\t\t}\n\t}, [_shadowRoot]);\n\n\t// Track chat opened on mount\n\tconst tracked = useRef(false);\n\tuseEffect(() => {\n\t\tif (!tracked.current) {\n\t\t\ttracked.current = true;\n\t\t\ttrackChatOpened();\n\t\t}\n\t}, [trackChatOpened]);\n\n\treturn (\n\t\t<div style={cssVars} data-waniwani-chat=\"\">\n\t\t\t<ChatPanel\n\t\t\t\tmessages={messages}\n\t\t\t\tinput={input}\n\t\t\t\tonInputChange={setInput}\n\t\t\t\tonSend={handleSend}\n\t\t\t\tstatus={status}\n\t\t\t\ttitle={title}\n\t\t\t\tsubtitle={subtitle}\n\t\t\t\twelcomeMessage={welcomeMessage}\n\t\t\t\twidth={width}\n\t\t\t\theight={height}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","import { useCallback, useRef } from \"react\";\n\ninterface TrackingConfig {\n\tapiKey?: string;\n\tbaseUrl?: string;\n}\n\n/**\n * Lightweight browser-compatible tracking hook for chat events.\n * Fires events to the WaniWani API without requiring the full tracking client\n * (which depends on process.env).\n */\nexport function useChatTracking(config: TrackingConfig) {\n\tconst configRef = useRef(config);\n\tconfigRef.current = config;\n\n\tconst trackEvent = useCallback(\n\t\t(event: string, properties?: Record<string, unknown>) => {\n\t\t\tconst { apiKey, baseUrl = \"https://app.waniwani.ai\" } = configRef.current;\n\t\t\tif (!apiKey) return;\n\n\t\t\tfetch(`${baseUrl}/api/mcp/events`, {\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\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tevent,\n\t\t\t\t\tproperties,\n\t\t\t\t}),\n\t\t\t}).catch(() => {\n\t\t\t\t// Silently fail - tracking should never break the chat\n\t\t\t});\n\t\t},\n\t\t[],\n\t);\n\n\tconst trackChatOpened = useCallback(() => {\n\t\ttrackEvent(\"tool.called\", { name: \"chat.opened\", type: \"other\" });\n\t}, [trackEvent]);\n\n\tconst trackMessageSent = useCallback(() => {\n\t\ttrackEvent(\"tool.called\", { name: \"chat.message_sent\", type: \"other\" });\n\t}, [trackEvent]);\n\n\tconst trackResponseReceived = useCallback(() => {\n\t\ttrackEvent(\"tool.called\", {\n\t\t\tname: \"chat.response_received\",\n\t\t\ttype: \"other\",\n\t\t});\n\t}, [trackEvent]);\n\n\treturn { trackChatOpened, trackMessageSent, trackResponseReceived };\n}\n","export function buildStyleSheet(): string {\n\treturn `\n/* WaniWani Chat Widget Reset & Styles */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n@keyframes ww-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes ww-bounce {\n 0%, 60%, 100% { transform: translateY(0); }\n 30% { transform: translateY(-4px); }\n}\n\n@keyframes ww-pulse {\n 0%, 100% { opacity: 0.4; }\n 50% { opacity: 1; }\n}\n\n/* Scrollbar */\n.ww-scrollbar::-webkit-scrollbar {\n width: 4px;\n}\n.ww-scrollbar::-webkit-scrollbar-track {\n background: transparent;\n}\n.ww-scrollbar::-webkit-scrollbar-thumb {\n background: var(--ww-border);\n border-radius: 4px;\n}\n.ww-scrollbar::-webkit-scrollbar-thumb:hover {\n background: var(--ww-muted);\n}\n`.trim();\n}\n","import type { ChatTheme } from \"./@types\";\n\nexport const DEFAULT_THEME: Required<ChatTheme> = {\n\tprimaryColor: \"#6366f1\",\n\tprimaryForeground: \"#ffffff\",\n\tbackgroundColor: \"#ffffff\",\n\ttextColor: \"#1f2937\",\n\tmutedColor: \"#6b7280\",\n\tborderColor: \"#e5e7eb\",\n\tassistantBubbleColor: \"#f3f4f6\",\n\tuserBubbleColor: \"#6366f1\",\n\tinputBackgroundColor: \"#f9fafb\",\n\tborderRadius: 16,\n\tmessageBorderRadius: 12,\n\tfontFamily: \"system-ui, -apple-system, 'Segoe UI', sans-serif\",\n};\n\nconst CSS_VAR_MAP: Record<keyof ChatTheme, string> = {\n\tprimaryColor: \"--ww-primary\",\n\tprimaryForeground: \"--ww-primary-fg\",\n\tbackgroundColor: \"--ww-bg\",\n\ttextColor: \"--ww-text\",\n\tmutedColor: \"--ww-muted\",\n\tborderColor: \"--ww-border\",\n\tassistantBubbleColor: \"--ww-assistant-bubble\",\n\tuserBubbleColor: \"--ww-user-bubble\",\n\tinputBackgroundColor: \"--ww-input-bg\",\n\tborderRadius: \"--ww-radius\",\n\tmessageBorderRadius: \"--ww-msg-radius\",\n\tfontFamily: \"--ww-font\",\n};\n\nexport function mergeTheme(userTheme?: ChatTheme): Required<ChatTheme> {\n\treturn { ...DEFAULT_THEME, ...userTheme };\n}\n\nexport function themeToCSSProperties(\n\ttheme: Required<ChatTheme>,\n): Record<string, string> {\n\tconst vars: Record<string, string> = {};\n\tfor (const [key, cssVar] of Object.entries(CSS_VAR_MAP)) {\n\t\tconst value = theme[key as keyof ChatTheme];\n\t\tvars[cssVar] = typeof value === \"number\" ? `${value}px` : String(value);\n\t}\n\treturn vars;\n}\n","interface ChatHeaderProps {\n\ttitle: string;\n\tsubtitle?: string;\n}\n\nexport function ChatHeader(props: ChatHeaderProps) {\n\tconst { title, subtitle } = props;\n\n\treturn (\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\tdisplay: \"flex\",\n\t\t\t\talignItems: \"center\",\n\t\t\t\tpadding: \"14px 16px\",\n\t\t\t\tborderBottom: \"1px solid var(--ww-border)\",\n\t\t\t\tbackgroundColor: \"var(--ww-primary)\",\n\t\t\t\tcolor: \"var(--ww-primary-fg)\",\n\t\t\t\tborderRadius: \"var(--ww-radius) var(--ww-radius) 0 0\",\n\t\t\t}}\n\t\t>\n\t\t\t<div style={{ display: \"flex\", flexDirection: \"column\", gap: \"2px\" }}>\n\t\t\t\t<span style={{ fontWeight: 600, fontSize: \"15px\" }}>{title}</span>\n\t\t\t\t{subtitle && (\n\t\t\t\t\t<span style={{ fontSize: \"12px\", opacity: 0.85 }}>{subtitle}</span>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","import type React from \"react\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { SendIcon } from \"../icons\";\n\ninterface ChatInputProps {\n\tvalue: string;\n\tonChange: (value: string) => void;\n\tonSend: () => void;\n\tdisabled: boolean;\n}\n\nexport function ChatInput(props: ChatInputProps) {\n\tconst { value, onChange, onSend, disabled } = props;\n\tconst textareaRef = useRef<HTMLTextAreaElement>(null);\n\n\tconst handleKeyDown = useCallback(\n\t\t(e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n\t\t\tif (e.key === \"Enter\" && !e.shiftKey) {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (!disabled && value.trim()) {\n\t\t\t\t\tonSend();\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[disabled, value, onSend],\n\t);\n\n\t// Auto-resize textarea\n\tuseEffect(() => {\n\t\tconst textarea = textareaRef.current;\n\t\tif (textarea) {\n\t\t\ttextarea.style.height = \"auto\";\n\t\t\ttextarea.style.height = `${Math.min(textarea.scrollHeight, 120)}px`;\n\t\t}\n\t}, []);\n\n\treturn (\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\tdisplay: \"flex\",\n\t\t\t\talignItems: \"flex-end\",\n\t\t\t\tgap: \"8px\",\n\t\t\t\tpadding: \"12px 16px\",\n\t\t\t\tborderTop: \"1px solid var(--ww-border)\",\n\t\t\t\tbackgroundColor: \"var(--ww-bg)\",\n\t\t\t}}\n\t\t>\n\t\t\t<textarea\n\t\t\t\tref={textareaRef}\n\t\t\t\tvalue={value}\n\t\t\t\tonChange={(e) => onChange(e.target.value)}\n\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\tdisabled={disabled}\n\t\t\t\tplaceholder=\"Type a message...\"\n\t\t\t\trows={1}\n\t\t\t\tstyle={{\n\t\t\t\t\tflex: 1,\n\t\t\t\t\tresize: \"none\",\n\t\t\t\t\tborder: \"1px solid var(--ww-border)\",\n\t\t\t\t\tborderRadius: \"8px\",\n\t\t\t\t\tpadding: \"8px 12px\",\n\t\t\t\t\tfontSize: \"14px\",\n\t\t\t\t\tlineHeight: 1.5,\n\t\t\t\t\tfontFamily: \"var(--ww-font)\",\n\t\t\t\t\tbackgroundColor: \"var(--ww-input-bg)\",\n\t\t\t\t\tcolor: \"var(--ww-text)\",\n\t\t\t\t\toutline: \"none\",\n\t\t\t\t\tmaxHeight: \"120px\",\n\t\t\t\t\ttransition: \"border-color 0.15s\",\n\t\t\t\t}}\n\t\t\t\tonFocus={(e) => {\n\t\t\t\t\te.currentTarget.style.borderColor = \"var(--ww-primary)\";\n\t\t\t\t}}\n\t\t\t\tonBlur={(e) => {\n\t\t\t\t\te.currentTarget.style.borderColor = \"var(--ww-border)\";\n\t\t\t\t}}\n\t\t\t/>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={onSend}\n\t\t\t\tdisabled={disabled || !value.trim()}\n\t\t\t\tstyle={{\n\t\t\t\t\tdisplay: \"flex\",\n\t\t\t\t\talignItems: \"center\",\n\t\t\t\t\tjustifyContent: \"center\",\n\t\t\t\t\twidth: \"36px\",\n\t\t\t\t\theight: \"36px\",\n\t\t\t\t\tborderRadius: \"8px\",\n\t\t\t\t\tborder: \"none\",\n\t\t\t\t\tbackgroundColor:\n\t\t\t\t\t\tdisabled || !value.trim()\n\t\t\t\t\t\t\t? \"var(--ww-border)\"\n\t\t\t\t\t\t\t: \"var(--ww-primary)\",\n\t\t\t\t\tcolor:\n\t\t\t\t\t\tdisabled || !value.trim()\n\t\t\t\t\t\t\t? \"var(--ww-muted)\"\n\t\t\t\t\t\t\t: \"var(--ww-primary-fg)\",\n\t\t\t\t\tcursor: disabled || !value.trim() ? \"not-allowed\" : \"pointer\",\n\t\t\t\t\ttransition: \"background-color 0.15s, color 0.15s\",\n\t\t\t\t\tflexShrink: 0,\n\t\t\t\t}}\n\t\t\t\taria-label=\"Send message\"\n\t\t\t>\n\t\t\t\t<SendIcon size={18} />\n\t\t\t</button>\n\t\t</div>\n\t);\n}\n","import type React from \"react\";\n\nconst iconProps: React.SVGProps<SVGSVGElement> = {\n\txmlns: \"http://www.w3.org/2000/svg\",\n\tfill: \"none\",\n\tviewBox: \"0 0 24 24\",\n\tstrokeWidth: 1.5,\n\tstroke: \"currentColor\",\n\t\"aria-hidden\": \"true\",\n\trole: \"img\",\n};\n\nexport function SendIcon(props: { size?: number }) {\n\tconst size = props.size ?? 20;\n\treturn (\n\t\t<svg {...iconProps} width={size} height={size}>\n\t\t\t<title>Send</title>\n\t\t\t<path\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\td=\"M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nexport function ToolIcon(props: { size?: number }) {\n\tconst size = props.size ?? 14;\n\treturn (\n\t\t<svg {...iconProps} width={size} height={size}>\n\t\t\t<title>Tool</title>\n\t\t\t<path\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\td=\"M11.42 15.17 17.25 21A2.652 2.652 0 0 0 21 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 1 1-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 0 0 4.486-6.336l-3.276 3.277a3.004 3.004 0 0 1-2.25-2.25l3.276-3.276a4.5 4.5 0 0 0-6.336 4.486c.048.58.024 1.194-.14 1.743\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n","import { useEffect, useRef } from \"react\";\nimport { ChatMessage } from \"./chat-message\";\n\ninterface MessagePart {\n\ttype: string;\n\ttext?: string;\n\t[key: string]: unknown;\n}\n\ninterface Message {\n\tid: string;\n\trole: string;\n\tparts: MessagePart[];\n}\n\ninterface ChatMessagesProps {\n\tmessages: Message[];\n\tstatus: string;\n\twelcomeMessage?: string;\n}\n\nexport function ChatMessages(props: ChatMessagesProps) {\n\tconst { messages, status, welcomeMessage } = props;\n\tconst scrollRef = useRef<HTMLDivElement>(null);\n\n\tuseEffect(() => {\n\t\tif (scrollRef.current) {\n\t\t\tscrollRef.current.scrollTop = scrollRef.current.scrollHeight;\n\t\t}\n\t}, []);\n\n\tconst showTyping = status === \"submitted\" || status === \"streaming\";\n\n\treturn (\n\t\t<div\n\t\t\tref={scrollRef}\n\t\t\tclassName=\"ww-scrollbar\"\n\t\t\tstyle={{\n\t\t\t\tflex: 1,\n\t\t\t\toverflowY: \"auto\",\n\t\t\t\tpadding: \"16px\",\n\t\t\t\tdisplay: \"flex\",\n\t\t\t\tflexDirection: \"column\",\n\t\t\t}}\n\t\t>\n\t\t\t{welcomeMessage && messages.length === 0 && (\n\t\t\t\t<div\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tdisplay: \"flex\",\n\t\t\t\t\t\tjustifyContent: \"flex-start\",\n\t\t\t\t\t\tmarginBottom: \"8px\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tmaxWidth: \"85%\",\n\t\t\t\t\t\t\tpadding: \"10px 14px\",\n\t\t\t\t\t\t\tborderRadius:\n\t\t\t\t\t\t\t\t\"var(--ww-msg-radius) var(--ww-msg-radius) var(--ww-msg-radius) 4px\",\n\t\t\t\t\t\t\tbackgroundColor: \"var(--ww-assistant-bubble)\",\n\t\t\t\t\t\t\tcolor: \"var(--ww-text)\",\n\t\t\t\t\t\t\tfontSize: \"14px\",\n\t\t\t\t\t\t\tlineHeight: 1.5,\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{welcomeMessage}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{messages.map((message) => (\n\t\t\t\t<ChatMessage key={message.id} message={message} />\n\t\t\t))}\n\n\t\t\t{showTyping && (\n\t\t\t\t<div\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tdisplay: \"flex\",\n\t\t\t\t\t\tjustifyContent: \"flex-start\",\n\t\t\t\t\t\tmarginBottom: \"8px\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tpadding: \"10px 14px\",\n\t\t\t\t\t\t\tborderRadius:\n\t\t\t\t\t\t\t\t\"var(--ww-msg-radius) var(--ww-msg-radius) var(--ww-msg-radius) 4px\",\n\t\t\t\t\t\t\tbackgroundColor: \"var(--ww-assistant-bubble)\",\n\t\t\t\t\t\t\tdisplay: \"flex\",\n\t\t\t\t\t\t\tgap: \"4px\",\n\t\t\t\t\t\t\talignItems: \"center\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<TypingDot delay=\"0s\" />\n\t\t\t\t\t\t<TypingDot delay=\"0.15s\" />\n\t\t\t\t\t\t<TypingDot delay=\"0.3s\" />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction TypingDot(props: { delay: string }) {\n\treturn (\n\t\t<span\n\t\t\tstyle={{\n\t\t\t\twidth: \"6px\",\n\t\t\t\theight: \"6px\",\n\t\t\t\tborderRadius: \"50%\",\n\t\t\t\tbackgroundColor: \"var(--ww-muted)\",\n\t\t\t\tanimation: `ww-bounce 1.2s ease-in-out infinite`,\n\t\t\t\tanimationDelay: props.delay,\n\t\t\t}}\n\t\t/>\n\t);\n}\n","/**\n * Minimal markdown renderer. Handles bold, italic, inline code, code blocks,\n * links, and unordered lists. No external dependencies.\n */\nexport function ChatMarkdown(props: { text: string }) {\n\tconst blocks = props.text.split(/\\n\\n+/);\n\n\treturn (\n\t\t<>\n\t\t\t{blocks.map((block, i) => (\n\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: static parsed text blocks\n\t\t\t\t<MarkdownBlock key={i} block={block.trim()} />\n\t\t\t))}\n\t\t</>\n\t);\n}\n\nfunction MarkdownBlock(props: { block: string }) {\n\tconst { block } = props;\n\n\t// Code block\n\tif (block.startsWith(\"```\")) {\n\t\tconst lines = block.split(\"\\n\");\n\t\tconst code = lines.slice(1, -1).join(\"\\n\");\n\t\treturn (\n\t\t\t<pre\n\t\t\t\tstyle={{\n\t\t\t\t\tbackgroundColor: \"var(--ww-border)\",\n\t\t\t\t\tborderRadius: \"6px\",\n\t\t\t\t\tpadding: \"8px 12px\",\n\t\t\t\t\toverflowX: \"auto\",\n\t\t\t\t\tfontSize: \"13px\",\n\t\t\t\t\tlineHeight: 1.5,\n\t\t\t\t\tmargin: \"4px 0\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<code>{code}</code>\n\t\t\t</pre>\n\t\t);\n\t}\n\n\t// Unordered list\n\tif (/^[-*]\\s/.test(block)) {\n\t\tconst items = block.split(/\\n/).filter((l) => /^[-*]\\s/.test(l));\n\t\treturn (\n\t\t\t<ul style={{ paddingLeft: \"20px\", margin: \"4px 0\" }}>\n\t\t\t\t{items.map((item, i) => (\n\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: static list items\n\t\t\t\t\t<li key={i}>\n\t\t\t\t\t\t<InlineMarkdown text={item.replace(/^[-*]\\s/, \"\")} />\n\t\t\t\t\t</li>\n\t\t\t\t))}\n\t\t\t</ul>\n\t\t);\n\t}\n\n\t// Paragraph\n\treturn (\n\t\t<p style={{ margin: \"4px 0\", lineHeight: 1.5 }}>\n\t\t\t<InlineMarkdown text={block} />\n\t\t</p>\n\t);\n}\n\nfunction InlineMarkdown(props: { text: string }) {\n\tconst parts = parseInline(props.text);\n\treturn (\n\t\t<>\n\t\t\t{parts.map((part, i) => (\n\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: static inline parsed parts\n\t\t\t\t<InlinePart key={i} part={part} />\n\t\t\t))}\n\t\t</>\n\t);\n}\n\nfunction InlinePart(props: { part: InlinePartData }) {\n\tconst { part } = props;\n\tif (part.type === \"text\") return <span>{part.value}</span>;\n\tif (part.type === \"bold\")\n\t\treturn <strong style={{ fontWeight: 600 }}>{part.value}</strong>;\n\tif (part.type === \"italic\") return <em>{part.value}</em>;\n\tif (part.type === \"code\")\n\t\treturn (\n\t\t\t<code\n\t\t\t\tstyle={{\n\t\t\t\t\tbackgroundColor: \"var(--ww-border)\",\n\t\t\t\t\tborderRadius: \"3px\",\n\t\t\t\t\tpadding: \"1px 4px\",\n\t\t\t\t\tfontSize: \"0.9em\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{part.value}\n\t\t\t</code>\n\t\t);\n\tif (part.type === \"link\")\n\t\treturn (\n\t\t\t<a\n\t\t\t\thref={part.href}\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\tstyle={{\n\t\t\t\t\tcolor: \"var(--ww-primary)\",\n\t\t\t\t\ttextDecoration: \"underline\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{part.value}\n\t\t\t</a>\n\t\t);\n\treturn null;\n}\n\ntype InlinePartData =\n\t| { type: \"text\"; value: string }\n\t| { type: \"bold\"; value: string }\n\t| { type: \"italic\"; value: string }\n\t| { type: \"code\"; value: string }\n\t| { type: \"link\"; value: string; href: string };\n\nfunction parseInline(text: string): InlinePartData[] {\n\tconst parts: InlinePartData[] = [];\n\t// Match: `code`, **bold**, *italic*, [text](url)\n\tconst regex =\n\t\t/`([^`]+)`|\\*\\*([^*]+)\\*\\*|\\*([^*]+)\\*|\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n\tlet lastIndex = 0;\n\tlet match: RegExpExecArray | null;\n\n\tmatch = regex.exec(text);\n\twhile (match !== null) {\n\t\tif (match.index > lastIndex) {\n\t\t\tparts.push({ type: \"text\", value: text.slice(lastIndex, match.index) });\n\t\t}\n\n\t\tif (match[1] != null) {\n\t\t\tparts.push({ type: \"code\", value: match[1] });\n\t\t} else if (match[2] != null) {\n\t\t\tparts.push({ type: \"bold\", value: match[2] });\n\t\t} else if (match[3] != null) {\n\t\t\tparts.push({ type: \"italic\", value: match[3] });\n\t\t} else if (match[4] != null && match[5] != null) {\n\t\t\tparts.push({ type: \"link\", value: match[4], href: match[5] });\n\t\t}\n\n\t\tlastIndex = match.index + match[0].length;\n\t\tmatch = regex.exec(text);\n\t}\n\n\tif (lastIndex < text.length) {\n\t\tparts.push({ type: \"text\", value: text.slice(lastIndex) });\n\t}\n\n\treturn parts;\n}\n","import { ToolIcon } from \"../icons\";\nimport { ChatMarkdown } from \"./chat-markdown\";\n\ninterface MessagePart {\n\ttype: string;\n\ttext?: string;\n\ttoolName?: string;\n\tstate?: string;\n\t[key: string]: unknown;\n}\n\ninterface Message {\n\tid: string;\n\trole: string;\n\tparts: MessagePart[];\n}\n\nexport function ChatMessage(props: { message: Message }) {\n\tconst { message } = props;\n\tconst isUser = message.role === \"user\";\n\n\treturn (\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\tdisplay: \"flex\",\n\t\t\t\tjustifyContent: isUser ? \"flex-end\" : \"flex-start\",\n\t\t\t\tmarginBottom: \"8px\",\n\t\t\t\tanimation: \"ww-fade-in 0.2s ease-out\",\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tmaxWidth: \"85%\",\n\t\t\t\t\tpadding: \"10px 14px\",\n\t\t\t\t\tborderRadius: isUser\n\t\t\t\t\t\t? \"var(--ww-msg-radius) var(--ww-msg-radius) 4px var(--ww-msg-radius)\"\n\t\t\t\t\t\t: \"var(--ww-msg-radius) var(--ww-msg-radius) var(--ww-msg-radius) 4px\",\n\t\t\t\t\tbackgroundColor: isUser\n\t\t\t\t\t\t? \"var(--ww-user-bubble)\"\n\t\t\t\t\t\t: \"var(--ww-assistant-bubble)\",\n\t\t\t\t\tcolor: isUser ? \"var(--ww-primary-fg)\" : \"var(--ww-text)\",\n\t\t\t\t\tfontSize: \"14px\",\n\t\t\t\t\tlineHeight: 1.5,\n\t\t\t\t\twordBreak: \"break-word\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{message.parts.map((part, i) => (\n\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: message parts are append-only\n\t\t\t\t\t<MessagePartRenderer key={i} part={part} isUser={isUser} />\n\t\t\t\t))}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction MessagePartRenderer(props: { part: MessagePart; isUser: boolean }) {\n\tconst { part, isUser } = props;\n\n\tif (part.type === \"text\" && part.text) {\n\t\treturn isUser ? (\n\t\t\t<span>{part.text}</span>\n\t\t) : (\n\t\t\t<ChatMarkdown text={part.text} />\n\t\t);\n\t}\n\n\tif (part.type.startsWith(\"tool-\") || part.type === \"tool-invocation\") {\n\t\tconst toolName = part.toolName ?? part.type.replace(\"tool-\", \"\");\n\t\tconst isLoading =\n\t\t\tpart.state === \"input-streaming\" || part.state === \"input-available\";\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tdisplay: \"flex\",\n\t\t\t\t\talignItems: \"center\",\n\t\t\t\t\tgap: \"6px\",\n\t\t\t\t\tpadding: \"4px 0\",\n\t\t\t\t\tfontSize: \"12px\",\n\t\t\t\t\tcolor: \"var(--ww-muted)\",\n\t\t\t\t\tfontStyle: \"italic\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<ToolIcon size={12} />\n\t\t\t\t<span>{isLoading ? `Using ${toolName}...` : `Used ${toolName}`}</span>\n\t\t\t\t{isLoading && (\n\t\t\t\t\t<span style={{ animation: \"ww-pulse 1.5s ease-in-out infinite\" }}>\n\t\t\t\t\t\t...\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn null;\n}\n","import { ChatHeader } from \"./chat-header\";\nimport { ChatInput } from \"./chat-input\";\nimport { ChatMessages } from \"./chat-messages\";\n\ninterface MessagePart {\n\ttype: string;\n\ttext?: string;\n\t[key: string]: unknown;\n}\n\ninterface Message {\n\tid: string;\n\trole: string;\n\tparts: MessagePart[];\n}\n\ninterface ChatPanelProps {\n\tmessages: Message[];\n\tinput: string;\n\tonInputChange: (value: string) => void;\n\tonSend: () => void;\n\tstatus: string;\n\ttitle: string;\n\tsubtitle?: string;\n\twelcomeMessage?: string;\n\twidth: number;\n\theight: number;\n}\n\nexport function ChatPanel(props: ChatPanelProps) {\n\tconst {\n\t\tmessages,\n\t\tinput,\n\t\tonInputChange,\n\t\tonSend,\n\t\tstatus,\n\t\ttitle,\n\t\tsubtitle,\n\t\twelcomeMessage,\n\t\twidth,\n\t\theight,\n\t} = props;\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"ww-panel\"\n\t\t\tstyle={{\n\t\t\t\twidth: `${width}px`,\n\t\t\t\theight: `${height}px`,\n\t\t\t\tdisplay: \"flex\",\n\t\t\t\tflexDirection: \"column\",\n\t\t\t\tbackgroundColor: \"var(--ww-bg)\",\n\t\t\t\tborderRadius: \"var(--ww-radius)\",\n\t\t\t\tborder: \"1px solid var(--ww-border)\",\n\t\t\t\tboxShadow:\n\t\t\t\t\t\"0 20px 60px -12px rgba(0, 0, 0, 0.15), 0 8px 20px -8px rgba(0, 0, 0, 0.1)\",\n\t\t\t\toverflow: \"hidden\",\n\t\t\t\tfontFamily: \"var(--ww-font)\",\n\t\t\t}}\n\t\t>\n\t\t\t<ChatHeader title={title} subtitle={subtitle} />\n\t\t\t<ChatMessages\n\t\t\t\tmessages={messages}\n\t\t\t\tstatus={status}\n\t\t\t\twelcomeMessage={welcomeMessage}\n\t\t\t/>\n\t\t\t<ChatInput\n\t\t\t\tvalue={input}\n\t\t\t\tonChange={onInputChange}\n\t\t\t\tonSend={onSend}\n\t\t\t\tdisabled={status !== \"ready\"}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n"],"mappings":"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","names":["useChat","DefaultChatTransport","useCallback","useEffect","useRef","useState","useCallback","useRef","useChatTracking","config","configRef","trackEvent","event","properties","apiKey","baseUrl","trackChatOpened","trackMessageSent","trackResponseReceived","buildStyleSheet","DEFAULT_THEME","CSS_VAR_MAP","mergeTheme","userTheme","themeToCSSProperties","theme","vars","key","cssVar","value","jsx","jsxs","ChatHeader","props","title","subtitle","useCallback","useEffect","useRef","jsx","jsxs","iconProps","SendIcon","props","size","ToolIcon","jsx","jsxs","ChatInput","props","value","onChange","onSend","disabled","textareaRef","useRef","handleKeyDown","useCallback","e","useEffect","textarea","SendIcon","useEffect","useRef","Fragment","jsx","ChatMarkdown","props","blocks","block","i","MarkdownBlock","code","items","l","item","InlineMarkdown","parts","parseInline","part","InlinePart","text","regex","lastIndex","match","jsx","jsxs","ChatMessage","props","message","isUser","part","i","MessagePartRenderer","ChatMarkdown","toolName","isLoading","ToolIcon","jsx","jsxs","ChatMessages","props","messages","status","welcomeMessage","scrollRef","useRef","useEffect","showTyping","message","ChatMessage","TypingDot","jsx","jsxs","ChatPanel","props","messages","input","onInputChange","onSend","status","title","subtitle","welcomeMessage","width","height","ChatHeader","ChatMessages","ChatInput","jsx","DEFAULT_API","ChatWidget","props","apiKey","api","welcomeMessage","title","subtitle","userTheme","userHeaders","body","width","height","onMessageSent","onResponseReceived","_shadowRoot","resolvedTheme","mergeTheme","cssVars","themeToCSSProperties","trackChatOpened","trackMessageSent","trackResponseReceived","useChatTracking","transportRef","useRef","DefaultChatTransport","messages","sendMessage","status","useChat","input","setInput","useState","handleSend","useCallback","styleInjected","useEffect","style","buildStyleSheet","tracked","ChatPanel"]}
|
|
1
|
+
{"version":3,"sources":["../../src/chat/layouts/chat-bar.tsx","../../src/chat/ai-elements/conversation.tsx","../../src/chat/lib/utils.ts","../../src/chat/ui/button.tsx","../../src/chat/ai-elements/prompt-input.tsx","../../src/chat/ai-elements/attachments.tsx","../../src/chat/ai-elements/loader.tsx","../../src/chat/ai-elements/message.tsx","../../src/chat/ai-elements/reasoning.tsx","../../src/chat/ai-elements/tool.tsx","../../src/chat/components/mcp-app-frame.tsx","../../src/chat/components/message-list.tsx","../../src/chat/hooks/use-chat-engine.ts","../../src/chat/theme.ts","../../src/chat/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 { useChatEngine } from \"../hooks/use-chat-engine\";\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\tresourceEndpoint,\n\t\t} = props;\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\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},\n\t\t\t}),\n\t\t\t[engine.handleSubmit],\n\t\t);\n\n\t\tconst [isFocused, setIsFocused] = useState(false);\n\t\tconst containerRef = useRef<HTMLDivElement>(null);\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={resourceEndpoint}\n\t\t\t\t\t\t\t\tisDark={isDark}\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{/* 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)}\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=\"Ask anything...\"\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 = 300;\nconst PROTOCOL_VERSION = \"2026-01-26\";\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}\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}: McpAppFrameProps) {\n\tconst iframeRef = useRef<HTMLIFrameElement>(null);\n\tconst toolInputRef = useRef(toolInput);\n\tconst toolResultRef = useRef(toolResult);\n\tconst heightSettledRef = useRef(false);\n\tconst [height, setHeight] = useState(DEFAULT_HEIGHT);\n\n\ttoolInputRef.current = toolInput;\n\ttoolResultRef.current = toolResult;\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// 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\n\t\tconst postToIframe = (msg: Record<string, unknown>) => {\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\t// ui/initialize — widget requests handshake\n\t\t\tif (method === \"ui/initialize\" && id != null) {\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\thostContext: {\n\t\t\t\t\t\t\ttheme: isDarkRef.current ? \"dark\" : \"light\",\n\t\t\t\t\t\t\tautoHeight,\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\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 requests resize\n\t\t\tif (method === \"ui/notifications/size-changed\") {\n\t\t\t\tif (heightSettledRef.current) return;\n\t\t\t\tconst h = data.params?.height;\n\t\t\t\tif (typeof h === \"number\" && !disposed) {\n\t\t\t\t\tsetHeight(clampHeight(h));\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\twindow.open(url, \"_blank\", \"noopener,noreferrer\");\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// 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\tdisposed = true;\n\t\t\twindow.removeEventListener(\"message\", handleMessage);\n\t\t};\n\t}, [autoHeight, clampHeight]);\n\n\t// Auto-height: observe the iframe body size via ResizeObserver (same-origin only)\n\tuseEffect(() => {\n\t\tif (!autoHeight) return;\n\n\t\tconst iframe = iframeRef.current;\n\t\tif (!iframe) return;\n\n\t\tlet observer: ResizeObserver | undefined;\n\t\tlet disposed = false;\n\n\t\tconst attach = () => {\n\t\t\tif (disposed) return;\n\t\t\ttry {\n\t\t\t\tconst body = iframe.contentDocument?.body;\n\t\t\t\tif (!body) return;\n\n\t\t\t\tobserver = new ResizeObserver(() => {\n\t\t\t\t\tif (disposed) return;\n\t\t\t\t\tconst style =\n\t\t\t\t\t\tiframe.contentDocument?.defaultView?.getComputedStyle(body);\n\t\t\t\t\tconst marginTop = Number.parseInt(style?.marginTop ?? \"0\", 10) || 0;\n\t\t\t\t\tconst marginBottom =\n\t\t\t\t\t\tNumber.parseInt(style?.marginBottom ?? \"0\", 10) || 0;\n\t\t\t\t\tconst h =\n\t\t\t\t\t\tMath.max(body.scrollHeight, body.offsetHeight) +\n\t\t\t\t\t\tmarginTop +\n\t\t\t\t\t\tmarginBottom;\n\t\t\t\t\tif (h > 0) setHeight(h);\n\t\t\t\t});\n\n\t\t\t\tobserver.observe(body);\n\t\t\t} catch {\n\t\t\t\t// Cross-origin — fall back to postMessage size-changed protocol\n\t\t\t}\n\t\t};\n\n\t\tiframe.addEventListener(\"load\", attach);\n\t\tattach();\n\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\tobserver?.disconnect();\n\t\t\tiframe.removeEventListener(\"load\", attach);\n\t\t};\n\t}, [autoHeight]);\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(\"w-full rounded-md border border-border\", className)}\n\t\t\tstyle={{\n\t\t\t\theight: height || undefined,\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 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\";\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}\n\nexport function MessageList({\n\tmessages,\n\tstatus,\n\twelcomeMessage,\n\tresourceEndpoint,\n\tisDark,\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<McpAppFrame\n\t\t\t\t\t\t\t\t\t\t\tresourceUri={resourceUri}\n\t\t\t\t\t\t\t\t\t\t\ttoolInput={(part.input as Record<string, unknown>) ?? {}}\n\t\t\t\t\t\t\t\t\t\t\ttoolResult={{\n\t\t\t\t\t\t\t\t\t\t\t\tcontent: (output as Record<string, unknown>).content as\n\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| undefined,\n\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.structuredContent as\n\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| undefined,\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tresourceEndpoint={resourceEndpoint}\n\t\t\t\t\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\t\t\t\t\tautoHeight={autoHeight}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</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 { 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});\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","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 { forwardRef, useImperativeHandle } 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 { useChatEngine } from \"../hooks/use-chat-engine\";\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\tresourceEndpoint,\n\t\t} = props;\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\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},\n\t\t\t}),\n\t\t\t[engine.handleSubmit],\n\t\t);\n\n\t\treturn (\n\t\t\t<div\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=\"flex flex-col font-[family-name:var(--ww-font)] text-foreground bg-background rounded-[var(--ww-radius)] border border-border shadow-md overflow-hidden\"\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={resourceEndpoint}\n\t\t\t\t\t\t\tisDark={isDark}\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{/* 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=\"Ask anything...\"\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","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","McpAppFrame","resourceUri","toolInput","toolResult","resourceEndpoint","isDark","className","autoHeight","iframeRef","useRef","toolInputRef","toolResultRef","heightSettledRef","height","setHeight","useState","clampHeight","useCallback","h","iframeSrc","useMemo","isDarkRef","useEffect","iframe","disposed","postToIframe","msg","handleMessage","event","data","method","id","input","result","content","url","observer","attach","body","style","marginTop","marginBottom","cn","Fragment","jsx","jsxs","formatToolName","name","c","MessageList","messages","status","welcomeMessage","resourceEndpoint","isDark","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","McpAppFrame","Attachments","Loader","useChat","DefaultChatTransport","useCallback","useRef","useState","useChatEngine","props","api","userHeaders","body","onMessageSent","onResponseReceived","transportRef","messages","sendMessage","status","text","setText","handleSubmit","message","hasText","hasFiles","handleTextChange","e","isLoading","lastMessage","hasMessages","showLoaderBubble","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","resourceEndpoint","resolvedTheme","mergeTheme","cssVars","themeToCSSProperties","isDark","isDarkTheme","engine","useChatEngine","useImperativeHandle","text","isFocused","setIsFocused","useState","containerRef","useRef","isExpanded","useEffect","handleClickOutside","e","handleFocus","useCallback","cn","Conversation","ConversationContent","MessageList","ConversationScrollButton","PromptInput","PromptInputAddAttachments","PromptInputTextarea","PromptInputSubmit","forwardRef","useImperativeHandle","jsx","jsxs","ChatCard","forwardRef","props","ref","userTheme","title","subtitle","showStatus","width","height","allowAttachments","welcomeMessage","resourceEndpoint","resolvedTheme","mergeTheme","cssVars","themeToCSSProperties","isDark","isDarkTheme","engine","useChatEngine","useImperativeHandle","text","Conversation","ConversationContent","MessageList","ConversationScrollButton","PromptInput","cn","PromptInputAddAttachments","PromptInputTextarea","PromptInputSubmit"]}
|