@inappai/react 1.1.0 → 1.2.0

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/index.d.mts CHANGED
@@ -37,6 +37,11 @@ interface Tool {
37
37
  parameters: any;
38
38
  handler: (params: any) => Promise<any> | any;
39
39
  }
40
+ interface ToolAction {
41
+ tool: string;
42
+ args: Record<string, any>;
43
+ result: any;
44
+ }
40
45
  interface Message {
41
46
  id: string;
42
47
  role: 'user' | 'assistant' | 'system';
@@ -47,6 +52,7 @@ interface Message {
47
52
  completionTokens: number;
48
53
  totalTokens: number;
49
54
  };
55
+ toolActions?: ToolAction[];
50
56
  }
51
57
  interface InAppAIProps {
52
58
  endpoint?: string;
@@ -258,4 +264,4 @@ declare function ToolRegistryProvider({ children, initialTools, }: ToolRegistryP
258
264
  */
259
265
  declare function useToolRegistry(): ToolRegistry;
260
266
 
261
- export { type CustomStyles, InAppAI, type InAppAIProps, type Message, type Tool, type ToolRegistry, ToolRegistryProvider, type ToolRegistryProviderProps, type UseToolsOptions, type UseToolsReturn, useToolRegistry, useTools };
267
+ export { type CustomStyles, InAppAI, type InAppAIProps, type Message, type Tool, type ToolAction, type ToolRegistry, ToolRegistryProvider, type ToolRegistryProviderProps, type UseToolsOptions, type UseToolsReturn, useToolRegistry, useTools };
package/dist/index.d.ts CHANGED
@@ -37,6 +37,11 @@ interface Tool {
37
37
  parameters: any;
38
38
  handler: (params: any) => Promise<any> | any;
39
39
  }
40
+ interface ToolAction {
41
+ tool: string;
42
+ args: Record<string, any>;
43
+ result: any;
44
+ }
40
45
  interface Message {
41
46
  id: string;
42
47
  role: 'user' | 'assistant' | 'system';
@@ -47,6 +52,7 @@ interface Message {
47
52
  completionTokens: number;
48
53
  totalTokens: number;
49
54
  };
55
+ toolActions?: ToolAction[];
50
56
  }
51
57
  interface InAppAIProps {
52
58
  endpoint?: string;
@@ -258,4 +264,4 @@ declare function ToolRegistryProvider({ children, initialTools, }: ToolRegistryP
258
264
  */
259
265
  declare function useToolRegistry(): ToolRegistry;
260
266
 
261
- export { type CustomStyles, InAppAI, type InAppAIProps, type Message, type Tool, type ToolRegistry, ToolRegistryProvider, type ToolRegistryProviderProps, type UseToolsOptions, type UseToolsReturn, useToolRegistry, useTools };
267
+ export { type CustomStyles, InAppAI, type InAppAIProps, type Message, type Tool, type ToolAction, type ToolRegistry, ToolRegistryProvider, type ToolRegistryProviderProps, type UseToolsOptions, type UseToolsReturn, useToolRegistry, useTools };
package/dist/index.js CHANGED
@@ -1,8 +1,13 @@
1
- "use strict";var ja=Object.create;var J=Object.defineProperty;var Ka=Object.getOwnPropertyDescriptor;var Va=Object.getOwnPropertyNames;var Xa=Object.getPrototypeOf,_a=Object.prototype.hasOwnProperty;var Ja=(t,o)=>{for(var p in o)J(t,p,{get:o[p],enumerable:!0})},Ra=(t,o,p,n)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of Va(o))!_a.call(t,s)&&s!==p&&J(t,s,{get:()=>o[s],enumerable:!(n=Ka(o,s))||n.enumerable});return t};var qa=(t,o,p)=>(p=t!=null?ja(Xa(t)):{},Ra(o||!t||!t.__esModule?J(p,"default",{value:t,enumerable:!0}):p,t)),Ga=t=>Ra(J({},"__esModule",{value:!0}),t);var Za={};Ja(Za,{InAppAI:()=>Ba,ToolRegistryProvider:()=>da,useToolRegistry:()=>la,useTools:()=>sa});module.exports=Ga(Za);var l=require("react"),pa=qa(require("react-markdown")),Aa=require("react-syntax-highlighter"),za=require("react-syntax-highlighter/dist/esm/styles/prism");var a=require("react/jsx-runtime"),Pa={};function Ea(){return(0,a.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"8px",padding:"12px 16px",background:"var(--inapp-ai-assistant-bg)",borderRadius:"var(--inapp-ai-border-radius)"},children:[(0,a.jsx)("div",{style:{height:"14px",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",borderRadius:"4px"}}),(0,a.jsx)("div",{style:{height:"14px",width:"90%",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",animationDelay:"0.1s",borderRadius:"4px"}}),(0,a.jsx)("div",{style:{height:"14px",width:"75%",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",animationDelay:"0.2s",borderRadius:"4px"}}),(0,a.jsx)("style",{children:`
1
+ "use strict";var Va=Object.create;var Z=Object.defineProperty;var Xa=Object.getOwnPropertyDescriptor;var _a=Object.getOwnPropertyNames;var qa=Object.getPrototypeOf,Ga=Object.prototype.hasOwnProperty;var Za=(t,o)=>{for(var p in o)Z(t,p,{get:o[p],enumerable:!0})},Aa=(t,o,p,n)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of _a(o))!Ga.call(t,s)&&s!==p&&Z(t,s,{get:()=>o[s],enumerable:!(n=Xa(o,s))||n.enumerable});return t};var Qa=(t,o,p)=>(p=t!=null?Va(qa(t)):{},Aa(o||!t||!t.__esModule?Z(p,"default",{value:t,enumerable:!0}):p,t)),ae=t=>Aa(Z({},"__esModule",{value:!0}),t);var ee={};Za(ee,{InAppAI:()=>Oa,ToolRegistryProvider:()=>ca,useToolRegistry:()=>ga,useTools:()=>la});module.exports=ae(ee);var l=require("react"),da=Qa(require("react-markdown")),$a=require("react-syntax-highlighter"),Da=require("react-syntax-highlighter/dist/esm/styles/prism");var a=require("react/jsx-runtime"),Ba={};function Ma(){return(0,a.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"8px",padding:"12px 16px",background:"var(--inapp-ai-assistant-bg)",borderRadius:"var(--inapp-ai-border-radius)"},children:[(0,a.jsx)("div",{style:{height:"14px",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",borderRadius:"4px"}}),(0,a.jsx)("div",{style:{height:"14px",width:"90%",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",animationDelay:"0.1s",borderRadius:"4px"}}),(0,a.jsx)("div",{style:{height:"14px",width:"75%",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",animationDelay:"0.2s",borderRadius:"4px"}}),(0,a.jsx)("style",{children:`
2
2
  @keyframes shimmer {
3
3
  0% { background-position: 200% 0; }
4
4
  100% { background-position: -200% 0; }
5
5
  }
6
- `})]})}function Ia({error:t,onDismiss:o}){let n=(s=>s.includes("not responding")||s.includes("connection")||s.includes("network")?{type:"connection",icon:"\u{1F50C}",title:"Connection Error"}:s.includes("timeout")?{type:"timeout",icon:"\u23F1\uFE0F",title:"Request Timeout"}:s.includes("rate limit")?{type:"rateLimit",icon:"\u{1F6A6}",title:"Rate Limit"}:s.includes("authentication")||s.includes("unauthorized")?{type:"auth",icon:"\u{1F512}",title:"Authentication Error"}:{type:"generic",icon:"\u26A0\uFE0F",title:"Error"})(t);return(0,a.jsxs)("div",{className:"inapp-ai-error-banner",role:"alert","aria-live":"assertive",style:{display:"flex",alignItems:"flex-start",gap:"12px",padding:"14px 16px",background:"linear-gradient(135deg, #fff3cd 0%, #ffe9a6 100%)",borderLeft:"4px solid #ff9800",borderRadius:"0"},children:[(0,a.jsx)("span",{style:{fontSize:"20px"},children:n.icon}),(0,a.jsxs)("div",{style:{flex:1,minWidth:0},children:[(0,a.jsx)("div",{style:{fontWeight:600,marginBottom:"4px",color:"#856404"},children:n.title}),(0,a.jsx)("div",{style:{fontSize:"13px",color:"#856404",wordBreak:"break-word"},children:t}),n.type==="connection"&&(0,a.jsx)("div",{style:{fontSize:"12px",marginTop:"6px",color:"#997404"},children:"\u{1F4A1} Make sure the backend server is running on the correct port"})]}),(0,a.jsx)("button",{onClick:o,style:{background:"none",border:"none",color:"#856404",cursor:"pointer",padding:"4px 8px",fontSize:"16px",lineHeight:1},"aria-label":"Dismiss error",children:"\u2715"})]})}function Ma({inline:t,className:o,children:p,...n}){let[s,h]=(0,l.useState)(!1),y=/language-(\w+)/.exec(o||""),e=y?y[1]:"",k=String(p).replace(/\n$/,""),C=()=>{navigator.clipboard.writeText(k),h(!0),setTimeout(()=>h(!1),2e3)};return t?(0,a.jsx)("code",{className:o,...n,children:p}):(0,a.jsxs)("div",{style:{position:"relative",marginTop:"8px",marginBottom:"8px"},children:[(0,a.jsx)("button",{onClick:C,style:{position:"absolute",top:"8px",right:"8px",padding:"4px 8px",background:s?"#28a745":"rgba(255, 255, 255, 0.1)",color:"white",border:"none",borderRadius:"4px",cursor:"pointer",fontSize:"12px",zIndex:1,transition:"background 0.2s"},"aria-label":"Copy code",children:s?"\u2713 Copied!":"\u{1F4CB} Copy"}),(0,a.jsx)(Aa.Prism,{language:e||"text",style:za.vscDarkPlus,customStyle:{borderRadius:"8px",padding:"16px",fontSize:"13px",marginTop:0,marginBottom:0},...n,children:k})]})}function Ba({endpoint:t,agentId:o,position:p="bottom-right",displayMode:n="popup",defaultFolded:s=!1,theme:h="light",context:y,customStyles:e={},tools:k=[],panelMinWidth:C="20%",panelMaxWidth:r="33.33%",panelDefaultWidth:d="25%",onPanelResize:c,conversationId:M,messages:x,onMessagesChange:T,showHeader:ca=!0,authToken:W,maxToolRounds:ga=10}){if(x===void 0||T===void 0)throw new Error("InAppAI requires controlled mode. Please provide both `messages` and `onMessagesChange` props. See documentation for usage examples.");let Y=t||typeof process<"u"&&process.env?.REACT_APP_INAPPAI_ENDPOINT||typeof Pa.env<"u"&&Pa.env?.VITE_INAPPAI_ENDPOINT||"https://api.inappai.com/api",fa=()=>W&&(typeof W=="function"?W():W)||null,ua=()=>{let i={"Content-Type":"application/json"},v=fa();return v&&(i.Authorization=`Bearer ${v}`),i},q=(0,l.useRef)(x);(0,l.useEffect)(()=>{q.current=x},[x]);let N=x,G=i=>{let v=q.current,f=typeof i=="function"?i(v):i;q.current=f,T(f)},[$,Z]=(0,l.useState)(n.startsWith("sidebar")||n.startsWith("panel")||n==="embedded"),[A,Oa]=(0,l.useState)(s&&(n.startsWith("sidebar")||n.startsWith("panel"))),[D,Q]=(0,l.useState)(""),[u,ba]=(0,l.useState)(!1),[R,ma]=(0,l.useState)(!1),[j,z]=(0,l.useState)(null),[La,Sa]=(0,l.useState)(d),[ha,xa]=(0,l.useState)(!1),aa=(0,l.useRef)(null),Ua=(0,l.useRef)(`react-demo-${Date.now()}`),va=M||Ua.current,ea=(0,l.useRef)(null),ia=(0,l.useRef)(null),E=n==="embedded",g=n.startsWith("sidebar"),b=n.startsWith("panel"),Fa=n==="sidebar-left",K=n==="panel-left";(0,l.useEffect)(()=>{(g||b||E)&&Z(!0)},[g,b,E]),(0,l.useEffect)(()=>{if(!b||!ha)return;let i=f=>{let P=ea.current?.parentElement;if(!P)return;let O=P.getBoundingClientRect(),L;K?L=f.clientX:L=O.width-f.clientX;let I=L/O.width*100,S=parseFloat(C),ta=parseFloat(r),F=`${Math.max(S,Math.min(ta,I))}%`;Sa(F),c&&c(L)},v=()=>{xa(!1)};return document.addEventListener("mousemove",i),document.addEventListener("mouseup",v),()=>{document.removeEventListener("mousemove",i),document.removeEventListener("mouseup",v)}},[ha,b,K,C,r,c]),(0,l.useEffect)(()=>{(async()=>{try{let v={},f=fa();f&&(v.Authorization=`Bearer ${f}`),(await fetch(`${Y}/${o}/health`,{headers:v})).ok?(ma(!0),z(null)):z("Backend not responding")}catch{z("Failed to connect to backend"),ma(!1)}})()},[Y,o]),(0,l.useEffect)(()=>{aa.current?.scrollIntoView({behavior:"smooth"})},[N]),(0,l.useEffect)(()=>{!u&&R&&setTimeout(()=>{ia.current?.focus()},100)},[u,R]);let na=async()=>{let i=D.trim();if(!i||u)return;let v={id:`${Date.now()}-user`,role:"user",content:i,timestamp:new Date};G(f=>[...f,v]),Q(""),ba(!0),z(null);try{let f=()=>typeof y=="function"?y():y,P=k.map(({name:U,description:F,parameters:oa})=>({type:"function",function:{name:U,description:F,parameters:oa}})),O=await fetch(`${Y}/${o}/chat`,{method:"POST",headers:ua(),body:JSON.stringify({message:i,conversationId:va,context:f(),tools:P.length>0?P:void 0,disableCache:!1})});if(!O.ok)throw new Error("Failed to get response");let I=await O.json(),S=0;for(;I.toolCalls&&I.toolCalls.length>0&&S<ga;){S++;let F=(await Promise.all(I.toolCalls.map(async B=>{let V=B.function?.name||B.name,X=B.function?.arguments?JSON.parse(B.function.arguments):B.parameters,_=k.find(H=>H.name===V);if(!_)return{success:!1,error:`Tool '${V}' not found`};try{return await Promise.resolve(_.handler(X))}catch(H){return console.error(`Tool '${_.name}' failed:`,H),{success:!1,error:H.message}}}))).map((B,V)=>{let X=I.toolCalls[V];return`Tool "${X.function?.name||X.name}" result: ${JSON.stringify(B)}`}).join(`
7
- `),oa=S>=ga,Na=await fetch(`${Y}/${o}/chat`,{method:"POST",headers:ua(),body:JSON.stringify({message:F,conversationId:va,context:f(),tools:oa?void 0:P.length>0?P:void 0,disableCache:!1})});if(!Na.ok)throw new Error("Failed to get AI response for tool results");I=await Na.json()}let ta={id:`${Date.now()}-assistant`,role:"assistant",content:I.message||"I executed the tools successfully.",timestamp:new Date,usage:I.usage};G(U=>[...U,ta])}catch(f){console.error("\u274C Error:",f),z(f instanceof Error?f.message:"Unknown error")}finally{ba(!1)}},ya=i=>{i.key==="Enter"&&!i.shiftKey&&(i.preventDefault(),na())},wa=()=>{G([])},ka=()=>{Oa(!A)},Ta=`inapp-ai-${p}`,ra=h&&h!=="light"?`inapp-ai-theme-${h}`:"",Ha=E?"inapp-ai-embedded":g?`inapp-ai-sidebar inapp-ai-${n}`:b?`inapp-ai-panel inapp-ai-${n}`:"inapp-ai-popup",Wa=A?"inapp-ai-folded":"",Ya={...e.buttonBackgroundColor&&{background:e.buttonBackgroundColor},...e.buttonTextColor&&{color:e.buttonTextColor},...e.buttonSize&&{width:e.buttonSize,height:e.buttonSize,fontSize:`calc(${e.buttonSize} * 0.5)`},...e.buttonBorderRadius&&{borderRadius:e.buttonBorderRadius},...e.boxShadow&&{boxShadow:e.boxShadow}},Ca={...e.windowWidth&&!g&&!b&&{width:e.windowWidth},...e.windowHeight&&!g&&!b&&{height:e.windowHeight},...e.windowBorderRadius&&{borderRadius:e.windowBorderRadius},...e.fontFamily&&{fontFamily:e.fontFamily},...e.fontSize&&{fontSize:e.fontSize},...e.sidebarWidth&&g&&!A&&{width:e.sidebarWidth},...e.sidebarFoldedWidth&&g&&A&&{width:e.sidebarFoldedWidth},...b&&{width:La}};return E&&$?(0,a.jsxs)("div",{ref:ea,role:"region","aria-label":"AI Assistant Chat",className:`inapp-ai-window inapp-ai-embedded ${ra}`,style:Ca,children:[ca&&(0,a.jsx)("div",{className:"inapp-ai-header",style:{...e.headerBackground&&{background:e.headerBackground},...e.headerTextColor&&{color:e.headerTextColor}},children:(0,a.jsxs)("div",{className:"inapp-ai-header-title",children:[(0,a.jsx)("span",{className:"inapp-ai-header-icon",children:e.buttonIcon||"\u{1F916}"}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{children:e.headerTitle||"AI Assistant"}),(0,a.jsx)("p",{children:R?(0,a.jsxs)("span",{className:"inapp-ai-status-connected",children:[(0,a.jsx)("span",{className:"inapp-ai-status-dot"}),"Connected"]}):(0,a.jsxs)("span",{className:"inapp-ai-status-disconnected",children:[(0,a.jsx)("span",{className:"inapp-ai-status-dot"}),"Disconnected"]})})]})]})}),j&&(0,a.jsx)(Ia,{error:j,onDismiss:()=>z(null)}),(0,a.jsxs)("div",{className:"inapp-ai-messages",role:"log","aria-live":"polite","aria-label":"Chat messages",children:[N.length===0?(0,a.jsxs)("div",{className:"inapp-ai-empty-state",role:"status",children:[(0,a.jsx)("div",{className:"inapp-ai-empty-icon","aria-hidden":"true",children:"\u{1F4AC}"}),(0,a.jsx)("h4",{children:"Start a conversation"}),(0,a.jsx)("p",{children:"How can I help you today?"})]}):N.map(i=>(0,a.jsxs)("div",{className:`inapp-ai-message inapp-ai-message-${i.role}`,role:"article","aria-label":`${i.role==="user"?"User":"Assistant"} message`,children:[(0,a.jsx)("div",{className:"inapp-ai-message-icon","aria-hidden":"true",children:i.role==="user"?"\u{1F464}":"\u{1F916}"}),(0,a.jsxs)("div",{className:"inapp-ai-message-content",children:[(0,a.jsx)("div",{className:"inapp-ai-message-text",style:{...i.role==="user"&&e.userMessageBackground&&{background:e.userMessageBackground},...i.role==="user"&&e.userMessageColor&&{color:e.userMessageColor},...i.role==="assistant"&&e.assistantMessageBackground&&{background:e.assistantMessageBackground},...i.role==="assistant"&&e.assistantMessageColor&&{color:e.assistantMessageColor},...e.borderRadius&&{borderRadius:e.borderRadius}},children:i.role==="assistant"?(0,a.jsx)(pa.default,{components:{code:Ma},children:i.content}):i.content}),(0,a.jsxs)("div",{className:"inapp-ai-message-time",children:[i.timestamp?new Date(i.timestamp).toLocaleTimeString():"",i.usage&&(0,a.jsxs)("span",{className:"inapp-ai-message-tokens",children:[" \u2022 ",i.usage.totalTokens," tokens"]})]})]})]},i.id)),u&&(0,a.jsxs)("div",{className:"inapp-ai-message inapp-ai-message-assistant",children:[(0,a.jsx)("div",{className:"inapp-ai-message-icon",children:(0,a.jsx)("div",{style:{animation:"pulse 2s infinite"},children:"\u{1F916}"})}),(0,a.jsx)("div",{className:"inapp-ai-message-content",children:(0,a.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[(0,a.jsxs)("div",{className:"inapp-ai-typing-indicator",children:[(0,a.jsx)("span",{}),(0,a.jsx)("span",{}),(0,a.jsx)("span",{})]}),(0,a.jsx)(Ea,{})]})})]}),(0,a.jsx)("div",{ref:aa})]}),(0,a.jsxs)("div",{className:"inapp-ai-input-area",role:"form","aria-label":"Message input",children:[N.length>0&&(0,a.jsxs)("button",{className:"inapp-ai-clear-btn",onClick:wa,disabled:u,"aria-label":"Clear conversation history",tabIndex:0,children:[(0,a.jsx)("span",{"aria-hidden":"true",children:"\u{1F5D1}\uFE0F"})," Clear"]}),(0,a.jsxs)("div",{className:"inapp-ai-input-wrapper",children:[(0,a.jsx)("input",{ref:ia,type:"text",className:"inapp-ai-input",placeholder:e.inputPlaceholder||"Type your message...",value:D,onChange:i=>Q(i.target.value),onKeyPress:ya,disabled:u||!R,"aria-label":"Message input","aria-describedby":"send-hint",tabIndex:0,style:{...e.inputBackground&&{background:e.inputBackground},...e.inputBorderColor&&{borderColor:e.inputBorderColor},...e.inputTextColor&&{color:e.inputTextColor}}}),(0,a.jsxs)("button",{className:"inapp-ai-send-btn",onClick:na,disabled:u||!R||!D.trim(),"aria-label":"Send message",tabIndex:0,children:[(0,a.jsx)("span",{"aria-hidden":"true",children:u?"\u23F3":"\u2B06"}),(0,a.jsx)("span",{className:"sr-only",children:u?"Sending...":"Send message"})]}),(0,a.jsx)("span",{id:"send-hint",className:"sr-only",children:"Press Enter to send"})]})]})]}):(0,a.jsxs)(a.Fragment,{children:[!g&&!b&&!E&&(0,a.jsxs)("button",{className:`inapp-ai-button ${Ta} ${ra}`,style:Ya,onClick:()=>Z(!$),"aria-label":$?"Close AI Assistant":"Open AI Assistant","aria-expanded":$,"aria-haspopup":"dialog",tabIndex:0,children:[$?"\u2715":e.buttonIcon||"\u{1F916}",!R&&(0,a.jsx)("span",{className:"inapp-ai-offline-indicator",role:"status","aria-label":"Backend disconnected"})]}),$&&(0,a.jsxs)("div",{ref:ea,role:E?"region":"dialog","aria-label":"AI Assistant Chat","aria-modal":!g&&!b&&!E?"true":void 0,className:`inapp-ai-window ${Ha} ${g||b||E?"":Ta} ${ra} ${Wa}`,style:Ca,children:[b&&(0,a.jsx)("div",{className:`inapp-ai-resize-handle ${K?"inapp-ai-resize-handle-right":"inapp-ai-resize-handle-left"}`,onMouseDown:()=>xa(!0),title:"Drag to resize"}),(g||b)&&A?(0,a.jsxs)("div",{className:"inapp-ai-folded-content",onClick:ka,style:{cursor:"pointer"},title:"Click to unfold",children:[(0,a.jsx)("div",{className:"inapp-ai-folded-icon",children:e.buttonIcon||"\u{1F916}"}),(0,a.jsx)("div",{className:"inapp-ai-folded-text",children:"AI"}),N.length>0&&(0,a.jsx)("div",{className:"inapp-ai-message-count",children:N.length})]}):(0,a.jsxs)(a.Fragment,{children:[ca&&(0,a.jsxs)("div",{className:"inapp-ai-header",style:{...e.headerBackground&&{background:e.headerBackground},...e.headerTextColor&&{color:e.headerTextColor}},children:[(0,a.jsxs)("div",{className:"inapp-ai-header-title",children:[(0,a.jsx)("span",{className:"inapp-ai-header-icon",children:e.buttonIcon||"\u{1F916}"}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{children:e.headerTitle||"AI Assistant"}),(0,a.jsx)("p",{children:R?(0,a.jsxs)("span",{className:"inapp-ai-status-connected",children:[(0,a.jsx)("span",{className:"inapp-ai-status-dot"}),"Connected"]}):(0,a.jsxs)("span",{className:"inapp-ai-status-disconnected",children:[(0,a.jsx)("span",{className:"inapp-ai-status-dot"}),"Disconnected"]})})]})]}),(g||b)&&(0,a.jsx)("button",{className:"inapp-ai-header-fold-btn",onClick:ka,"aria-label":A?`Unfold ${g?"sidebar":"panel"}`:`Fold ${g?"sidebar":"panel"}`,title:A?`Unfold ${g?"sidebar":"panel"}`:`Fold ${g?"sidebar":"panel"}`,children:Fa||K?"\u25C0":"\u25B6"}),!g&&!b&&!E&&(0,a.jsx)("button",{className:"inapp-ai-close-btn",onClick:()=>Z(!1),"aria-label":"Close",children:"\u2715"})]}),j&&(0,a.jsx)(Ia,{error:j,onDismiss:()=>z(null)}),(0,a.jsxs)("div",{className:"inapp-ai-messages",role:"log","aria-live":"polite","aria-label":"Chat messages",children:[N.length===0?(0,a.jsxs)("div",{className:"inapp-ai-empty-state",role:"status",children:[(0,a.jsx)("div",{className:"inapp-ai-empty-icon","aria-hidden":"true",children:"\u{1F4AC}"}),(0,a.jsx)("h4",{children:"Start a conversation"}),(0,a.jsx)("p",{children:"How can I help you today?"})]}):N.map(i=>(0,a.jsxs)("div",{className:`inapp-ai-message inapp-ai-message-${i.role}`,role:"article","aria-label":`${i.role==="user"?"User":"Assistant"} message`,children:[(0,a.jsx)("div",{className:"inapp-ai-message-icon","aria-hidden":"true",children:i.role==="user"?"\u{1F464}":"\u{1F916}"}),(0,a.jsxs)("div",{className:"inapp-ai-message-content",children:[(0,a.jsx)("div",{className:"inapp-ai-message-text",style:{...i.role==="user"&&e.userMessageBackground&&{background:e.userMessageBackground},...i.role==="user"&&e.userMessageColor&&{color:e.userMessageColor},...i.role==="assistant"&&e.assistantMessageBackground&&{background:e.assistantMessageBackground},...i.role==="assistant"&&e.assistantMessageColor&&{color:e.assistantMessageColor},...e.borderRadius&&{borderRadius:e.borderRadius}},children:i.role==="assistant"?(0,a.jsx)(pa.default,{components:{code:Ma},children:i.content}):i.content}),(0,a.jsxs)("div",{className:"inapp-ai-message-time",children:[i.timestamp?.toLocaleTimeString()||"",i.usage&&(0,a.jsxs)("span",{className:"inapp-ai-message-tokens",children:[" \u2022 ",i.usage.totalTokens," tokens"]})]})]})]},i.id)),u&&(0,a.jsxs)("div",{className:"inapp-ai-message inapp-ai-message-assistant",children:[(0,a.jsx)("div",{className:"inapp-ai-message-icon",children:(0,a.jsx)("div",{style:{animation:"pulse 2s infinite"},children:"\u{1F916}"})}),(0,a.jsx)("div",{className:"inapp-ai-message-content",children:(0,a.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[(0,a.jsxs)("div",{className:"inapp-ai-typing-indicator",children:[(0,a.jsx)("span",{}),(0,a.jsx)("span",{}),(0,a.jsx)("span",{})]}),(0,a.jsx)(Ea,{})]})})]}),(0,a.jsx)("div",{ref:aa})]}),(0,a.jsxs)("div",{className:"inapp-ai-input-area",role:"form","aria-label":"Message input",children:[N.length>0&&(0,a.jsxs)("button",{className:"inapp-ai-clear-btn",onClick:wa,disabled:u,"aria-label":"Clear conversation history",tabIndex:0,children:[(0,a.jsx)("span",{"aria-hidden":"true",children:"\u{1F5D1}\uFE0F"})," Clear"]}),(0,a.jsxs)("div",{className:"inapp-ai-input-wrapper",children:[(0,a.jsx)("input",{ref:ia,type:"text",className:"inapp-ai-input",placeholder:e.inputPlaceholder||"Type your message...",value:D,onChange:i=>Q(i.target.value),onKeyPress:ya,disabled:u||!R,"aria-label":"Message input","aria-describedby":"send-hint",tabIndex:0,style:{...e.inputBackground&&{background:e.inputBackground},...e.inputBorderColor&&{borderColor:e.inputBorderColor},...e.inputTextColor&&{color:e.inputTextColor}}}),(0,a.jsxs)("button",{className:"inapp-ai-send-btn",onClick:na,disabled:u||!R||!D.trim(),"aria-label":"Send message",tabIndex:0,children:[(0,a.jsx)("span",{"aria-hidden":"true",children:u?"\u23F3":"\u2B06"}),(0,a.jsx)("span",{className:"sr-only",children:u?"Sending...":"Send message"})]}),(0,a.jsx)("span",{id:"send-hint",className:"sr-only",children:"Press Enter to send"})]})]})]})]})]})}var w=require("react");function sa(t={}){let{initialTools:o=[],autoCleanup:p=!0}=t,n=(0,w.useRef)(new Set),[s,h]=(0,w.useState)(()=>(o.forEach(r=>{n.current.has(r.name)?console.warn(`[useTools] Duplicate tool name in initialTools: "${r.name}". Only the first occurrence will be used.`):n.current.add(r.name)}),o.filter((r,d)=>o.findIndex(c=>c.name===r.name)===d))),y=(0,w.useCallback)(r=>{if(!r.name)throw new Error("[useTools] Tool must have a name");if(n.current.has(r.name)){console.warn(`[useTools] Tool "${r.name}" is already registered. Use unregisterTool() first if you want to replace it.`);return}if(r.description||console.warn(`[useTools] Tool "${r.name}" is missing description`),r.parameters||console.warn(`[useTools] Tool "${r.name}" is missing parameters schema`),typeof r.handler!="function")throw new Error(`[useTools] Tool "${r.name}" must have a handler function`);n.current.add(r.name),h(d=>[...d,r])},[]),e=(0,w.useCallback)(r=>{if(!n.current.has(r)){console.warn(`[useTools] Tool "${r}" is not registered`);return}n.current.delete(r),h(d=>d.filter(c=>c.name!==r))},[]),k=(0,w.useCallback)(()=>{n.current.clear(),h([])},[]),C=(0,w.useCallback)(r=>n.current.has(r),[]);return(0,w.useEffect)(()=>{if(p)return()=>{n.current.clear()}},[p]),{tools:s,registerTool:y,unregisterTool:e,clearTools:k,hasTool:C}}var m=require("react"),Da=require("react/jsx-runtime"),$a=(0,m.createContext)(null);function da({children:t,initialTools:o={}}){let[p,n]=(0,m.useState)(()=>new Map(Object.entries(o))),s=(0,m.useCallback)((d,c)=>{if(!d||typeof d!="string")throw new Error("[ToolRegistry] Namespace must be a non-empty string");if(!/^[a-zA-Z0-9_-]+$/.test(d))throw new Error(`[ToolRegistry] Invalid namespace "${d}". Use only alphanumeric characters, hyphens, and underscores.`);if(!Array.isArray(c))throw new Error("[ToolRegistry] Tools must be an array");let M=new Set;c.forEach(x=>{M.has(x.name)&&console.warn(`[ToolRegistry] Duplicate tool name in namespace "${d}": "${x.name}"`),M.add(x.name)}),n(x=>{let T=new Map(x);return T.set(d,c),T})},[]),h=(0,m.useCallback)(d=>{n(c=>{if(!c.has(d))return console.warn(`[ToolRegistry] Namespace "${d}" is not registered`),c;let M=new Map(c);return M.delete(d),M})},[]),y=(0,m.useCallback)(d=>p.get(d)||[],[p]),e=(0,m.useCallback)(()=>{let d=[],c=new Set;for(let[M,x]of p.entries())for(let T of x){if(c.has(T.name)){console.warn(`[ToolRegistry] Tool name conflict: "${T.name}" exists in multiple namespaces. Only the first occurrence will be used.`);continue}c.add(T.name),d.push(T)}return d},[p]),k=(0,m.useCallback)(()=>{n(new Map)},[]),C=(0,m.useCallback)(()=>Array.from(p.keys()),[p]),r=(0,m.useMemo)(()=>({register:s,unregister:h,getTools:y,getAllTools:e,clear:k,getNamespaces:C}),[s,h,y,e,k,C]);return(0,Da.jsx)($a.Provider,{value:r,children:t})}function la(){let t=(0,m.useContext)($a);if(!t)throw new Error("[useToolRegistry] must be used within a ToolRegistryProvider. Wrap your component tree with <ToolRegistryProvider>.");return t}0&&(module.exports={InAppAI,ToolRegistryProvider,useToolRegistry,useTools});
6
+ `})]})}function Pa({error:t,onDismiss:o}){let n=(s=>s.includes("not responding")||s.includes("connection")||s.includes("network")?{type:"connection",icon:"\u{1F50C}",title:"Connection Error"}:s.includes("timeout")?{type:"timeout",icon:"\u23F1\uFE0F",title:"Request Timeout"}:s.includes("rate limit")?{type:"rateLimit",icon:"\u{1F6A6}",title:"Rate Limit"}:s.includes("authentication")||s.includes("unauthorized")?{type:"auth",icon:"\u{1F512}",title:"Authentication Error"}:{type:"generic",icon:"\u26A0\uFE0F",title:"Error"})(t);return(0,a.jsxs)("div",{className:"inapp-ai-error-banner",role:"alert","aria-live":"assertive",style:{display:"flex",alignItems:"flex-start",gap:"12px",padding:"14px 16px",background:"linear-gradient(135deg, #fff3cd 0%, #ffe9a6 100%)",borderLeft:"4px solid #ff9800",borderRadius:"0"},children:[(0,a.jsx)("span",{style:{fontSize:"20px"},children:n.icon}),(0,a.jsxs)("div",{style:{flex:1,minWidth:0},children:[(0,a.jsx)("div",{style:{fontWeight:600,marginBottom:"4px",color:"#856404"},children:n.title}),(0,a.jsx)("div",{style:{fontSize:"13px",color:"#856404",wordBreak:"break-word"},children:t}),n.type==="connection"&&(0,a.jsx)("div",{style:{fontSize:"12px",marginTop:"6px",color:"#997404"},children:"\u{1F4A1} Make sure the backend server is running on the correct port"})]}),(0,a.jsx)("button",{onClick:o,style:{background:"none",border:"none",color:"#856404",cursor:"pointer",padding:"4px 8px",fontSize:"16px",lineHeight:1},"aria-label":"Dismiss error",children:"\u2715"})]})}function za({inline:t,className:o,children:p,...n}){let[s,h]=(0,l.useState)(!1),w=/language-(\w+)/.exec(o||""),e=w?w[1]:"",T=String(p).replace(/\n$/,""),I=()=>{navigator.clipboard.writeText(T),h(!0),setTimeout(()=>h(!1),2e3)};return t?(0,a.jsx)("code",{className:o,...n,children:p}):(0,a.jsxs)("div",{style:{position:"relative",marginTop:"8px",marginBottom:"8px"},children:[(0,a.jsx)("button",{onClick:I,style:{position:"absolute",top:"8px",right:"8px",padding:"4px 8px",background:s?"#28a745":"rgba(255, 255, 255, 0.1)",color:"white",border:"none",borderRadius:"4px",cursor:"pointer",fontSize:"12px",zIndex:1,transition:"background 0.2s"},"aria-label":"Copy code",children:s?"\u2713 Copied!":"\u{1F4CB} Copy"}),(0,a.jsx)($a.Prism,{language:e||"text",style:Da.vscDarkPlus,customStyle:{borderRadius:"8px",padding:"16px",fontSize:"13px",marginTop:0,marginBottom:0},...n,children:T})]})}function Oa({endpoint:t,agentId:o,position:p="bottom-right",displayMode:n="popup",defaultFolded:s=!1,theme:h="light",context:w,customStyles:e={},tools:T=[],panelMinWidth:I="20%",panelMaxWidth:r="33.33%",panelDefaultWidth:d="25%",onPanelResize:c,conversationId:z,messages:x,onMessagesChange:C,showHeader:fa=!0,authToken:V,maxToolRounds:ua=10}){if(x===void 0||C===void 0)throw new Error("InAppAI requires controlled mode. Please provide both `messages` and `onMessagesChange` props. See documentation for usage examples.");let X=t||typeof process<"u"&&process.env?.REACT_APP_INAPPAI_ENDPOINT||typeof Ba.env<"u"&&Ba.env?.VITE_INAPPAI_ENDPOINT||"https://api.inappai.com/api",ba=()=>V&&(typeof V=="function"?V():V)||null,ma=()=>{let i={"Content-Type":"application/json"},v=ba();return v&&(i.Authorization=`Bearer ${v}`),i},Q=(0,l.useRef)(x);(0,l.useEffect)(()=>{Q.current=x},[x]);let N=x,aa=i=>{let v=Q.current,f=typeof i=="function"?i(v):i;Q.current=f,C(f)},[S,ea]=(0,l.useState)(n.startsWith("sidebar")||n.startsWith("panel")||n==="embedded"),[$,Ua]=(0,l.useState)(s&&(n.startsWith("sidebar")||n.startsWith("panel"))),[j,ia]=(0,l.useState)(""),[u,ha]=(0,l.useState)(!1),[A,xa]=(0,l.useState)(!1),[_,D]=(0,l.useState)(null),[Fa,Ha]=(0,l.useState)(d),[va,ya]=(0,l.useState)(!1),na=(0,l.useRef)(null),Wa=(0,l.useRef)(`react-demo-${Date.now()}`),wa=z||Wa.current,ra=(0,l.useRef)(null),ta=(0,l.useRef)(null),M=n==="embedded",g=n.startsWith("sidebar"),b=n.startsWith("panel"),ja=n==="sidebar-left",q=n==="panel-left";(0,l.useEffect)(()=>{(g||b||M)&&ea(!0)},[g,b,M]),(0,l.useEffect)(()=>{if(!b||!va)return;let i=f=>{let B=ra.current?.parentElement;if(!B)return;let Y=B.getBoundingClientRect(),O;q?O=f.clientX:O=Y.width-f.clientX;let Ea=O/Y.width*100,R=parseFloat(I),U=parseFloat(r),sa=`${Math.max(R,Math.min(U,Ea))}%`;Ha(sa),c&&c(O)},v=()=>{ya(!1)};return document.addEventListener("mousemove",i),document.addEventListener("mouseup",v),()=>{document.removeEventListener("mousemove",i),document.removeEventListener("mouseup",v)}},[va,b,q,I,r,c]),(0,l.useEffect)(()=>{(async()=>{try{let v={},f=ba();f&&(v.Authorization=`Bearer ${f}`),(await fetch(`${X}/${o}/health`,{headers:v})).ok?(xa(!0),D(null)):D("Backend not responding")}catch{D("Failed to connect to backend"),xa(!1)}})()},[X,o]),(0,l.useEffect)(()=>{na.current?.scrollIntoView({behavior:"smooth"})},[N]),(0,l.useEffect)(()=>{!u&&A&&setTimeout(()=>{ta.current?.focus()},100)},[u,A]);let oa=async()=>{let i=j.trim();if(!i||u)return;let v={id:`${Date.now()}-user`,role:"user",content:i,timestamp:new Date};aa(f=>[...f,v]),ia(""),ha(!0),D(null);try{let f=()=>typeof w=="function"?w():w,B=T.map(({name:y,description:F,parameters:J})=>({type:"function",function:{name:y,description:F,parameters:J}})),Y=()=>N.map(y=>{let F={role:y.role,content:y.content};if(y.toolActions&&y.toolActions.length>0){let J=y.toolActions.map(K=>`${K.tool}(${JSON.stringify(K.args)}) \u2192 ${JSON.stringify(K.result)}`).join(", ");F.content+=`
7
+
8
+ [Tool actions: ${J}]`}return F}),O=await fetch(`${X}/${o}/chat`,{method:"POST",headers:ma(),body:JSON.stringify({message:i,messages:Y(),conversationId:wa,context:f(),tools:B.length>0?B:void 0,disableCache:!1})});if(!O.ok)throw new Error("Failed to get response");let R=await O.json(),U=0,G=[];for(;R.toolCalls&&R.toolCalls.length>0&&U<ua;){U++;let y=await Promise.all(R.toolCalls.map(async P=>{let H=P.function?.name||P.name,E=P.function?.arguments?JSON.parse(P.function.arguments):P.parameters,W=T.find(L=>L.name===H);if(!W)return{success:!1,error:`Tool '${H}' not found`};try{return await Promise.resolve(W.handler(E))}catch(L){return console.error(`Tool '${W.name}' failed:`,L),{success:!1,error:L.message}}}));y.forEach((P,H)=>{let E=R.toolCalls[H],W=E.function?.name||E.name,L=E.function?.arguments?JSON.parse(E.function.arguments):E.parameters||{};G.push({tool:W,args:L,result:P})});let F=y.map((P,H)=>{let E=R.toolCalls[H];return`Tool "${E.function?.name||E.name}" result: ${JSON.stringify(P)}`}).join(`
9
+ `),J=`[TOOL EXECUTION COMPLETE - Round ${U}]
10
+ The following ${y.length} tool call(s) have been executed successfully. Do NOT re-execute them.
11
+ `+F+`
12
+ If all requested actions are complete, respond to the user with a summary. Only make additional tool calls if new/different actions are needed.`,K=U>=ua,Ia=await fetch(`${X}/${o}/chat`,{method:"POST",headers:ma(),body:JSON.stringify({message:J,messages:Y(),conversationId:wa,context:f(),tools:K?void 0:B.length>0?B:void 0,disableCache:!1})});if(!Ia.ok)throw new Error("Failed to get AI response for tool results");R=await Ia.json()}let sa={id:`${Date.now()}-assistant`,role:"assistant",content:R.message||"I executed the tools successfully.",timestamp:new Date,usage:R.usage,toolActions:G.length>0?G:void 0};aa(y=>[...y,sa])}catch(f){console.error("\u274C Error:",f),D(f instanceof Error?f.message:"Unknown error")}finally{ha(!1)}},ka=i=>{i.key==="Enter"&&!i.shiftKey&&(i.preventDefault(),oa())},Ta=()=>{aa([])},Ca=()=>{Ua(!$)},Na=`inapp-ai-${p}`,pa=h&&h!=="light"?`inapp-ai-theme-${h}`:"",Ya=M?"inapp-ai-embedded":g?`inapp-ai-sidebar inapp-ai-${n}`:b?`inapp-ai-panel inapp-ai-${n}`:"inapp-ai-popup",Ja=$?"inapp-ai-folded":"",Ka={...e.buttonBackgroundColor&&{background:e.buttonBackgroundColor},...e.buttonTextColor&&{color:e.buttonTextColor},...e.buttonSize&&{width:e.buttonSize,height:e.buttonSize,fontSize:`calc(${e.buttonSize} * 0.5)`},...e.buttonBorderRadius&&{borderRadius:e.buttonBorderRadius},...e.boxShadow&&{boxShadow:e.boxShadow}},Ra={...e.windowWidth&&!g&&!b&&{width:e.windowWidth},...e.windowHeight&&!g&&!b&&{height:e.windowHeight},...e.windowBorderRadius&&{borderRadius:e.windowBorderRadius},...e.fontFamily&&{fontFamily:e.fontFamily},...e.fontSize&&{fontSize:e.fontSize},...e.sidebarWidth&&g&&!$&&{width:e.sidebarWidth},...e.sidebarFoldedWidth&&g&&$&&{width:e.sidebarFoldedWidth},...b&&{width:Fa}};return M&&S?(0,a.jsxs)("div",{ref:ra,role:"region","aria-label":"AI Assistant Chat",className:`inapp-ai-window inapp-ai-embedded ${pa}`,style:Ra,children:[fa&&(0,a.jsx)("div",{className:"inapp-ai-header",style:{...e.headerBackground&&{background:e.headerBackground},...e.headerTextColor&&{color:e.headerTextColor}},children:(0,a.jsxs)("div",{className:"inapp-ai-header-title",children:[(0,a.jsx)("span",{className:"inapp-ai-header-icon",children:e.buttonIcon||"\u{1F916}"}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{children:e.headerTitle||"AI Assistant"}),(0,a.jsx)("p",{children:A?(0,a.jsxs)("span",{className:"inapp-ai-status-connected",children:[(0,a.jsx)("span",{className:"inapp-ai-status-dot"}),"Connected"]}):(0,a.jsxs)("span",{className:"inapp-ai-status-disconnected",children:[(0,a.jsx)("span",{className:"inapp-ai-status-dot"}),"Disconnected"]})})]})]})}),_&&(0,a.jsx)(Pa,{error:_,onDismiss:()=>D(null)}),(0,a.jsxs)("div",{className:"inapp-ai-messages",role:"log","aria-live":"polite","aria-label":"Chat messages",children:[N.length===0?(0,a.jsxs)("div",{className:"inapp-ai-empty-state",role:"status",children:[(0,a.jsx)("div",{className:"inapp-ai-empty-icon","aria-hidden":"true",children:"\u{1F4AC}"}),(0,a.jsx)("h4",{children:"Start a conversation"}),(0,a.jsx)("p",{children:"How can I help you today?"})]}):N.map(i=>(0,a.jsxs)("div",{className:`inapp-ai-message inapp-ai-message-${i.role}`,role:"article","aria-label":`${i.role==="user"?"User":"Assistant"} message`,children:[(0,a.jsx)("div",{className:"inapp-ai-message-icon","aria-hidden":"true",children:i.role==="user"?"\u{1F464}":"\u{1F916}"}),(0,a.jsxs)("div",{className:"inapp-ai-message-content",children:[(0,a.jsx)("div",{className:"inapp-ai-message-text",style:{...i.role==="user"&&e.userMessageBackground&&{background:e.userMessageBackground},...i.role==="user"&&e.userMessageColor&&{color:e.userMessageColor},...i.role==="assistant"&&e.assistantMessageBackground&&{background:e.assistantMessageBackground},...i.role==="assistant"&&e.assistantMessageColor&&{color:e.assistantMessageColor},...e.borderRadius&&{borderRadius:e.borderRadius}},children:i.role==="assistant"?(0,a.jsx)(da.default,{components:{code:za},children:i.content}):i.content}),(0,a.jsxs)("div",{className:"inapp-ai-message-time",children:[i.timestamp?new Date(i.timestamp).toLocaleTimeString():"",i.usage&&(0,a.jsxs)("span",{className:"inapp-ai-message-tokens",children:[" \u2022 ",i.usage.totalTokens," tokens"]})]})]})]},i.id)),u&&(0,a.jsxs)("div",{className:"inapp-ai-message inapp-ai-message-assistant",children:[(0,a.jsx)("div",{className:"inapp-ai-message-icon",children:(0,a.jsx)("div",{style:{animation:"pulse 2s infinite"},children:"\u{1F916}"})}),(0,a.jsx)("div",{className:"inapp-ai-message-content",children:(0,a.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[(0,a.jsxs)("div",{className:"inapp-ai-typing-indicator",children:[(0,a.jsx)("span",{}),(0,a.jsx)("span",{}),(0,a.jsx)("span",{})]}),(0,a.jsx)(Ma,{})]})})]}),(0,a.jsx)("div",{ref:na})]}),(0,a.jsxs)("div",{className:"inapp-ai-input-area",role:"form","aria-label":"Message input",children:[N.length>0&&(0,a.jsxs)("button",{className:"inapp-ai-clear-btn",onClick:Ta,disabled:u,"aria-label":"Clear conversation history",tabIndex:0,children:[(0,a.jsx)("span",{"aria-hidden":"true",children:"\u{1F5D1}\uFE0F"})," Clear"]}),(0,a.jsxs)("div",{className:"inapp-ai-input-wrapper",children:[(0,a.jsx)("input",{ref:ta,type:"text",className:"inapp-ai-input",placeholder:e.inputPlaceholder||"Type your message...",value:j,onChange:i=>ia(i.target.value),onKeyPress:ka,disabled:u||!A,"aria-label":"Message input","aria-describedby":"send-hint",tabIndex:0,style:{...e.inputBackground&&{background:e.inputBackground},...e.inputBorderColor&&{borderColor:e.inputBorderColor},...e.inputTextColor&&{color:e.inputTextColor}}}),(0,a.jsxs)("button",{className:"inapp-ai-send-btn",onClick:oa,disabled:u||!A||!j.trim(),"aria-label":"Send message",tabIndex:0,children:[(0,a.jsx)("span",{"aria-hidden":"true",children:u?"\u23F3":"\u2B06"}),(0,a.jsx)("span",{className:"sr-only",children:u?"Sending...":"Send message"})]}),(0,a.jsx)("span",{id:"send-hint",className:"sr-only",children:"Press Enter to send"})]})]})]}):(0,a.jsxs)(a.Fragment,{children:[!g&&!b&&!M&&(0,a.jsxs)("button",{className:`inapp-ai-button ${Na} ${pa}`,style:Ka,onClick:()=>ea(!S),"aria-label":S?"Close AI Assistant":"Open AI Assistant","aria-expanded":S,"aria-haspopup":"dialog",tabIndex:0,children:[S?"\u2715":e.buttonIcon||"\u{1F916}",!A&&(0,a.jsx)("span",{className:"inapp-ai-offline-indicator",role:"status","aria-label":"Backend disconnected"})]}),S&&(0,a.jsxs)("div",{ref:ra,role:M?"region":"dialog","aria-label":"AI Assistant Chat","aria-modal":!g&&!b&&!M?"true":void 0,className:`inapp-ai-window ${Ya} ${g||b||M?"":Na} ${pa} ${Ja}`,style:Ra,children:[b&&(0,a.jsx)("div",{className:`inapp-ai-resize-handle ${q?"inapp-ai-resize-handle-right":"inapp-ai-resize-handle-left"}`,onMouseDown:()=>ya(!0),title:"Drag to resize"}),(g||b)&&$?(0,a.jsxs)("div",{className:"inapp-ai-folded-content",onClick:Ca,style:{cursor:"pointer"},title:"Click to unfold",children:[(0,a.jsx)("div",{className:"inapp-ai-folded-icon",children:e.buttonIcon||"\u{1F916}"}),(0,a.jsx)("div",{className:"inapp-ai-folded-text",children:"AI"}),N.length>0&&(0,a.jsx)("div",{className:"inapp-ai-message-count",children:N.length})]}):(0,a.jsxs)(a.Fragment,{children:[fa&&(0,a.jsxs)("div",{className:"inapp-ai-header",style:{...e.headerBackground&&{background:e.headerBackground},...e.headerTextColor&&{color:e.headerTextColor}},children:[(0,a.jsxs)("div",{className:"inapp-ai-header-title",children:[(0,a.jsx)("span",{className:"inapp-ai-header-icon",children:e.buttonIcon||"\u{1F916}"}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{children:e.headerTitle||"AI Assistant"}),(0,a.jsx)("p",{children:A?(0,a.jsxs)("span",{className:"inapp-ai-status-connected",children:[(0,a.jsx)("span",{className:"inapp-ai-status-dot"}),"Connected"]}):(0,a.jsxs)("span",{className:"inapp-ai-status-disconnected",children:[(0,a.jsx)("span",{className:"inapp-ai-status-dot"}),"Disconnected"]})})]})]}),(g||b)&&(0,a.jsx)("button",{className:"inapp-ai-header-fold-btn",onClick:Ca,"aria-label":$?`Unfold ${g?"sidebar":"panel"}`:`Fold ${g?"sidebar":"panel"}`,title:$?`Unfold ${g?"sidebar":"panel"}`:`Fold ${g?"sidebar":"panel"}`,children:ja||q?"\u25C0":"\u25B6"}),!g&&!b&&!M&&(0,a.jsx)("button",{className:"inapp-ai-close-btn",onClick:()=>ea(!1),"aria-label":"Close",children:"\u2715"})]}),_&&(0,a.jsx)(Pa,{error:_,onDismiss:()=>D(null)}),(0,a.jsxs)("div",{className:"inapp-ai-messages",role:"log","aria-live":"polite","aria-label":"Chat messages",children:[N.length===0?(0,a.jsxs)("div",{className:"inapp-ai-empty-state",role:"status",children:[(0,a.jsx)("div",{className:"inapp-ai-empty-icon","aria-hidden":"true",children:"\u{1F4AC}"}),(0,a.jsx)("h4",{children:"Start a conversation"}),(0,a.jsx)("p",{children:"How can I help you today?"})]}):N.map(i=>(0,a.jsxs)("div",{className:`inapp-ai-message inapp-ai-message-${i.role}`,role:"article","aria-label":`${i.role==="user"?"User":"Assistant"} message`,children:[(0,a.jsx)("div",{className:"inapp-ai-message-icon","aria-hidden":"true",children:i.role==="user"?"\u{1F464}":"\u{1F916}"}),(0,a.jsxs)("div",{className:"inapp-ai-message-content",children:[(0,a.jsx)("div",{className:"inapp-ai-message-text",style:{...i.role==="user"&&e.userMessageBackground&&{background:e.userMessageBackground},...i.role==="user"&&e.userMessageColor&&{color:e.userMessageColor},...i.role==="assistant"&&e.assistantMessageBackground&&{background:e.assistantMessageBackground},...i.role==="assistant"&&e.assistantMessageColor&&{color:e.assistantMessageColor},...e.borderRadius&&{borderRadius:e.borderRadius}},children:i.role==="assistant"?(0,a.jsx)(da.default,{components:{code:za},children:i.content}):i.content}),(0,a.jsxs)("div",{className:"inapp-ai-message-time",children:[i.timestamp?.toLocaleTimeString()||"",i.usage&&(0,a.jsxs)("span",{className:"inapp-ai-message-tokens",children:[" \u2022 ",i.usage.totalTokens," tokens"]})]})]})]},i.id)),u&&(0,a.jsxs)("div",{className:"inapp-ai-message inapp-ai-message-assistant",children:[(0,a.jsx)("div",{className:"inapp-ai-message-icon",children:(0,a.jsx)("div",{style:{animation:"pulse 2s infinite"},children:"\u{1F916}"})}),(0,a.jsx)("div",{className:"inapp-ai-message-content",children:(0,a.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[(0,a.jsxs)("div",{className:"inapp-ai-typing-indicator",children:[(0,a.jsx)("span",{}),(0,a.jsx)("span",{}),(0,a.jsx)("span",{})]}),(0,a.jsx)(Ma,{})]})})]}),(0,a.jsx)("div",{ref:na})]}),(0,a.jsxs)("div",{className:"inapp-ai-input-area",role:"form","aria-label":"Message input",children:[N.length>0&&(0,a.jsxs)("button",{className:"inapp-ai-clear-btn",onClick:Ta,disabled:u,"aria-label":"Clear conversation history",tabIndex:0,children:[(0,a.jsx)("span",{"aria-hidden":"true",children:"\u{1F5D1}\uFE0F"})," Clear"]}),(0,a.jsxs)("div",{className:"inapp-ai-input-wrapper",children:[(0,a.jsx)("input",{ref:ta,type:"text",className:"inapp-ai-input",placeholder:e.inputPlaceholder||"Type your message...",value:j,onChange:i=>ia(i.target.value),onKeyPress:ka,disabled:u||!A,"aria-label":"Message input","aria-describedby":"send-hint",tabIndex:0,style:{...e.inputBackground&&{background:e.inputBackground},...e.inputBorderColor&&{borderColor:e.inputBorderColor},...e.inputTextColor&&{color:e.inputTextColor}}}),(0,a.jsxs)("button",{className:"inapp-ai-send-btn",onClick:oa,disabled:u||!A||!j.trim(),"aria-label":"Send message",tabIndex:0,children:[(0,a.jsx)("span",{"aria-hidden":"true",children:u?"\u23F3":"\u2B06"}),(0,a.jsx)("span",{className:"sr-only",children:u?"Sending...":"Send message"})]}),(0,a.jsx)("span",{id:"send-hint",className:"sr-only",children:"Press Enter to send"})]})]})]})]})]})}var k=require("react");function la(t={}){let{initialTools:o=[],autoCleanup:p=!0}=t,n=(0,k.useRef)(new Set),[s,h]=(0,k.useState)(()=>(o.forEach(r=>{n.current.has(r.name)?console.warn(`[useTools] Duplicate tool name in initialTools: "${r.name}". Only the first occurrence will be used.`):n.current.add(r.name)}),o.filter((r,d)=>o.findIndex(c=>c.name===r.name)===d))),w=(0,k.useCallback)(r=>{if(!r.name)throw new Error("[useTools] Tool must have a name");if(n.current.has(r.name)){console.warn(`[useTools] Tool "${r.name}" is already registered. Use unregisterTool() first if you want to replace it.`);return}if(r.description||console.warn(`[useTools] Tool "${r.name}" is missing description`),r.parameters||console.warn(`[useTools] Tool "${r.name}" is missing parameters schema`),typeof r.handler!="function")throw new Error(`[useTools] Tool "${r.name}" must have a handler function`);n.current.add(r.name),h(d=>[...d,r])},[]),e=(0,k.useCallback)(r=>{if(!n.current.has(r)){console.warn(`[useTools] Tool "${r}" is not registered`);return}n.current.delete(r),h(d=>d.filter(c=>c.name!==r))},[]),T=(0,k.useCallback)(()=>{n.current.clear(),h([])},[]),I=(0,k.useCallback)(r=>n.current.has(r),[]);return(0,k.useEffect)(()=>{if(p)return()=>{n.current.clear()}},[p]),{tools:s,registerTool:w,unregisterTool:e,clearTools:T,hasTool:I}}var m=require("react"),Sa=require("react/jsx-runtime"),La=(0,m.createContext)(null);function ca({children:t,initialTools:o={}}){let[p,n]=(0,m.useState)(()=>new Map(Object.entries(o))),s=(0,m.useCallback)((d,c)=>{if(!d||typeof d!="string")throw new Error("[ToolRegistry] Namespace must be a non-empty string");if(!/^[a-zA-Z0-9_-]+$/.test(d))throw new Error(`[ToolRegistry] Invalid namespace "${d}". Use only alphanumeric characters, hyphens, and underscores.`);if(!Array.isArray(c))throw new Error("[ToolRegistry] Tools must be an array");let z=new Set;c.forEach(x=>{z.has(x.name)&&console.warn(`[ToolRegistry] Duplicate tool name in namespace "${d}": "${x.name}"`),z.add(x.name)}),n(x=>{let C=new Map(x);return C.set(d,c),C})},[]),h=(0,m.useCallback)(d=>{n(c=>{if(!c.has(d))return console.warn(`[ToolRegistry] Namespace "${d}" is not registered`),c;let z=new Map(c);return z.delete(d),z})},[]),w=(0,m.useCallback)(d=>p.get(d)||[],[p]),e=(0,m.useCallback)(()=>{let d=[],c=new Set;for(let[z,x]of p.entries())for(let C of x){if(c.has(C.name)){console.warn(`[ToolRegistry] Tool name conflict: "${C.name}" exists in multiple namespaces. Only the first occurrence will be used.`);continue}c.add(C.name),d.push(C)}return d},[p]),T=(0,m.useCallback)(()=>{n(new Map)},[]),I=(0,m.useCallback)(()=>Array.from(p.keys()),[p]),r=(0,m.useMemo)(()=>({register:s,unregister:h,getTools:w,getAllTools:e,clear:T,getNamespaces:I}),[s,h,w,e,T,I]);return(0,Sa.jsx)(La.Provider,{value:r,children:t})}function ga(){let t=(0,m.useContext)(La);if(!t)throw new Error("[useToolRegistry] must be used within a ToolRegistryProvider. Wrap your component tree with <ToolRegistryProvider>.");return t}0&&(module.exports={InAppAI,ToolRegistryProvider,useToolRegistry,useTools});
8
13
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/components/InAppAI.tsx","../src/hooks/useTools.ts","../src/hooks/useToolRegistry.tsx"],"sourcesContent":["export { InAppAI } from './components/InAppAI';\nexport type {\n CustomStyles,\n Tool,\n Message,\n InAppAIProps,\n} from './types';\n\n// Hooks\nexport {\n useTools,\n useToolRegistry,\n ToolRegistryProvider,\n} from './hooks';\nexport type {\n UseToolsOptions,\n UseToolsReturn,\n ToolRegistry,\n ToolRegistryProviderProps,\n} from './hooks';\n","import { useState, useEffect, useRef } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';\nimport type { CustomStyles, Tool, InAppAIProps, Message } from '../types';\nimport './themes.css';\nimport './InAppAI.css';\n\n// Internal message type (extends exported Message with required timestamp)\ninterface InternalMessage extends Message {\n timestamp: Date;\n}\n\n// Loading skeleton component\nfunction LoadingSkeleton() {\n return (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '8px',\n padding: '12px 16px',\n background: 'var(--inapp-ai-assistant-bg)',\n borderRadius: 'var(--inapp-ai-border-radius)',\n }}\n >\n <div\n style={{\n height: '14px',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '90%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.1s',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '75%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.2s',\n borderRadius: '4px',\n }}\n />\n <style>{`\n @keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n `}</style>\n </div>\n );\n}\n\n// Error message component with enhanced display\ninterface ErrorMessageProps {\n error: string;\n onDismiss: () => void;\n}\n\nfunction ErrorMessage({ error, onDismiss }: ErrorMessageProps) {\n // Determine error type based on message\n const getErrorType = (msg: string) => {\n if (msg.includes('not responding') || msg.includes('connection') || msg.includes('network')) {\n return { type: 'connection', icon: '🔌', title: 'Connection Error' };\n }\n if (msg.includes('timeout')) {\n return { type: 'timeout', icon: '⏱️', title: 'Request Timeout' };\n }\n if (msg.includes('rate limit')) {\n return { type: 'rateLimit', icon: '🚦', title: 'Rate Limit' };\n }\n if (msg.includes('authentication') || msg.includes('unauthorized')) {\n return { type: 'auth', icon: '🔒', title: 'Authentication Error' };\n }\n return { type: 'generic', icon: '⚠️', title: 'Error' };\n };\n\n const errorInfo = getErrorType(error);\n\n return (\n <div\n className=\"inapp-ai-error-banner\"\n role=\"alert\"\n aria-live=\"assertive\"\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n gap: '12px',\n padding: '14px 16px',\n background: 'linear-gradient(135deg, #fff3cd 0%, #ffe9a6 100%)',\n borderLeft: '4px solid #ff9800',\n borderRadius: '0',\n }}\n >\n <span style={{ fontSize: '20px' }}>{errorInfo.icon}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontWeight: 600, marginBottom: '4px', color: '#856404' }}>\n {errorInfo.title}\n </div>\n <div style={{ fontSize: '13px', color: '#856404', wordBreak: 'break-word' }}>\n {error}\n </div>\n {errorInfo.type === 'connection' && (\n <div style={{ fontSize: '12px', marginTop: '6px', color: '#997404' }}>\n 💡 Make sure the backend server is running on the correct port\n </div>\n )}\n </div>\n <button\n onClick={onDismiss}\n style={{\n background: 'none',\n border: 'none',\n color: '#856404',\n cursor: 'pointer',\n padding: '4px 8px',\n fontSize: '16px',\n lineHeight: 1,\n }}\n aria-label=\"Dismiss error\"\n >\n ✕\n </button>\n </div>\n );\n}\n\n// Code block component with syntax highlighting and copy button\ninterface CodeBlockProps {\n inline?: boolean;\n className?: string;\n children?: React.ReactNode;\n}\n\nfunction CodeBlock({ inline, className, children, ...props }: CodeBlockProps) {\n const [copied, setCopied] = useState(false);\n\n // Extract language from className (format: language-js, language-python, etc.)\n const match = /language-(\\w+)/.exec(className || '');\n const language = match ? match[1] : '';\n\n const codeString = String(children).replace(/\\n$/, '');\n\n const handleCopy = () => {\n navigator.clipboard.writeText(codeString);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n // Inline code (backticks)\n if (inline) {\n return <code className={className} {...props}>{children}</code>;\n }\n\n // Code block with syntax highlighting\n return (\n <div style={{ position: 'relative', marginTop: '8px', marginBottom: '8px' }}>\n <button\n onClick={handleCopy}\n style={{\n position: 'absolute',\n top: '8px',\n right: '8px',\n padding: '4px 8px',\n background: copied ? '#28a745' : 'rgba(255, 255, 255, 0.1)',\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n zIndex: 1,\n transition: 'background 0.2s',\n }}\n aria-label=\"Copy code\"\n >\n {copied ? '✓ Copied!' : '📋 Copy'}\n </button>\n {/* @ts-ignore - React 18 JSX type compatibility */}\n <SyntaxHighlighter\n language={language || 'text'}\n style={vscDarkPlus}\n customStyle={{\n borderRadius: '8px',\n padding: '16px',\n fontSize: '13px',\n marginTop: 0,\n marginBottom: 0,\n }}\n {...props}\n >\n {codeString}\n </SyntaxHighlighter>\n </div>\n );\n}\n\nexport function InAppAI({\n endpoint,\n agentId,\n position = 'bottom-right',\n displayMode = 'popup',\n defaultFolded = false,\n theme = 'light',\n context,\n customStyles = {},\n tools = [],\n panelMinWidth = '20%',\n panelMaxWidth = '33.33%',\n panelDefaultWidth = '25%',\n onPanelResize,\n // Controlled mode props\n conversationId: externalConversationId,\n messages: externalMessages,\n onMessagesChange,\n showHeader = true,\n // Authentication\n authToken,\n // Tool execution\n maxToolRounds = 10,\n}: InAppAIProps) {\n // Require controlled mode - messages and onMessagesChange are required\n if (externalMessages === undefined || onMessagesChange === undefined) {\n throw new Error(\n 'InAppAI requires controlled mode. Please provide both `messages` and `onMessagesChange` props. ' +\n 'See documentation for usage examples.'\n );\n }\n\n // Determine API endpoint (environment variable > prop > default)\n const apiEndpoint = endpoint ||\n (typeof process !== 'undefined' && (process as any).env?.REACT_APP_INAPPAI_ENDPOINT) ||\n (typeof (import.meta as any).env !== 'undefined' && (import.meta as any).env?.VITE_INAPPAI_ENDPOINT) ||\n 'https://api.inappai.com/api';\n\n // Helper to get auth token (supports both static string and function)\n const getAuthToken = (): string | null => {\n if (!authToken) return null;\n const token = typeof authToken === 'function' ? authToken() : authToken;\n return token || null;\n };\n\n // Helper to build headers with optional Authorization\n const buildHeaders = (): Record<string, string> => {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const token = getAuthToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n return headers;\n };\n\n // Keep a ref to the latest external messages to avoid stale closures\n const externalMessagesRef = useRef<Message[]>(externalMessages);\n useEffect(() => {\n externalMessagesRef.current = externalMessages;\n }, [externalMessages]);\n\n // Messages from props, setMessages calls onMessagesChange\n const messages = externalMessages;\n const setMessages = (updater: Message[] | ((prev: Message[]) => Message[])) => {\n const currentMessages = externalMessagesRef.current;\n const newMessages = typeof updater === 'function' ? updater(currentMessages) : updater;\n externalMessagesRef.current = newMessages; // Update ref immediately for chained calls\n onMessagesChange(newMessages);\n };\n\n const [isOpen, setIsOpen] = useState(displayMode.startsWith('sidebar') || displayMode.startsWith('panel') || displayMode === 'embedded');\n const [isFolded, setIsFolded] = useState(defaultFolded && (displayMode.startsWith('sidebar') || displayMode.startsWith('panel')));\n const [inputValue, setInputValue] = useState('');\n const [isLoading, setIsLoading] = useState(false);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [panelWidth, setPanelWidth] = useState(panelDefaultWidth);\n const [isResizing, setIsResizing] = useState(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const internalConversationId = useRef(`react-demo-${Date.now()}`);\n const conversationId = externalConversationId || internalConversationId.current;\n const resizeRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const isEmbedded = displayMode === 'embedded';\n const isSidebar = displayMode.startsWith('sidebar');\n const isPanel = displayMode.startsWith('panel');\n const isLeftSidebar = displayMode === 'sidebar-left';\n // const isRightSidebar = displayMode === 'sidebar-right';\n const isLeftPanel = displayMode === 'panel-left';\n // const isRightPanel = displayMode === 'panel-right';\n\n // For sidebar, panel, and embedded mode, always stay open\n useEffect(() => {\n if (isSidebar || isPanel || isEmbedded) {\n setIsOpen(true);\n }\n }, [isSidebar, isPanel, isEmbedded]);\n\n // Panel resize handlers\n useEffect(() => {\n if (!isPanel || !isResizing) return;\n\n const handleMouseMove = (e: MouseEvent) => {\n const container = resizeRef.current?.parentElement;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n let newWidth: number;\n\n if (isLeftPanel) {\n newWidth = e.clientX;\n } else {\n newWidth = containerRect.width - e.clientX;\n }\n\n // Convert to percentage\n const widthPercent = (newWidth / containerRect.width) * 100;\n\n // Parse min/max widths\n const minPercent = parseFloat(panelMinWidth);\n const maxPercent = parseFloat(panelMaxWidth);\n\n // Clamp width between min and max\n const clampedPercent = Math.max(minPercent, Math.min(maxPercent, widthPercent));\n const newWidthStr = `${clampedPercent}%`;\n\n setPanelWidth(newWidthStr);\n if (onPanelResize) {\n onPanelResize(newWidth);\n }\n };\n\n const handleMouseUp = () => {\n setIsResizing(false);\n };\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n }, [isResizing, isPanel, isLeftPanel, panelMinWidth, panelMaxWidth, onPanelResize]);\n\n // Check backend connection once on mount\n useEffect(() => {\n const checkConnection = async () => {\n try {\n const headers: Record<string, string> = {};\n const token = getAuthToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n const response = await fetch(`${apiEndpoint}/${agentId}/health`, { headers });\n if (response.ok) {\n setIsConnected(true);\n setError(null);\n } else {\n setError('Backend not responding');\n }\n } catch (err) {\n setError('Failed to connect to backend');\n setIsConnected(false);\n }\n };\n\n checkConnection();\n }, [apiEndpoint, agentId]);\n\n // Auto-scroll to bottom\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n // Refocus input after loading completes\n useEffect(() => {\n if (!isLoading && isConnected) {\n // Small delay to ensure DOM has updated\n setTimeout(() => {\n inputRef.current?.focus();\n }, 100);\n }\n }, [isLoading, isConnected]);\n\n const sendMessage = async () => {\n const message = inputValue.trim();\n if (!message || isLoading) return;\n\n // Add user message\n const userMessage: Message = {\n id: `${Date.now()}-user`,\n role: 'user',\n content: message,\n timestamp: new Date(),\n };\n\n setMessages(prev => [...prev, userMessage]);\n setInputValue('');\n setIsLoading(true);\n setError(null);\n\n try {\n // Helper to get fresh context (supports both static and function contexts)\n const getContext = () => typeof context === 'function' ? context() : context;\n\n // Prepare tool definitions (without handlers) for backend in OpenAI format\n const toolDefinitions = tools.map(({ name, description, parameters }) => ({\n type: 'function' as const,\n function: {\n name,\n description,\n parameters,\n },\n }));\n\n const response = await fetch(`${apiEndpoint}/${agentId}/chat`, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({\n message,\n conversationId,\n context: getContext(),\n tools: toolDefinitions.length > 0 ? toolDefinitions : undefined,\n disableCache: false,\n }),\n });\n\n if (!response.ok) {\n throw new Error('Failed to get response');\n }\n\n const data = await response.json();\n\n // Iterative tool execution loop\n // The AI may return tool calls that, once executed, lead to more tool calls.\n // We loop until the AI returns a text-only response or we hit maxToolRounds.\n let currentData = data;\n let round = 0;\n\n while (\n currentData.toolCalls &&\n currentData.toolCalls.length > 0 &&\n round < maxToolRounds\n ) {\n round++;\n\n // Execute all tool calls in parallel\n const results = await Promise.all(\n currentData.toolCalls.map(async (toolCall: any) => {\n const toolName = toolCall.function?.name || toolCall.name;\n const toolArgs = toolCall.function?.arguments\n ? JSON.parse(toolCall.function.arguments)\n : toolCall.parameters;\n\n const tool = tools.find(t => t.name === toolName);\n if (!tool) {\n return { success: false, error: `Tool '${toolName}' not found` };\n }\n try {\n const result = await Promise.resolve(tool.handler(toolArgs));\n return result;\n } catch (error: any) {\n console.error(`Tool '${tool.name}' failed:`, error);\n return { success: false, error: error.message };\n }\n })\n );\n\n // Format tool results as text message\n const toolResultsMessage = results\n .map((r: any, idx: number) => {\n const toolCall = currentData.toolCalls[idx];\n const toolName = toolCall.function?.name || toolCall.name;\n return `Tool \"${toolName}\" result: ${JSON.stringify(r)}`;\n })\n .join('\\n');\n\n // On last allowed round, omit tools to force a text-only response\n const isLastAllowedRound = round >= maxToolRounds;\n\n // Send tool results back with tools and fresh context\n const followUpResponse = await fetch(`${apiEndpoint}/${agentId}/chat`, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({\n message: toolResultsMessage,\n conversationId,\n context: getContext(),\n tools: isLastAllowedRound ? undefined : (toolDefinitions.length > 0 ? toolDefinitions : undefined),\n disableCache: false,\n }),\n });\n\n if (!followUpResponse.ok) {\n throw new Error('Failed to get AI response for tool results');\n }\n\n currentData = await followUpResponse.json();\n }\n\n // Display final text response\n const assistantMessage: Message = {\n id: `${Date.now()}-assistant`,\n role: 'assistant',\n content: currentData.message || 'I executed the tools successfully.',\n timestamp: new Date(),\n usage: currentData.usage,\n };\n\n setMessages(prev => [...prev, assistantMessage]);\n } catch (err) {\n console.error('❌ Error:', err);\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleKeyPress = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n sendMessage();\n }\n };\n\n const clearMessages = () => {\n setMessages([]);\n };\n\n const toggleFolded = () => {\n setIsFolded(!isFolded);\n };\n\n const positionClass = `inapp-ai-${position}`;\n // Don't add a theme class for 'light' since it's the default\n const themeClass = theme && theme !== 'light' ? `inapp-ai-theme-${theme}` : '';\n const modeClass = isEmbedded\n ? 'inapp-ai-embedded'\n : isSidebar\n ? `inapp-ai-sidebar inapp-ai-${displayMode}`\n : isPanel\n ? `inapp-ai-panel inapp-ai-${displayMode}`\n : 'inapp-ai-popup';\n const foldedClass = isFolded ? 'inapp-ai-folded' : '';\n\n // Build custom button style\n const buttonStyle: React.CSSProperties = {\n ...(customStyles.buttonBackgroundColor && { background: customStyles.buttonBackgroundColor }),\n ...(customStyles.buttonTextColor && { color: customStyles.buttonTextColor }),\n ...(customStyles.buttonSize && {\n width: customStyles.buttonSize,\n height: customStyles.buttonSize,\n fontSize: `calc(${customStyles.buttonSize} * 0.5)`\n }),\n ...(customStyles.buttonBorderRadius && { borderRadius: customStyles.buttonBorderRadius }),\n ...(customStyles.boxShadow && { boxShadow: customStyles.boxShadow }),\n };\n\n // Build custom window style\n const windowStyle: React.CSSProperties = {\n ...(customStyles.windowWidth && !isSidebar && !isPanel && { width: customStyles.windowWidth }),\n ...(customStyles.windowHeight && !isSidebar && !isPanel && { height: customStyles.windowHeight }),\n ...(customStyles.windowBorderRadius && { borderRadius: customStyles.windowBorderRadius }),\n ...(customStyles.fontFamily && { fontFamily: customStyles.fontFamily }),\n ...(customStyles.fontSize && { fontSize: customStyles.fontSize }),\n ...(customStyles.sidebarWidth && isSidebar && !isFolded && { width: customStyles.sidebarWidth }),\n ...(customStyles.sidebarFoldedWidth && isSidebar && isFolded && { width: customStyles.sidebarFoldedWidth }),\n ...(isPanel && { width: panelWidth }),\n };\n\n // For embedded mode, return the chat window directly without fragment\n // This ensures proper flex layout inheritance from parent\n if (isEmbedded && isOpen) {\n return (\n <div\n ref={resizeRef}\n role=\"region\"\n aria-label=\"AI Assistant Chat\"\n className={`inapp-ai-window inapp-ai-embedded ${themeClass}`}\n style={windowStyle}\n >\n {/* Header (can be hidden via showHeader prop) */}\n {showHeader && (\n <div className=\"inapp-ai-header\" style={{\n ...(customStyles.headerBackground && { background: customStyles.headerBackground }),\n ...(customStyles.headerTextColor && { color: customStyles.headerTextColor }),\n }}>\n <div className=\"inapp-ai-header-title\">\n <span className=\"inapp-ai-header-icon\">{customStyles.buttonIcon || '🤖'}</span>\n <div>\n <h3>{customStyles.headerTitle || 'AI Assistant'}</h3>\n <p>\n {isConnected ? (\n <span className=\"inapp-ai-status-connected\">\n <span className=\"inapp-ai-status-dot\" />\n Connected\n </span>\n ) : (\n <span className=\"inapp-ai-status-disconnected\">\n <span className=\"inapp-ai-status-dot\" />\n Disconnected\n </span>\n )}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Error Banner */}\n {error && (\n <ErrorMessage error={error} onDismiss={() => setError(null)} />\n )}\n\n {/* Messages */}\n <div\n className=\"inapp-ai-messages\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Chat messages\"\n >\n {messages.length === 0 ? (\n <div className=\"inapp-ai-empty-state\" role=\"status\">\n <div className=\"inapp-ai-empty-icon\" aria-hidden=\"true\">💬</div>\n <h4>Start a conversation</h4>\n <p>How can I help you today?</p>\n </div>\n ) : (\n messages.map((msg) => (\n <div\n key={msg.id}\n className={`inapp-ai-message inapp-ai-message-${msg.role}`}\n role=\"article\"\n aria-label={`${msg.role === 'user' ? 'User' : 'Assistant'} message`}\n >\n <div className=\"inapp-ai-message-icon\" aria-hidden=\"true\">\n {msg.role === 'user' ? '👤' : '🤖'}\n </div>\n <div className=\"inapp-ai-message-content\">\n <div className=\"inapp-ai-message-text\" style={{\n ...(msg.role === 'user' && customStyles.userMessageBackground && { background: customStyles.userMessageBackground }),\n ...(msg.role === 'user' && customStyles.userMessageColor && { color: customStyles.userMessageColor }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageBackground && { background: customStyles.assistantMessageBackground }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageColor && { color: customStyles.assistantMessageColor }),\n ...(customStyles.borderRadius && { borderRadius: customStyles.borderRadius }),\n }}>\n {msg.role === 'assistant' ? (\n <ReactMarkdown\n components={{\n code: CodeBlock as any,\n }}\n >\n {msg.content}\n </ReactMarkdown>\n ) : (\n msg.content\n )}\n </div>\n <div className=\"inapp-ai-message-time\">\n {msg.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : ''}\n {msg.usage && (\n <span className=\"inapp-ai-message-tokens\">\n {' • '}{msg.usage.totalTokens} tokens\n </span>\n )}\n </div>\n </div>\n </div>\n ))\n )}\n {isLoading && (\n <div className=\"inapp-ai-message inapp-ai-message-assistant\">\n <div className=\"inapp-ai-message-icon\">\n <div style={{ animation: 'pulse 2s infinite' }}>🤖</div>\n </div>\n <div className=\"inapp-ai-message-content\">\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div className=\"inapp-ai-typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n <LoadingSkeleton />\n </div>\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input Area */}\n <div className=\"inapp-ai-input-area\" role=\"form\" aria-label=\"Message input\">\n {messages.length > 0 && (\n <button\n className=\"inapp-ai-clear-btn\"\n onClick={clearMessages}\n disabled={isLoading}\n aria-label=\"Clear conversation history\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">🗑️</span> Clear\n </button>\n )}\n <div className=\"inapp-ai-input-wrapper\">\n <input\n ref={inputRef}\n type=\"text\"\n className=\"inapp-ai-input\"\n placeholder={customStyles.inputPlaceholder || \"Type your message...\"}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n disabled={isLoading || !isConnected}\n aria-label=\"Message input\"\n aria-describedby=\"send-hint\"\n tabIndex={0}\n style={{\n ...(customStyles.inputBackground && { background: customStyles.inputBackground }),\n ...(customStyles.inputBorderColor && { borderColor: customStyles.inputBorderColor }),\n ...(customStyles.inputTextColor && { color: customStyles.inputTextColor }),\n }}\n />\n <button\n className=\"inapp-ai-send-btn\"\n onClick={sendMessage}\n disabled={isLoading || !isConnected || !inputValue.trim()}\n aria-label=\"Send message\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">{isLoading ? '⏳' : '⬆'}</span>\n <span className=\"sr-only\">\n {isLoading ? 'Sending...' : 'Send message'}\n </span>\n </button>\n <span id=\"send-hint\" className=\"sr-only\">\n Press Enter to send\n </span>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <>\n {/* Chat Button (only for popup mode, not embedded) */}\n {!isSidebar && !isPanel && !isEmbedded && (\n <button\n className={`inapp-ai-button ${positionClass} ${themeClass}`}\n style={buttonStyle}\n onClick={() => setIsOpen(!isOpen)}\n aria-label={isOpen ? \"Close AI Assistant\" : \"Open AI Assistant\"}\n aria-expanded={isOpen}\n aria-haspopup=\"dialog\"\n tabIndex={0}\n >\n {isOpen ? '✕' : (customStyles.buttonIcon || '🤖')}\n {!isConnected && (\n <span\n className=\"inapp-ai-offline-indicator\"\n role=\"status\"\n aria-label=\"Backend disconnected\"\n />\n )}\n </button>\n )}\n\n {/* Chat Window */}\n {isOpen && (\n <div\n ref={resizeRef}\n role={isEmbedded ? \"region\" : \"dialog\"}\n aria-label=\"AI Assistant Chat\"\n aria-modal={!isSidebar && !isPanel && !isEmbedded ? \"true\" : undefined}\n className={`inapp-ai-window ${modeClass} ${isSidebar || isPanel || isEmbedded ? '' : positionClass} ${themeClass} ${foldedClass}`}\n style={windowStyle}\n >\n {/* Resize Handle for Panel */}\n {isPanel && (\n <div\n className={`inapp-ai-resize-handle ${isLeftPanel ? 'inapp-ai-resize-handle-right' : 'inapp-ai-resize-handle-left'}`}\n onMouseDown={() => setIsResizing(true)}\n title=\"Drag to resize\"\n />\n )}\n\n {/* Folded State Content */}\n {(isSidebar || isPanel) && isFolded ? (\n <div\n className=\"inapp-ai-folded-content\"\n onClick={toggleFolded}\n style={{ cursor: 'pointer' }}\n title=\"Click to unfold\"\n >\n <div className=\"inapp-ai-folded-icon\">\n {customStyles.buttonIcon || '🤖'}\n </div>\n <div className=\"inapp-ai-folded-text\">\n AI\n </div>\n {messages.length > 0 && (\n <div className=\"inapp-ai-message-count\">\n {messages.length}\n </div>\n )}\n </div>\n ) : (\n <>\n {/* Header (can be hidden via showHeader prop) */}\n {showHeader && (\n <div className=\"inapp-ai-header\" style={{\n ...(customStyles.headerBackground && { background: customStyles.headerBackground }),\n ...(customStyles.headerTextColor && { color: customStyles.headerTextColor }),\n }}>\n <div className=\"inapp-ai-header-title\">\n <span className=\"inapp-ai-header-icon\">{customStyles.buttonIcon || '🤖'}</span>\n <div>\n <h3>{customStyles.headerTitle || 'AI Assistant'}</h3>\n <p>\n {isConnected ? (\n <span className=\"inapp-ai-status-connected\">\n <span className=\"inapp-ai-status-dot\" />\n Connected\n </span>\n ) : (\n <span className=\"inapp-ai-status-disconnected\">\n <span className=\"inapp-ai-status-dot\" />\n Disconnected\n </span>\n )}\n </p>\n </div>\n </div>\n {/* Fold button for panels (in header top right) */}\n {(isSidebar || isPanel) && (\n <button\n className=\"inapp-ai-header-fold-btn\"\n onClick={toggleFolded}\n aria-label={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n title={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n >\n {(isLeftSidebar || isLeftPanel) ? '◀' : '▶'}\n </button>\n )}\n {/* Close button for popup mode */}\n {!isSidebar && !isPanel && !isEmbedded && (\n <button\n className=\"inapp-ai-close-btn\"\n onClick={() => setIsOpen(false)}\n aria-label=\"Close\"\n >\n ✕\n </button>\n )}\n </div>\n )}\n\n {/* Error Banner */}\n {error && (\n <ErrorMessage error={error} onDismiss={() => setError(null)} />\n )}\n\n {/* Messages */}\n <div\n className=\"inapp-ai-messages\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Chat messages\"\n >\n {messages.length === 0 ? (\n <div className=\"inapp-ai-empty-state\" role=\"status\">\n <div className=\"inapp-ai-empty-icon\" aria-hidden=\"true\">💬</div>\n <h4>Start a conversation</h4>\n <p>How can I help you today?</p>\n </div>\n ) : (\n messages.map((msg) => (\n <div\n key={msg.id}\n className={`inapp-ai-message inapp-ai-message-${msg.role}`}\n role=\"article\"\n aria-label={`${msg.role === 'user' ? 'User' : 'Assistant'} message`}\n >\n <div className=\"inapp-ai-message-icon\" aria-hidden=\"true\">\n {msg.role === 'user' ? '👤' : '🤖'}\n </div>\n <div className=\"inapp-ai-message-content\">\n <div className=\"inapp-ai-message-text\" style={{\n ...(msg.role === 'user' && customStyles.userMessageBackground && { background: customStyles.userMessageBackground }),\n ...(msg.role === 'user' && customStyles.userMessageColor && { color: customStyles.userMessageColor }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageBackground && { background: customStyles.assistantMessageBackground }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageColor && { color: customStyles.assistantMessageColor }),\n ...(customStyles.borderRadius && { borderRadius: customStyles.borderRadius }),\n }}>\n {msg.role === 'assistant' ? (\n <ReactMarkdown\n components={{\n code: CodeBlock as any,\n }}\n >\n {msg.content}\n </ReactMarkdown>\n ) : (\n msg.content\n )}\n </div>\n <div className=\"inapp-ai-message-time\">\n {msg.timestamp?.toLocaleTimeString() || ''}\n {msg.usage && (\n <span className=\"inapp-ai-message-tokens\">\n {' • '}{msg.usage.totalTokens} tokens\n </span>\n )}\n </div>\n </div>\n </div>\n ))\n )}\n {isLoading && (\n <div className=\"inapp-ai-message inapp-ai-message-assistant\">\n <div className=\"inapp-ai-message-icon\">\n <div style={{ animation: 'pulse 2s infinite' }}>🤖</div>\n </div>\n <div className=\"inapp-ai-message-content\">\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div className=\"inapp-ai-typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n <LoadingSkeleton />\n </div>\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input Area */}\n <div className=\"inapp-ai-input-area\" role=\"form\" aria-label=\"Message input\">\n {messages.length > 0 && (\n <button\n className=\"inapp-ai-clear-btn\"\n onClick={clearMessages}\n disabled={isLoading}\n aria-label=\"Clear conversation history\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">🗑️</span> Clear\n </button>\n )}\n <div className=\"inapp-ai-input-wrapper\">\n <input\n ref={inputRef}\n type=\"text\"\n className=\"inapp-ai-input\"\n placeholder={customStyles.inputPlaceholder || \"Type your message...\"}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n disabled={isLoading || !isConnected}\n aria-label=\"Message input\"\n aria-describedby=\"send-hint\"\n tabIndex={0}\n style={{\n ...(customStyles.inputBackground && { background: customStyles.inputBackground }),\n ...(customStyles.inputBorderColor && { borderColor: customStyles.inputBorderColor }),\n ...(customStyles.inputTextColor && { color: customStyles.inputTextColor }),\n }}\n />\n <button\n className=\"inapp-ai-send-btn\"\n onClick={sendMessage}\n disabled={isLoading || !isConnected || !inputValue.trim()}\n aria-label=\"Send message\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">{isLoading ? '⏳' : '⬆'}</span>\n <span className=\"sr-only\">\n {isLoading ? 'Sending...' : 'Send message'}\n </span>\n </button>\n <span id=\"send-hint\" className=\"sr-only\">\n Press Enter to send\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n )}\n </>\n );\n}\n","import { useState, useCallback, useEffect, useRef } from 'react';\nimport type { Tool } from '../types';\n\n/**\n * Options for the useTools hook\n */\nexport interface UseToolsOptions {\n /**\n * Initial tools to register\n */\n initialTools?: Tool[];\n\n /**\n * Whether to automatically cleanup tools on unmount\n * @default true\n */\n autoCleanup?: boolean;\n}\n\n/**\n * Return value from the useTools hook\n */\nexport interface UseToolsReturn {\n /**\n * Current array of registered tools\n */\n tools: Tool[];\n\n /**\n * Register a new tool\n * @param tool - Tool to register\n * @throws {Error} If tool with same name already exists\n */\n registerTool: (tool: Tool) => void;\n\n /**\n * Unregister a tool by name\n * @param name - Name of the tool to unregister\n */\n unregisterTool: (name: string) => void;\n\n /**\n * Clear all registered tools\n */\n clearTools: () => void;\n\n /**\n * Check if a tool with the given name exists\n * @param name - Tool name to check\n * @returns true if tool exists, false otherwise\n */\n hasTool: (name: string) => boolean;\n}\n\n/**\n * Hook for managing tools dynamically in React components\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { tools, registerTool, unregisterTool } = useTools();\n * const [todos, setTodos] = useState([]);\n *\n * useEffect(() => {\n * registerTool({\n * name: 'addTodo',\n * description: 'Add a new todo',\n * parameters: {\n * type: 'object',\n * properties: {\n * task: { type: 'string' }\n * }\n * },\n * handler: async ({ task }) => {\n * setTodos(prev => [...prev, { id: Date.now(), text: task }]);\n * return { success: true };\n * }\n * });\n *\n * return () => unregisterTool('addTodo');\n * }, [registerTool, unregisterTool, setTodos]);\n *\n * return <InAppAI endpoint=\"...\" tools={tools} />;\n * }\n * ```\n *\n * @param options - Configuration options\n * @returns Tool management functions and current tools array\n */\nexport function useTools(options: UseToolsOptions = {}): UseToolsReturn {\n const { initialTools = [], autoCleanup = true } = options;\n\n // Use ref to track tool names for validation\n const toolNamesRef = useRef<Set<string>>(new Set());\n\n // State for tools array\n const [tools, setTools] = useState<Tool[]>(() => {\n // Initialize with initial tools\n initialTools.forEach(tool => {\n if (toolNamesRef.current.has(tool.name)) {\n console.warn(\n `[useTools] Duplicate tool name in initialTools: \"${tool.name}\". ` +\n `Only the first occurrence will be used.`\n );\n } else {\n toolNamesRef.current.add(tool.name);\n }\n });\n\n return initialTools.filter((tool, index) => {\n // Filter out duplicates\n return initialTools.findIndex(t => t.name === tool.name) === index;\n });\n });\n\n /**\n * Register a new tool\n */\n const registerTool = useCallback((tool: Tool) => {\n if (!tool.name) {\n throw new Error('[useTools] Tool must have a name');\n }\n\n if (toolNamesRef.current.has(tool.name)) {\n console.warn(\n `[useTools] Tool \"${tool.name}\" is already registered. ` +\n `Use unregisterTool() first if you want to replace it.`\n );\n return;\n }\n\n // Validate tool structure\n if (!tool.description) {\n console.warn(`[useTools] Tool \"${tool.name}\" is missing description`);\n }\n\n if (!tool.parameters) {\n console.warn(`[useTools] Tool \"${tool.name}\" is missing parameters schema`);\n }\n\n if (typeof tool.handler !== 'function') {\n throw new Error(`[useTools] Tool \"${tool.name}\" must have a handler function`);\n }\n\n toolNamesRef.current.add(tool.name);\n setTools(prev => [...prev, tool]);\n }, []);\n\n /**\n * Unregister a tool by name\n */\n const unregisterTool = useCallback((name: string) => {\n if (!toolNamesRef.current.has(name)) {\n console.warn(`[useTools] Tool \"${name}\" is not registered`);\n return;\n }\n\n toolNamesRef.current.delete(name);\n setTools(prev => prev.filter(tool => tool.name !== name));\n }, []);\n\n /**\n * Clear all tools\n */\n const clearTools = useCallback(() => {\n toolNamesRef.current.clear();\n setTools([]);\n }, []);\n\n /**\n * Check if a tool exists\n */\n const hasTool = useCallback((name: string): boolean => {\n return toolNamesRef.current.has(name);\n }, []);\n\n /**\n * Cleanup on unmount if autoCleanup is enabled\n */\n useEffect(() => {\n if (autoCleanup) {\n return () => {\n toolNamesRef.current.clear();\n };\n }\n }, [autoCleanup]);\n\n return {\n tools,\n registerTool,\n unregisterTool,\n clearTools,\n hasTool,\n };\n}\n","import React, { createContext, useContext, useState, useCallback, useMemo } from 'react';\nimport type { Tool } from '../types';\n\n/**\n * Tool registry interface\n */\nexport interface ToolRegistry {\n /**\n * Register tools under a namespace\n * @param namespace - Unique namespace identifier\n * @param tools - Array of tools to register\n */\n register(namespace: string, tools: Tool[]): void;\n\n /**\n * Unregister all tools for a namespace\n * @param namespace - Namespace to unregister\n */\n unregister(namespace: string): void;\n\n /**\n * Get tools for a specific namespace\n * @param namespace - Namespace to query\n * @returns Array of tools for the namespace, or empty array if not found\n */\n getTools(namespace: string): Tool[];\n\n /**\n * Get all registered tools from all namespaces\n * @returns Combined array of all tools\n */\n getAllTools(): Tool[];\n\n /**\n * Clear all registered tools from all namespaces\n */\n clear(): void;\n\n /**\n * Get all registered namespace names\n * @returns Array of namespace names\n */\n getNamespaces(): string[];\n}\n\n/**\n * Context for the tool registry\n */\nconst ToolRegistryContext = createContext<ToolRegistry | null>(null);\n\n/**\n * Props for ToolRegistryProvider\n */\nexport interface ToolRegistryProviderProps {\n /**\n * Child components\n */\n children: React.ReactNode;\n\n /**\n * Initial tools organized by namespace\n */\n initialTools?: Record<string, Tool[]>;\n}\n\n/**\n * Provider component for global tool registry\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <ToolRegistryProvider>\n * <MyApp />\n * </ToolRegistryProvider>\n * );\n * }\n * ```\n */\nexport function ToolRegistryProvider({\n children,\n initialTools = {},\n}: ToolRegistryProviderProps): React.ReactElement {\n // State: Map of namespace -> tools\n const [toolsMap, setToolsMap] = useState<Map<string, Tool[]>>(\n () => new Map(Object.entries(initialTools))\n );\n\n /**\n * Register tools under a namespace\n */\n const register = useCallback((namespace: string, tools: Tool[]) => {\n // Validate namespace\n if (!namespace || typeof namespace !== 'string') {\n throw new Error('[ToolRegistry] Namespace must be a non-empty string');\n }\n\n if (!/^[a-zA-Z0-9_-]+$/.test(namespace)) {\n throw new Error(\n `[ToolRegistry] Invalid namespace \"${namespace}\". ` +\n `Use only alphanumeric characters, hyphens, and underscores.`\n );\n }\n\n // Validate tools\n if (!Array.isArray(tools)) {\n throw new Error('[ToolRegistry] Tools must be an array');\n }\n\n // Check for duplicate tool names within the namespace\n const toolNames = new Set<string>();\n tools.forEach(tool => {\n if (toolNames.has(tool.name)) {\n console.warn(\n `[ToolRegistry] Duplicate tool name in namespace \"${namespace}\": \"${tool.name}\"`\n );\n }\n toolNames.add(tool.name);\n });\n\n setToolsMap(prev => {\n const next = new Map(prev);\n next.set(namespace, tools);\n return next;\n });\n }, []);\n\n /**\n * Unregister a namespace\n */\n const unregister = useCallback((namespace: string) => {\n setToolsMap(prev => {\n if (!prev.has(namespace)) {\n console.warn(`[ToolRegistry] Namespace \"${namespace}\" is not registered`);\n return prev;\n }\n\n const next = new Map(prev);\n next.delete(namespace);\n return next;\n });\n }, []);\n\n /**\n * Get tools for a specific namespace\n */\n const getTools = useCallback(\n (namespace: string): Tool[] => {\n return toolsMap.get(namespace) || [];\n },\n [toolsMap]\n );\n\n /**\n * Get all tools from all namespaces\n */\n const getAllTools = useCallback((): Tool[] => {\n const allTools: Tool[] = [];\n const seenNames = new Set<string>();\n\n // Iterate through namespaces\n for (const [namespace, tools] of toolsMap.entries()) {\n for (const tool of tools) {\n // Warn about name conflicts across namespaces\n if (seenNames.has(tool.name)) {\n console.warn(\n `[ToolRegistry] Tool name conflict: \"${tool.name}\" exists in multiple namespaces. ` +\n `Only the first occurrence will be used.`\n );\n continue;\n }\n\n seenNames.add(tool.name);\n allTools.push(tool);\n }\n }\n\n return allTools;\n }, [toolsMap]);\n\n /**\n * Clear all tools\n */\n const clear = useCallback(() => {\n setToolsMap(new Map());\n }, []);\n\n /**\n * Get all namespace names\n */\n const getNamespaces = useCallback((): string[] => {\n return Array.from(toolsMap.keys());\n }, [toolsMap]);\n\n // Memoize the registry value\n const registry = useMemo<ToolRegistry>(\n () => ({\n register,\n unregister,\n getTools,\n getAllTools,\n clear,\n getNamespaces,\n }),\n [register, unregister, getTools, getAllTools, clear, getNamespaces]\n );\n\n return (\n <ToolRegistryContext.Provider value={registry}>\n {children}\n </ToolRegistryContext.Provider>\n );\n}\n\n/**\n * Hook to access the tool registry\n *\n * @example\n * ```tsx\n * function TodoPage() {\n * const registry = useToolRegistry();\n * const [todos, setTodos] = useState([]);\n *\n * useEffect(() => {\n * registry.register('todos', [\n * {\n * name: 'addTodo',\n * description: 'Add a todo',\n * parameters: { type: 'object', properties: { task: { type: 'string' } } },\n * handler: async ({ task }) => {\n * setTodos(prev => [...prev, { id: Date.now(), text: task }]);\n * return { success: true };\n * }\n * }\n * ]);\n *\n * return () => registry.unregister('todos');\n * }, [registry, todos, setTodos]);\n *\n * return <TodoList />;\n * }\n *\n * function ChatWidget() {\n * const registry = useToolRegistry();\n * const allTools = registry.getAllTools();\n *\n * return <InAppAI endpoint=\"...\" tools={allTools} />;\n * }\n * ```\n *\n * @throws {Error} If used outside of ToolRegistryProvider\n * @returns Tool registry instance\n */\nexport function useToolRegistry(): ToolRegistry {\n const registry = useContext(ToolRegistryContext);\n\n if (!registry) {\n throw new Error(\n '[useToolRegistry] must be used within a ToolRegistryProvider. ' +\n 'Wrap your component tree with <ToolRegistryProvider>.'\n );\n }\n\n return registry;\n}\n"],"mappings":"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","names":["index_exports","__export","InAppAI","ToolRegistryProvider","useToolRegistry","useTools","__toCommonJS","import_react","import_react_markdown","import_react_syntax_highlighter","import_prism","import_jsx_runtime","import_meta","LoadingSkeleton","ErrorMessage","error","onDismiss","errorInfo","msg","CodeBlock","inline","className","children","props","copied","setCopied","match","language","codeString","handleCopy","SyntaxHighlighter","InAppAI","endpoint","agentId","position","displayMode","defaultFolded","theme","context","customStyles","tools","panelMinWidth","panelMaxWidth","panelDefaultWidth","onPanelResize","externalConversationId","externalMessages","onMessagesChange","showHeader","authToken","maxToolRounds","apiEndpoint","getAuthToken","buildHeaders","headers","token","externalMessagesRef","messages","setMessages","updater","currentMessages","newMessages","isOpen","setIsOpen","isFolded","setIsFolded","inputValue","setInputValue","isLoading","setIsLoading","isConnected","setIsConnected","setError","panelWidth","setPanelWidth","isResizing","setIsResizing","messagesEndRef","internalConversationId","conversationId","resizeRef","inputRef","isEmbedded","isSidebar","isPanel","isLeftSidebar","isLeftPanel","handleMouseMove","e","container","containerRect","newWidth","widthPercent","minPercent","maxPercent","newWidthStr","handleMouseUp","sendMessage","message","userMessage","prev","getContext","toolDefinitions","name","description","parameters","response","currentData","round","toolResultsMessage","toolCall","toolName","toolArgs","tool","t","r","idx","isLastAllowedRound","followUpResponse","assistantMessage","err","handleKeyPress","clearMessages","toggleFolded","positionClass","themeClass","modeClass","foldedClass","buttonStyle","windowStyle","ReactMarkdown","import_react","useTools","options","initialTools","autoCleanup","toolNamesRef","tools","setTools","tool","index","t","registerTool","prev","unregisterTool","name","clearTools","hasTool","import_react","import_jsx_runtime","ToolRegistryContext","ToolRegistryProvider","children","initialTools","toolsMap","setToolsMap","register","namespace","tools","toolNames","tool","prev","next","unregister","getTools","getAllTools","allTools","seenNames","clear","getNamespaces","registry","useToolRegistry"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/components/InAppAI.tsx","../src/hooks/useTools.ts","../src/hooks/useToolRegistry.tsx"],"sourcesContent":["export { InAppAI } from './components/InAppAI';\nexport type {\n CustomStyles,\n Tool,\n ToolAction,\n Message,\n InAppAIProps,\n} from './types';\n\n// Hooks\nexport {\n useTools,\n useToolRegistry,\n ToolRegistryProvider,\n} from './hooks';\nexport type {\n UseToolsOptions,\n UseToolsReturn,\n ToolRegistry,\n ToolRegistryProviderProps,\n} from './hooks';\n","import { useState, useEffect, useRef } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';\nimport type { CustomStyles, Tool, InAppAIProps, Message } from '../types';\nimport './themes.css';\nimport './InAppAI.css';\n\n// Internal message type (extends exported Message with required timestamp)\ninterface InternalMessage extends Message {\n timestamp: Date;\n}\n\n// Loading skeleton component\nfunction LoadingSkeleton() {\n return (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '8px',\n padding: '12px 16px',\n background: 'var(--inapp-ai-assistant-bg)',\n borderRadius: 'var(--inapp-ai-border-radius)',\n }}\n >\n <div\n style={{\n height: '14px',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '90%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.1s',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '75%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.2s',\n borderRadius: '4px',\n }}\n />\n <style>{`\n @keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n `}</style>\n </div>\n );\n}\n\n// Error message component with enhanced display\ninterface ErrorMessageProps {\n error: string;\n onDismiss: () => void;\n}\n\nfunction ErrorMessage({ error, onDismiss }: ErrorMessageProps) {\n // Determine error type based on message\n const getErrorType = (msg: string) => {\n if (msg.includes('not responding') || msg.includes('connection') || msg.includes('network')) {\n return { type: 'connection', icon: '🔌', title: 'Connection Error' };\n }\n if (msg.includes('timeout')) {\n return { type: 'timeout', icon: '⏱️', title: 'Request Timeout' };\n }\n if (msg.includes('rate limit')) {\n return { type: 'rateLimit', icon: '🚦', title: 'Rate Limit' };\n }\n if (msg.includes('authentication') || msg.includes('unauthorized')) {\n return { type: 'auth', icon: '🔒', title: 'Authentication Error' };\n }\n return { type: 'generic', icon: '⚠️', title: 'Error' };\n };\n\n const errorInfo = getErrorType(error);\n\n return (\n <div\n className=\"inapp-ai-error-banner\"\n role=\"alert\"\n aria-live=\"assertive\"\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n gap: '12px',\n padding: '14px 16px',\n background: 'linear-gradient(135deg, #fff3cd 0%, #ffe9a6 100%)',\n borderLeft: '4px solid #ff9800',\n borderRadius: '0',\n }}\n >\n <span style={{ fontSize: '20px' }}>{errorInfo.icon}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontWeight: 600, marginBottom: '4px', color: '#856404' }}>\n {errorInfo.title}\n </div>\n <div style={{ fontSize: '13px', color: '#856404', wordBreak: 'break-word' }}>\n {error}\n </div>\n {errorInfo.type === 'connection' && (\n <div style={{ fontSize: '12px', marginTop: '6px', color: '#997404' }}>\n 💡 Make sure the backend server is running on the correct port\n </div>\n )}\n </div>\n <button\n onClick={onDismiss}\n style={{\n background: 'none',\n border: 'none',\n color: '#856404',\n cursor: 'pointer',\n padding: '4px 8px',\n fontSize: '16px',\n lineHeight: 1,\n }}\n aria-label=\"Dismiss error\"\n >\n ✕\n </button>\n </div>\n );\n}\n\n// Code block component with syntax highlighting and copy button\ninterface CodeBlockProps {\n inline?: boolean;\n className?: string;\n children?: React.ReactNode;\n}\n\nfunction CodeBlock({ inline, className, children, ...props }: CodeBlockProps) {\n const [copied, setCopied] = useState(false);\n\n // Extract language from className (format: language-js, language-python, etc.)\n const match = /language-(\\w+)/.exec(className || '');\n const language = match ? match[1] : '';\n\n const codeString = String(children).replace(/\\n$/, '');\n\n const handleCopy = () => {\n navigator.clipboard.writeText(codeString);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n // Inline code (backticks)\n if (inline) {\n return <code className={className} {...props}>{children}</code>;\n }\n\n // Code block with syntax highlighting\n return (\n <div style={{ position: 'relative', marginTop: '8px', marginBottom: '8px' }}>\n <button\n onClick={handleCopy}\n style={{\n position: 'absolute',\n top: '8px',\n right: '8px',\n padding: '4px 8px',\n background: copied ? '#28a745' : 'rgba(255, 255, 255, 0.1)',\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n zIndex: 1,\n transition: 'background 0.2s',\n }}\n aria-label=\"Copy code\"\n >\n {copied ? '✓ Copied!' : '📋 Copy'}\n </button>\n {/* @ts-ignore - React 18 JSX type compatibility */}\n <SyntaxHighlighter\n language={language || 'text'}\n style={vscDarkPlus}\n customStyle={{\n borderRadius: '8px',\n padding: '16px',\n fontSize: '13px',\n marginTop: 0,\n marginBottom: 0,\n }}\n {...props}\n >\n {codeString}\n </SyntaxHighlighter>\n </div>\n );\n}\n\nexport function InAppAI({\n endpoint,\n agentId,\n position = 'bottom-right',\n displayMode = 'popup',\n defaultFolded = false,\n theme = 'light',\n context,\n customStyles = {},\n tools = [],\n panelMinWidth = '20%',\n panelMaxWidth = '33.33%',\n panelDefaultWidth = '25%',\n onPanelResize,\n // Controlled mode props\n conversationId: externalConversationId,\n messages: externalMessages,\n onMessagesChange,\n showHeader = true,\n // Authentication\n authToken,\n // Tool execution\n maxToolRounds = 10,\n}: InAppAIProps) {\n // Require controlled mode - messages and onMessagesChange are required\n if (externalMessages === undefined || onMessagesChange === undefined) {\n throw new Error(\n 'InAppAI requires controlled mode. Please provide both `messages` and `onMessagesChange` props. ' +\n 'See documentation for usage examples.'\n );\n }\n\n // Determine API endpoint (environment variable > prop > default)\n const apiEndpoint = endpoint ||\n (typeof process !== 'undefined' && (process as any).env?.REACT_APP_INAPPAI_ENDPOINT) ||\n (typeof (import.meta as any).env !== 'undefined' && (import.meta as any).env?.VITE_INAPPAI_ENDPOINT) ||\n 'https://api.inappai.com/api';\n\n // Helper to get auth token (supports both static string and function)\n const getAuthToken = (): string | null => {\n if (!authToken) return null;\n const token = typeof authToken === 'function' ? authToken() : authToken;\n return token || null;\n };\n\n // Helper to build headers with optional Authorization\n const buildHeaders = (): Record<string, string> => {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const token = getAuthToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n return headers;\n };\n\n // Keep a ref to the latest external messages to avoid stale closures\n const externalMessagesRef = useRef<Message[]>(externalMessages);\n useEffect(() => {\n externalMessagesRef.current = externalMessages;\n }, [externalMessages]);\n\n // Messages from props, setMessages calls onMessagesChange\n const messages = externalMessages;\n const setMessages = (updater: Message[] | ((prev: Message[]) => Message[])) => {\n const currentMessages = externalMessagesRef.current;\n const newMessages = typeof updater === 'function' ? updater(currentMessages) : updater;\n externalMessagesRef.current = newMessages; // Update ref immediately for chained calls\n onMessagesChange(newMessages);\n };\n\n const [isOpen, setIsOpen] = useState(displayMode.startsWith('sidebar') || displayMode.startsWith('panel') || displayMode === 'embedded');\n const [isFolded, setIsFolded] = useState(defaultFolded && (displayMode.startsWith('sidebar') || displayMode.startsWith('panel')));\n const [inputValue, setInputValue] = useState('');\n const [isLoading, setIsLoading] = useState(false);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [panelWidth, setPanelWidth] = useState(panelDefaultWidth);\n const [isResizing, setIsResizing] = useState(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const internalConversationId = useRef(`react-demo-${Date.now()}`);\n const conversationId = externalConversationId || internalConversationId.current;\n const resizeRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const isEmbedded = displayMode === 'embedded';\n const isSidebar = displayMode.startsWith('sidebar');\n const isPanel = displayMode.startsWith('panel');\n const isLeftSidebar = displayMode === 'sidebar-left';\n // const isRightSidebar = displayMode === 'sidebar-right';\n const isLeftPanel = displayMode === 'panel-left';\n // const isRightPanel = displayMode === 'panel-right';\n\n // For sidebar, panel, and embedded mode, always stay open\n useEffect(() => {\n if (isSidebar || isPanel || isEmbedded) {\n setIsOpen(true);\n }\n }, [isSidebar, isPanel, isEmbedded]);\n\n // Panel resize handlers\n useEffect(() => {\n if (!isPanel || !isResizing) return;\n\n const handleMouseMove = (e: MouseEvent) => {\n const container = resizeRef.current?.parentElement;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n let newWidth: number;\n\n if (isLeftPanel) {\n newWidth = e.clientX;\n } else {\n newWidth = containerRect.width - e.clientX;\n }\n\n // Convert to percentage\n const widthPercent = (newWidth / containerRect.width) * 100;\n\n // Parse min/max widths\n const minPercent = parseFloat(panelMinWidth);\n const maxPercent = parseFloat(panelMaxWidth);\n\n // Clamp width between min and max\n const clampedPercent = Math.max(minPercent, Math.min(maxPercent, widthPercent));\n const newWidthStr = `${clampedPercent}%`;\n\n setPanelWidth(newWidthStr);\n if (onPanelResize) {\n onPanelResize(newWidth);\n }\n };\n\n const handleMouseUp = () => {\n setIsResizing(false);\n };\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n }, [isResizing, isPanel, isLeftPanel, panelMinWidth, panelMaxWidth, onPanelResize]);\n\n // Check backend connection once on mount\n useEffect(() => {\n const checkConnection = async () => {\n try {\n const headers: Record<string, string> = {};\n const token = getAuthToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n const response = await fetch(`${apiEndpoint}/${agentId}/health`, { headers });\n if (response.ok) {\n setIsConnected(true);\n setError(null);\n } else {\n setError('Backend not responding');\n }\n } catch (err) {\n setError('Failed to connect to backend');\n setIsConnected(false);\n }\n };\n\n checkConnection();\n }, [apiEndpoint, agentId]);\n\n // Auto-scroll to bottom\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n // Refocus input after loading completes\n useEffect(() => {\n if (!isLoading && isConnected) {\n // Small delay to ensure DOM has updated\n setTimeout(() => {\n inputRef.current?.focus();\n }, 100);\n }\n }, [isLoading, isConnected]);\n\n const sendMessage = async () => {\n const message = inputValue.trim();\n if (!message || isLoading) return;\n\n // Add user message\n const userMessage: Message = {\n id: `${Date.now()}-user`,\n role: 'user',\n content: message,\n timestamp: new Date(),\n };\n\n setMessages(prev => [...prev, userMessage]);\n setInputValue('');\n setIsLoading(true);\n setError(null);\n\n try {\n // Helper to get fresh context (supports both static and function contexts)\n const getContext = () => typeof context === 'function' ? context() : context;\n\n // Prepare tool definitions (without handlers) for backend in OpenAI format\n const toolDefinitions = tools.map(({ name, description, parameters }) => ({\n type: 'function' as const,\n function: {\n name,\n description,\n parameters,\n },\n }));\n\n // Build compact conversation history with tool action context\n const buildMessagesPayload = () => messages.map(m => {\n const msg: { role: string; content: string } = {\n role: m.role,\n content: m.content,\n };\n if (m.toolActions && m.toolActions.length > 0) {\n const actionsSummary = m.toolActions\n .map(a => `${a.tool}(${JSON.stringify(a.args)}) → ${JSON.stringify(a.result)}`)\n .join(', ');\n msg.content += `\\n\\n[Tool actions: ${actionsSummary}]`;\n }\n return msg;\n });\n\n const response = await fetch(`${apiEndpoint}/${agentId}/chat`, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({\n message,\n messages: buildMessagesPayload(),\n conversationId,\n context: getContext(),\n tools: toolDefinitions.length > 0 ? toolDefinitions : undefined,\n disableCache: false,\n }),\n });\n\n if (!response.ok) {\n throw new Error('Failed to get response');\n }\n\n const data = await response.json();\n\n // Iterative tool execution loop\n // The AI may return tool calls that, once executed, lead to more tool calls.\n // We loop until the AI returns a text-only response or we hit maxToolRounds.\n let currentData = data;\n let round = 0;\n const allToolActions: Array<{ tool: string; args: Record<string, any>; result: any }> = [];\n\n while (\n currentData.toolCalls &&\n currentData.toolCalls.length > 0 &&\n round < maxToolRounds\n ) {\n round++;\n\n // Execute all tool calls in parallel\n const results = await Promise.all(\n currentData.toolCalls.map(async (toolCall: any) => {\n const toolName = toolCall.function?.name || toolCall.name;\n const toolArgs = toolCall.function?.arguments\n ? JSON.parse(toolCall.function.arguments)\n : toolCall.parameters;\n\n const tool = tools.find(t => t.name === toolName);\n if (!tool) {\n return { success: false, error: `Tool '${toolName}' not found` };\n }\n try {\n const result = await Promise.resolve(tool.handler(toolArgs));\n return result;\n } catch (error: any) {\n console.error(`Tool '${tool.name}' failed:`, error);\n return { success: false, error: error.message };\n }\n })\n );\n\n // Collect tool actions for conversation memory\n results.forEach((result: any, idx: number) => {\n const toolCall = currentData.toolCalls[idx];\n const toolName = toolCall.function?.name || toolCall.name;\n const toolArgs = toolCall.function?.arguments\n ? JSON.parse(toolCall.function.arguments)\n : toolCall.parameters || {};\n allToolActions.push({ tool: toolName, args: toolArgs, result });\n });\n\n // Format tool results with clear completion markers\n const toolResultLines = results\n .map((r: any, idx: number) => {\n const toolCall = currentData.toolCalls[idx];\n const toolName = toolCall.function?.name || toolCall.name;\n return `Tool \"${toolName}\" result: ${JSON.stringify(r)}`;\n })\n .join('\\n');\n\n const toolResultsMessage = `[TOOL EXECUTION COMPLETE - Round ${round}]\\n` +\n `The following ${results.length} tool call(s) have been executed successfully. Do NOT re-execute them.\\n` +\n toolResultLines + '\\n' +\n `If all requested actions are complete, respond to the user with a summary. Only make additional tool calls if new/different actions are needed.`;\n\n // On last allowed round, omit tools to force a text-only response\n const isLastAllowedRound = round >= maxToolRounds;\n\n // Send tool results back with tools, fresh context, and conversation history\n const followUpResponse = await fetch(`${apiEndpoint}/${agentId}/chat`, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({\n message: toolResultsMessage,\n messages: buildMessagesPayload(),\n conversationId,\n context: getContext(),\n tools: isLastAllowedRound ? undefined : (toolDefinitions.length > 0 ? toolDefinitions : undefined),\n disableCache: false,\n }),\n });\n\n if (!followUpResponse.ok) {\n throw new Error('Failed to get AI response for tool results');\n }\n\n currentData = await followUpResponse.json();\n }\n\n // Display final text response\n const assistantMessage: Message = {\n id: `${Date.now()}-assistant`,\n role: 'assistant',\n content: currentData.message || 'I executed the tools successfully.',\n timestamp: new Date(),\n usage: currentData.usage,\n toolActions: allToolActions.length > 0 ? allToolActions : undefined,\n };\n\n setMessages(prev => [...prev, assistantMessage]);\n } catch (err) {\n console.error('❌ Error:', err);\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleKeyPress = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n sendMessage();\n }\n };\n\n const clearMessages = () => {\n setMessages([]);\n };\n\n const toggleFolded = () => {\n setIsFolded(!isFolded);\n };\n\n const positionClass = `inapp-ai-${position}`;\n // Don't add a theme class for 'light' since it's the default\n const themeClass = theme && theme !== 'light' ? `inapp-ai-theme-${theme}` : '';\n const modeClass = isEmbedded\n ? 'inapp-ai-embedded'\n : isSidebar\n ? `inapp-ai-sidebar inapp-ai-${displayMode}`\n : isPanel\n ? `inapp-ai-panel inapp-ai-${displayMode}`\n : 'inapp-ai-popup';\n const foldedClass = isFolded ? 'inapp-ai-folded' : '';\n\n // Build custom button style\n const buttonStyle: React.CSSProperties = {\n ...(customStyles.buttonBackgroundColor && { background: customStyles.buttonBackgroundColor }),\n ...(customStyles.buttonTextColor && { color: customStyles.buttonTextColor }),\n ...(customStyles.buttonSize && {\n width: customStyles.buttonSize,\n height: customStyles.buttonSize,\n fontSize: `calc(${customStyles.buttonSize} * 0.5)`\n }),\n ...(customStyles.buttonBorderRadius && { borderRadius: customStyles.buttonBorderRadius }),\n ...(customStyles.boxShadow && { boxShadow: customStyles.boxShadow }),\n };\n\n // Build custom window style\n const windowStyle: React.CSSProperties = {\n ...(customStyles.windowWidth && !isSidebar && !isPanel && { width: customStyles.windowWidth }),\n ...(customStyles.windowHeight && !isSidebar && !isPanel && { height: customStyles.windowHeight }),\n ...(customStyles.windowBorderRadius && { borderRadius: customStyles.windowBorderRadius }),\n ...(customStyles.fontFamily && { fontFamily: customStyles.fontFamily }),\n ...(customStyles.fontSize && { fontSize: customStyles.fontSize }),\n ...(customStyles.sidebarWidth && isSidebar && !isFolded && { width: customStyles.sidebarWidth }),\n ...(customStyles.sidebarFoldedWidth && isSidebar && isFolded && { width: customStyles.sidebarFoldedWidth }),\n ...(isPanel && { width: panelWidth }),\n };\n\n // For embedded mode, return the chat window directly without fragment\n // This ensures proper flex layout inheritance from parent\n if (isEmbedded && isOpen) {\n return (\n <div\n ref={resizeRef}\n role=\"region\"\n aria-label=\"AI Assistant Chat\"\n className={`inapp-ai-window inapp-ai-embedded ${themeClass}`}\n style={windowStyle}\n >\n {/* Header (can be hidden via showHeader prop) */}\n {showHeader && (\n <div className=\"inapp-ai-header\" style={{\n ...(customStyles.headerBackground && { background: customStyles.headerBackground }),\n ...(customStyles.headerTextColor && { color: customStyles.headerTextColor }),\n }}>\n <div className=\"inapp-ai-header-title\">\n <span className=\"inapp-ai-header-icon\">{customStyles.buttonIcon || '🤖'}</span>\n <div>\n <h3>{customStyles.headerTitle || 'AI Assistant'}</h3>\n <p>\n {isConnected ? (\n <span className=\"inapp-ai-status-connected\">\n <span className=\"inapp-ai-status-dot\" />\n Connected\n </span>\n ) : (\n <span className=\"inapp-ai-status-disconnected\">\n <span className=\"inapp-ai-status-dot\" />\n Disconnected\n </span>\n )}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Error Banner */}\n {error && (\n <ErrorMessage error={error} onDismiss={() => setError(null)} />\n )}\n\n {/* Messages */}\n <div\n className=\"inapp-ai-messages\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Chat messages\"\n >\n {messages.length === 0 ? (\n <div className=\"inapp-ai-empty-state\" role=\"status\">\n <div className=\"inapp-ai-empty-icon\" aria-hidden=\"true\">💬</div>\n <h4>Start a conversation</h4>\n <p>How can I help you today?</p>\n </div>\n ) : (\n messages.map((msg) => (\n <div\n key={msg.id}\n className={`inapp-ai-message inapp-ai-message-${msg.role}`}\n role=\"article\"\n aria-label={`${msg.role === 'user' ? 'User' : 'Assistant'} message`}\n >\n <div className=\"inapp-ai-message-icon\" aria-hidden=\"true\">\n {msg.role === 'user' ? '👤' : '🤖'}\n </div>\n <div className=\"inapp-ai-message-content\">\n <div className=\"inapp-ai-message-text\" style={{\n ...(msg.role === 'user' && customStyles.userMessageBackground && { background: customStyles.userMessageBackground }),\n ...(msg.role === 'user' && customStyles.userMessageColor && { color: customStyles.userMessageColor }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageBackground && { background: customStyles.assistantMessageBackground }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageColor && { color: customStyles.assistantMessageColor }),\n ...(customStyles.borderRadius && { borderRadius: customStyles.borderRadius }),\n }}>\n {msg.role === 'assistant' ? (\n <ReactMarkdown\n components={{\n code: CodeBlock as any,\n }}\n >\n {msg.content}\n </ReactMarkdown>\n ) : (\n msg.content\n )}\n </div>\n <div className=\"inapp-ai-message-time\">\n {msg.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : ''}\n {msg.usage && (\n <span className=\"inapp-ai-message-tokens\">\n {' • '}{msg.usage.totalTokens} tokens\n </span>\n )}\n </div>\n </div>\n </div>\n ))\n )}\n {isLoading && (\n <div className=\"inapp-ai-message inapp-ai-message-assistant\">\n <div className=\"inapp-ai-message-icon\">\n <div style={{ animation: 'pulse 2s infinite' }}>🤖</div>\n </div>\n <div className=\"inapp-ai-message-content\">\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div className=\"inapp-ai-typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n <LoadingSkeleton />\n </div>\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input Area */}\n <div className=\"inapp-ai-input-area\" role=\"form\" aria-label=\"Message input\">\n {messages.length > 0 && (\n <button\n className=\"inapp-ai-clear-btn\"\n onClick={clearMessages}\n disabled={isLoading}\n aria-label=\"Clear conversation history\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">🗑️</span> Clear\n </button>\n )}\n <div className=\"inapp-ai-input-wrapper\">\n <input\n ref={inputRef}\n type=\"text\"\n className=\"inapp-ai-input\"\n placeholder={customStyles.inputPlaceholder || \"Type your message...\"}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n disabled={isLoading || !isConnected}\n aria-label=\"Message input\"\n aria-describedby=\"send-hint\"\n tabIndex={0}\n style={{\n ...(customStyles.inputBackground && { background: customStyles.inputBackground }),\n ...(customStyles.inputBorderColor && { borderColor: customStyles.inputBorderColor }),\n ...(customStyles.inputTextColor && { color: customStyles.inputTextColor }),\n }}\n />\n <button\n className=\"inapp-ai-send-btn\"\n onClick={sendMessage}\n disabled={isLoading || !isConnected || !inputValue.trim()}\n aria-label=\"Send message\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">{isLoading ? '⏳' : '⬆'}</span>\n <span className=\"sr-only\">\n {isLoading ? 'Sending...' : 'Send message'}\n </span>\n </button>\n <span id=\"send-hint\" className=\"sr-only\">\n Press Enter to send\n </span>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <>\n {/* Chat Button (only for popup mode, not embedded) */}\n {!isSidebar && !isPanel && !isEmbedded && (\n <button\n className={`inapp-ai-button ${positionClass} ${themeClass}`}\n style={buttonStyle}\n onClick={() => setIsOpen(!isOpen)}\n aria-label={isOpen ? \"Close AI Assistant\" : \"Open AI Assistant\"}\n aria-expanded={isOpen}\n aria-haspopup=\"dialog\"\n tabIndex={0}\n >\n {isOpen ? '✕' : (customStyles.buttonIcon || '🤖')}\n {!isConnected && (\n <span\n className=\"inapp-ai-offline-indicator\"\n role=\"status\"\n aria-label=\"Backend disconnected\"\n />\n )}\n </button>\n )}\n\n {/* Chat Window */}\n {isOpen && (\n <div\n ref={resizeRef}\n role={isEmbedded ? \"region\" : \"dialog\"}\n aria-label=\"AI Assistant Chat\"\n aria-modal={!isSidebar && !isPanel && !isEmbedded ? \"true\" : undefined}\n className={`inapp-ai-window ${modeClass} ${isSidebar || isPanel || isEmbedded ? '' : positionClass} ${themeClass} ${foldedClass}`}\n style={windowStyle}\n >\n {/* Resize Handle for Panel */}\n {isPanel && (\n <div\n className={`inapp-ai-resize-handle ${isLeftPanel ? 'inapp-ai-resize-handle-right' : 'inapp-ai-resize-handle-left'}`}\n onMouseDown={() => setIsResizing(true)}\n title=\"Drag to resize\"\n />\n )}\n\n {/* Folded State Content */}\n {(isSidebar || isPanel) && isFolded ? (\n <div\n className=\"inapp-ai-folded-content\"\n onClick={toggleFolded}\n style={{ cursor: 'pointer' }}\n title=\"Click to unfold\"\n >\n <div className=\"inapp-ai-folded-icon\">\n {customStyles.buttonIcon || '🤖'}\n </div>\n <div className=\"inapp-ai-folded-text\">\n AI\n </div>\n {messages.length > 0 && (\n <div className=\"inapp-ai-message-count\">\n {messages.length}\n </div>\n )}\n </div>\n ) : (\n <>\n {/* Header (can be hidden via showHeader prop) */}\n {showHeader && (\n <div className=\"inapp-ai-header\" style={{\n ...(customStyles.headerBackground && { background: customStyles.headerBackground }),\n ...(customStyles.headerTextColor && { color: customStyles.headerTextColor }),\n }}>\n <div className=\"inapp-ai-header-title\">\n <span className=\"inapp-ai-header-icon\">{customStyles.buttonIcon || '🤖'}</span>\n <div>\n <h3>{customStyles.headerTitle || 'AI Assistant'}</h3>\n <p>\n {isConnected ? (\n <span className=\"inapp-ai-status-connected\">\n <span className=\"inapp-ai-status-dot\" />\n Connected\n </span>\n ) : (\n <span className=\"inapp-ai-status-disconnected\">\n <span className=\"inapp-ai-status-dot\" />\n Disconnected\n </span>\n )}\n </p>\n </div>\n </div>\n {/* Fold button for panels (in header top right) */}\n {(isSidebar || isPanel) && (\n <button\n className=\"inapp-ai-header-fold-btn\"\n onClick={toggleFolded}\n aria-label={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n title={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n >\n {(isLeftSidebar || isLeftPanel) ? '◀' : '▶'}\n </button>\n )}\n {/* Close button for popup mode */}\n {!isSidebar && !isPanel && !isEmbedded && (\n <button\n className=\"inapp-ai-close-btn\"\n onClick={() => setIsOpen(false)}\n aria-label=\"Close\"\n >\n ✕\n </button>\n )}\n </div>\n )}\n\n {/* Error Banner */}\n {error && (\n <ErrorMessage error={error} onDismiss={() => setError(null)} />\n )}\n\n {/* Messages */}\n <div\n className=\"inapp-ai-messages\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Chat messages\"\n >\n {messages.length === 0 ? (\n <div className=\"inapp-ai-empty-state\" role=\"status\">\n <div className=\"inapp-ai-empty-icon\" aria-hidden=\"true\">💬</div>\n <h4>Start a conversation</h4>\n <p>How can I help you today?</p>\n </div>\n ) : (\n messages.map((msg) => (\n <div\n key={msg.id}\n className={`inapp-ai-message inapp-ai-message-${msg.role}`}\n role=\"article\"\n aria-label={`${msg.role === 'user' ? 'User' : 'Assistant'} message`}\n >\n <div className=\"inapp-ai-message-icon\" aria-hidden=\"true\">\n {msg.role === 'user' ? '👤' : '🤖'}\n </div>\n <div className=\"inapp-ai-message-content\">\n <div className=\"inapp-ai-message-text\" style={{\n ...(msg.role === 'user' && customStyles.userMessageBackground && { background: customStyles.userMessageBackground }),\n ...(msg.role === 'user' && customStyles.userMessageColor && { color: customStyles.userMessageColor }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageBackground && { background: customStyles.assistantMessageBackground }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageColor && { color: customStyles.assistantMessageColor }),\n ...(customStyles.borderRadius && { borderRadius: customStyles.borderRadius }),\n }}>\n {msg.role === 'assistant' ? (\n <ReactMarkdown\n components={{\n code: CodeBlock as any,\n }}\n >\n {msg.content}\n </ReactMarkdown>\n ) : (\n msg.content\n )}\n </div>\n <div className=\"inapp-ai-message-time\">\n {msg.timestamp?.toLocaleTimeString() || ''}\n {msg.usage && (\n <span className=\"inapp-ai-message-tokens\">\n {' • '}{msg.usage.totalTokens} tokens\n </span>\n )}\n </div>\n </div>\n </div>\n ))\n )}\n {isLoading && (\n <div className=\"inapp-ai-message inapp-ai-message-assistant\">\n <div className=\"inapp-ai-message-icon\">\n <div style={{ animation: 'pulse 2s infinite' }}>🤖</div>\n </div>\n <div className=\"inapp-ai-message-content\">\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div className=\"inapp-ai-typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n <LoadingSkeleton />\n </div>\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input Area */}\n <div className=\"inapp-ai-input-area\" role=\"form\" aria-label=\"Message input\">\n {messages.length > 0 && (\n <button\n className=\"inapp-ai-clear-btn\"\n onClick={clearMessages}\n disabled={isLoading}\n aria-label=\"Clear conversation history\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">🗑️</span> Clear\n </button>\n )}\n <div className=\"inapp-ai-input-wrapper\">\n <input\n ref={inputRef}\n type=\"text\"\n className=\"inapp-ai-input\"\n placeholder={customStyles.inputPlaceholder || \"Type your message...\"}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n disabled={isLoading || !isConnected}\n aria-label=\"Message input\"\n aria-describedby=\"send-hint\"\n tabIndex={0}\n style={{\n ...(customStyles.inputBackground && { background: customStyles.inputBackground }),\n ...(customStyles.inputBorderColor && { borderColor: customStyles.inputBorderColor }),\n ...(customStyles.inputTextColor && { color: customStyles.inputTextColor }),\n }}\n />\n <button\n className=\"inapp-ai-send-btn\"\n onClick={sendMessage}\n disabled={isLoading || !isConnected || !inputValue.trim()}\n aria-label=\"Send message\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">{isLoading ? '⏳' : '⬆'}</span>\n <span className=\"sr-only\">\n {isLoading ? 'Sending...' : 'Send message'}\n </span>\n </button>\n <span id=\"send-hint\" className=\"sr-only\">\n Press Enter to send\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n )}\n </>\n );\n}\n","import { useState, useCallback, useEffect, useRef } from 'react';\nimport type { Tool } from '../types';\n\n/**\n * Options for the useTools hook\n */\nexport interface UseToolsOptions {\n /**\n * Initial tools to register\n */\n initialTools?: Tool[];\n\n /**\n * Whether to automatically cleanup tools on unmount\n * @default true\n */\n autoCleanup?: boolean;\n}\n\n/**\n * Return value from the useTools hook\n */\nexport interface UseToolsReturn {\n /**\n * Current array of registered tools\n */\n tools: Tool[];\n\n /**\n * Register a new tool\n * @param tool - Tool to register\n * @throws {Error} If tool with same name already exists\n */\n registerTool: (tool: Tool) => void;\n\n /**\n * Unregister a tool by name\n * @param name - Name of the tool to unregister\n */\n unregisterTool: (name: string) => void;\n\n /**\n * Clear all registered tools\n */\n clearTools: () => void;\n\n /**\n * Check if a tool with the given name exists\n * @param name - Tool name to check\n * @returns true if tool exists, false otherwise\n */\n hasTool: (name: string) => boolean;\n}\n\n/**\n * Hook for managing tools dynamically in React components\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { tools, registerTool, unregisterTool } = useTools();\n * const [todos, setTodos] = useState([]);\n *\n * useEffect(() => {\n * registerTool({\n * name: 'addTodo',\n * description: 'Add a new todo',\n * parameters: {\n * type: 'object',\n * properties: {\n * task: { type: 'string' }\n * }\n * },\n * handler: async ({ task }) => {\n * setTodos(prev => [...prev, { id: Date.now(), text: task }]);\n * return { success: true };\n * }\n * });\n *\n * return () => unregisterTool('addTodo');\n * }, [registerTool, unregisterTool, setTodos]);\n *\n * return <InAppAI endpoint=\"...\" tools={tools} />;\n * }\n * ```\n *\n * @param options - Configuration options\n * @returns Tool management functions and current tools array\n */\nexport function useTools(options: UseToolsOptions = {}): UseToolsReturn {\n const { initialTools = [], autoCleanup = true } = options;\n\n // Use ref to track tool names for validation\n const toolNamesRef = useRef<Set<string>>(new Set());\n\n // State for tools array\n const [tools, setTools] = useState<Tool[]>(() => {\n // Initialize with initial tools\n initialTools.forEach(tool => {\n if (toolNamesRef.current.has(tool.name)) {\n console.warn(\n `[useTools] Duplicate tool name in initialTools: \"${tool.name}\". ` +\n `Only the first occurrence will be used.`\n );\n } else {\n toolNamesRef.current.add(tool.name);\n }\n });\n\n return initialTools.filter((tool, index) => {\n // Filter out duplicates\n return initialTools.findIndex(t => t.name === tool.name) === index;\n });\n });\n\n /**\n * Register a new tool\n */\n const registerTool = useCallback((tool: Tool) => {\n if (!tool.name) {\n throw new Error('[useTools] Tool must have a name');\n }\n\n if (toolNamesRef.current.has(tool.name)) {\n console.warn(\n `[useTools] Tool \"${tool.name}\" is already registered. ` +\n `Use unregisterTool() first if you want to replace it.`\n );\n return;\n }\n\n // Validate tool structure\n if (!tool.description) {\n console.warn(`[useTools] Tool \"${tool.name}\" is missing description`);\n }\n\n if (!tool.parameters) {\n console.warn(`[useTools] Tool \"${tool.name}\" is missing parameters schema`);\n }\n\n if (typeof tool.handler !== 'function') {\n throw new Error(`[useTools] Tool \"${tool.name}\" must have a handler function`);\n }\n\n toolNamesRef.current.add(tool.name);\n setTools(prev => [...prev, tool]);\n }, []);\n\n /**\n * Unregister a tool by name\n */\n const unregisterTool = useCallback((name: string) => {\n if (!toolNamesRef.current.has(name)) {\n console.warn(`[useTools] Tool \"${name}\" is not registered`);\n return;\n }\n\n toolNamesRef.current.delete(name);\n setTools(prev => prev.filter(tool => tool.name !== name));\n }, []);\n\n /**\n * Clear all tools\n */\n const clearTools = useCallback(() => {\n toolNamesRef.current.clear();\n setTools([]);\n }, []);\n\n /**\n * Check if a tool exists\n */\n const hasTool = useCallback((name: string): boolean => {\n return toolNamesRef.current.has(name);\n }, []);\n\n /**\n * Cleanup on unmount if autoCleanup is enabled\n */\n useEffect(() => {\n if (autoCleanup) {\n return () => {\n toolNamesRef.current.clear();\n };\n }\n }, [autoCleanup]);\n\n return {\n tools,\n registerTool,\n unregisterTool,\n clearTools,\n hasTool,\n };\n}\n","import React, { createContext, useContext, useState, useCallback, useMemo } from 'react';\nimport type { Tool } from '../types';\n\n/**\n * Tool registry interface\n */\nexport interface ToolRegistry {\n /**\n * Register tools under a namespace\n * @param namespace - Unique namespace identifier\n * @param tools - Array of tools to register\n */\n register(namespace: string, tools: Tool[]): void;\n\n /**\n * Unregister all tools for a namespace\n * @param namespace - Namespace to unregister\n */\n unregister(namespace: string): void;\n\n /**\n * Get tools for a specific namespace\n * @param namespace - Namespace to query\n * @returns Array of tools for the namespace, or empty array if not found\n */\n getTools(namespace: string): Tool[];\n\n /**\n * Get all registered tools from all namespaces\n * @returns Combined array of all tools\n */\n getAllTools(): Tool[];\n\n /**\n * Clear all registered tools from all namespaces\n */\n clear(): void;\n\n /**\n * Get all registered namespace names\n * @returns Array of namespace names\n */\n getNamespaces(): string[];\n}\n\n/**\n * Context for the tool registry\n */\nconst ToolRegistryContext = createContext<ToolRegistry | null>(null);\n\n/**\n * Props for ToolRegistryProvider\n */\nexport interface ToolRegistryProviderProps {\n /**\n * Child components\n */\n children: React.ReactNode;\n\n /**\n * Initial tools organized by namespace\n */\n initialTools?: Record<string, Tool[]>;\n}\n\n/**\n * Provider component for global tool registry\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <ToolRegistryProvider>\n * <MyApp />\n * </ToolRegistryProvider>\n * );\n * }\n * ```\n */\nexport function ToolRegistryProvider({\n children,\n initialTools = {},\n}: ToolRegistryProviderProps): React.ReactElement {\n // State: Map of namespace -> tools\n const [toolsMap, setToolsMap] = useState<Map<string, Tool[]>>(\n () => new Map(Object.entries(initialTools))\n );\n\n /**\n * Register tools under a namespace\n */\n const register = useCallback((namespace: string, tools: Tool[]) => {\n // Validate namespace\n if (!namespace || typeof namespace !== 'string') {\n throw new Error('[ToolRegistry] Namespace must be a non-empty string');\n }\n\n if (!/^[a-zA-Z0-9_-]+$/.test(namespace)) {\n throw new Error(\n `[ToolRegistry] Invalid namespace \"${namespace}\". ` +\n `Use only alphanumeric characters, hyphens, and underscores.`\n );\n }\n\n // Validate tools\n if (!Array.isArray(tools)) {\n throw new Error('[ToolRegistry] Tools must be an array');\n }\n\n // Check for duplicate tool names within the namespace\n const toolNames = new Set<string>();\n tools.forEach(tool => {\n if (toolNames.has(tool.name)) {\n console.warn(\n `[ToolRegistry] Duplicate tool name in namespace \"${namespace}\": \"${tool.name}\"`\n );\n }\n toolNames.add(tool.name);\n });\n\n setToolsMap(prev => {\n const next = new Map(prev);\n next.set(namespace, tools);\n return next;\n });\n }, []);\n\n /**\n * Unregister a namespace\n */\n const unregister = useCallback((namespace: string) => {\n setToolsMap(prev => {\n if (!prev.has(namespace)) {\n console.warn(`[ToolRegistry] Namespace \"${namespace}\" is not registered`);\n return prev;\n }\n\n const next = new Map(prev);\n next.delete(namespace);\n return next;\n });\n }, []);\n\n /**\n * Get tools for a specific namespace\n */\n const getTools = useCallback(\n (namespace: string): Tool[] => {\n return toolsMap.get(namespace) || [];\n },\n [toolsMap]\n );\n\n /**\n * Get all tools from all namespaces\n */\n const getAllTools = useCallback((): Tool[] => {\n const allTools: Tool[] = [];\n const seenNames = new Set<string>();\n\n // Iterate through namespaces\n for (const [namespace, tools] of toolsMap.entries()) {\n for (const tool of tools) {\n // Warn about name conflicts across namespaces\n if (seenNames.has(tool.name)) {\n console.warn(\n `[ToolRegistry] Tool name conflict: \"${tool.name}\" exists in multiple namespaces. ` +\n `Only the first occurrence will be used.`\n );\n continue;\n }\n\n seenNames.add(tool.name);\n allTools.push(tool);\n }\n }\n\n return allTools;\n }, [toolsMap]);\n\n /**\n * Clear all tools\n */\n const clear = useCallback(() => {\n setToolsMap(new Map());\n }, []);\n\n /**\n * Get all namespace names\n */\n const getNamespaces = useCallback((): string[] => {\n return Array.from(toolsMap.keys());\n }, [toolsMap]);\n\n // Memoize the registry value\n const registry = useMemo<ToolRegistry>(\n () => ({\n register,\n unregister,\n getTools,\n getAllTools,\n clear,\n getNamespaces,\n }),\n [register, unregister, getTools, getAllTools, clear, getNamespaces]\n );\n\n return (\n <ToolRegistryContext.Provider value={registry}>\n {children}\n </ToolRegistryContext.Provider>\n );\n}\n\n/**\n * Hook to access the tool registry\n *\n * @example\n * ```tsx\n * function TodoPage() {\n * const registry = useToolRegistry();\n * const [todos, setTodos] = useState([]);\n *\n * useEffect(() => {\n * registry.register('todos', [\n * {\n * name: 'addTodo',\n * description: 'Add a todo',\n * parameters: { type: 'object', properties: { task: { type: 'string' } } },\n * handler: async ({ task }) => {\n * setTodos(prev => [...prev, { id: Date.now(), text: task }]);\n * return { success: true };\n * }\n * }\n * ]);\n *\n * return () => registry.unregister('todos');\n * }, [registry, todos, setTodos]);\n *\n * return <TodoList />;\n * }\n *\n * function ChatWidget() {\n * const registry = useToolRegistry();\n * const allTools = registry.getAllTools();\n *\n * return <InAppAI endpoint=\"...\" tools={allTools} />;\n * }\n * ```\n *\n * @throws {Error} If used outside of ToolRegistryProvider\n * @returns Tool registry instance\n */\nexport function useToolRegistry(): ToolRegistry {\n const registry = useContext(ToolRegistryContext);\n\n if (!registry) {\n throw new Error(\n '[useToolRegistry] must be used within a ToolRegistryProvider. ' +\n 'Wrap your component tree with <ToolRegistryProvider>.'\n );\n }\n\n return registry;\n}\n"],"mappings":"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","names":["index_exports","__export","InAppAI","ToolRegistryProvider","useToolRegistry","useTools","__toCommonJS","import_react","import_react_markdown","import_react_syntax_highlighter","import_prism","import_jsx_runtime","import_meta","LoadingSkeleton","ErrorMessage","error","onDismiss","errorInfo","msg","CodeBlock","inline","className","children","props","copied","setCopied","match","language","codeString","handleCopy","SyntaxHighlighter","InAppAI","endpoint","agentId","position","displayMode","defaultFolded","theme","context","customStyles","tools","panelMinWidth","panelMaxWidth","panelDefaultWidth","onPanelResize","externalConversationId","externalMessages","onMessagesChange","showHeader","authToken","maxToolRounds","apiEndpoint","getAuthToken","buildHeaders","headers","token","externalMessagesRef","messages","setMessages","updater","currentMessages","newMessages","isOpen","setIsOpen","isFolded","setIsFolded","inputValue","setInputValue","isLoading","setIsLoading","isConnected","setIsConnected","setError","panelWidth","setPanelWidth","isResizing","setIsResizing","messagesEndRef","internalConversationId","conversationId","resizeRef","inputRef","isEmbedded","isSidebar","isPanel","isLeftSidebar","isLeftPanel","handleMouseMove","e","container","containerRect","newWidth","widthPercent","minPercent","maxPercent","newWidthStr","handleMouseUp","sendMessage","message","userMessage","prev","getContext","toolDefinitions","name","description","parameters","buildMessagesPayload","m","actionsSummary","a","response","currentData","round","allToolActions","results","toolCall","toolName","toolArgs","tool","t","result","idx","toolResultLines","r","toolResultsMessage","isLastAllowedRound","followUpResponse","assistantMessage","err","handleKeyPress","clearMessages","toggleFolded","positionClass","themeClass","modeClass","foldedClass","buttonStyle","windowStyle","ReactMarkdown","import_react","useTools","options","initialTools","autoCleanup","toolNamesRef","tools","setTools","tool","index","t","registerTool","prev","unregisterTool","name","clearTools","hasTool","import_react","import_jsx_runtime","ToolRegistryContext","ToolRegistryProvider","children","initialTools","toolsMap","setToolsMap","register","namespace","tools","toolNames","tool","prev","next","unregister","getTools","getAllTools","allTools","seenNames","clear","getNamespaces","registry","useToolRegistry"]}
package/dist/index.mjs CHANGED
@@ -1,8 +1,13 @@
1
- import{useState as E,useEffect as $,useRef as Y}from"react";import Ca from"react-markdown";import{Prism as Ha}from"react-syntax-highlighter";import{vscDarkPlus as Wa}from"react-syntax-highlighter/dist/esm/styles/prism";import{Fragment as Ia,jsx as e,jsxs as n}from"react/jsx-runtime";function Na(){return n("div",{style:{display:"flex",flexDirection:"column",gap:"8px",padding:"12px 16px",background:"var(--inapp-ai-assistant-bg)",borderRadius:"var(--inapp-ai-border-radius)"},children:[e("div",{style:{height:"14px",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",borderRadius:"4px"}}),e("div",{style:{height:"14px",width:"90%",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",animationDelay:"0.1s",borderRadius:"4px"}}),e("div",{style:{height:"14px",width:"75%",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",animationDelay:"0.2s",borderRadius:"4px"}}),e("style",{children:`
1
+ import{useState as M,useEffect as W,useRef as X}from"react";import Ea from"react-markdown";import{Prism as Ya}from"react-syntax-highlighter";import{vscDarkPlus as Ja}from"react-syntax-highlighter/dist/esm/styles/prism";import{Fragment as Pa,jsx as e,jsxs as n}from"react/jsx-runtime";function Ia(){return n("div",{style:{display:"flex",flexDirection:"column",gap:"8px",padding:"12px 16px",background:"var(--inapp-ai-assistant-bg)",borderRadius:"var(--inapp-ai-border-radius)"},children:[e("div",{style:{height:"14px",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",borderRadius:"4px"}}),e("div",{style:{height:"14px",width:"90%",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",animationDelay:"0.1s",borderRadius:"4px"}}),e("div",{style:{height:"14px",width:"75%",background:"linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)",backgroundSize:"200% 100%",animation:"shimmer 1.5s infinite",animationDelay:"0.2s",borderRadius:"4px"}}),e("style",{children:`
2
2
  @keyframes shimmer {
3
3
  0% { background-position: 200% 0; }
4
4
  100% { background-position: -200% 0; }
5
5
  }
6
- `})]})}function Ra({error:b,onDismiss:c}){let r=(s=>s.includes("not responding")||s.includes("connection")||s.includes("network")?{type:"connection",icon:"\u{1F50C}",title:"Connection Error"}:s.includes("timeout")?{type:"timeout",icon:"\u23F1\uFE0F",title:"Request Timeout"}:s.includes("rate limit")?{type:"rateLimit",icon:"\u{1F6A6}",title:"Rate Limit"}:s.includes("authentication")||s.includes("unauthorized")?{type:"auth",icon:"\u{1F512}",title:"Authentication Error"}:{type:"generic",icon:"\u26A0\uFE0F",title:"Error"})(b);return n("div",{className:"inapp-ai-error-banner",role:"alert","aria-live":"assertive",style:{display:"flex",alignItems:"flex-start",gap:"12px",padding:"14px 16px",background:"linear-gradient(135deg, #fff3cd 0%, #ffe9a6 100%)",borderLeft:"4px solid #ff9800",borderRadius:"0"},children:[e("span",{style:{fontSize:"20px"},children:r.icon}),n("div",{style:{flex:1,minWidth:0},children:[e("div",{style:{fontWeight:600,marginBottom:"4px",color:"#856404"},children:r.title}),e("div",{style:{fontSize:"13px",color:"#856404",wordBreak:"break-word"},children:b}),r.type==="connection"&&e("div",{style:{fontSize:"12px",marginTop:"6px",color:"#997404"},children:"\u{1F4A1} Make sure the backend server is running on the correct port"})]}),e("button",{onClick:c,style:{background:"none",border:"none",color:"#856404",cursor:"pointer",padding:"4px 8px",fontSize:"16px",lineHeight:1},"aria-label":"Dismiss error",children:"\u2715"})]})}function Ea({inline:b,className:c,children:g,...r}){let[s,m]=E(!1),v=/language-(\w+)/.exec(c||""),a=v?v[1]:"",y=String(g).replace(/\n$/,""),k=()=>{navigator.clipboard.writeText(y),m(!0),setTimeout(()=>m(!1),2e3)};return b?e("code",{className:c,...r,children:g}):n("div",{style:{position:"relative",marginTop:"8px",marginBottom:"8px"},children:[e("button",{onClick:k,style:{position:"absolute",top:"8px",right:"8px",padding:"4px 8px",background:s?"#28a745":"rgba(255, 255, 255, 0.1)",color:"white",border:"none",borderRadius:"4px",cursor:"pointer",fontSize:"12px",zIndex:1,transition:"background 0.2s"},"aria-label":"Copy code",children:s?"\u2713 Copied!":"\u{1F4CB} Copy"}),e(Ha,{language:a||"text",style:Wa,customStyle:{borderRadius:"8px",padding:"16px",fontSize:"13px",marginTop:0,marginBottom:0},...r,children:y})]})}function Ya({endpoint:b,agentId:c,position:g="bottom-right",displayMode:r="popup",defaultFolded:s=!1,theme:m="light",context:v,customStyles:a={},tools:y=[],panelMinWidth:k="20%",panelMaxWidth:t="33.33%",panelDefaultWidth:o="25%",onPanelResize:p,conversationId:I,messages:h,onMessagesChange:w,showHeader:da=!0,authToken:j,maxToolRounds:la=10}){if(h===void 0||w===void 0)throw new Error("InAppAI requires controlled mode. Please provide both `messages` and `onMessagesChange` props. See documentation for usage examples.");let K=b||typeof process<"u"&&process.env?.REACT_APP_INAPPAI_ENDPOINT||typeof import.meta.env<"u"&&import.meta.env?.VITE_INAPPAI_ENDPOINT||"https://api.inappai.com/api",ca=()=>j&&(typeof j=="function"?j():j)||null,ga=()=>{let i={"Content-Type":"application/json"},x=ca();return x&&(i.Authorization=`Bearer ${x}`),i},Z=Y(h);$(()=>{Z.current=h},[h]);let T=h,Q=i=>{let x=Z.current,l=typeof i=="function"?i(x):i;Z.current=l,w(l)},[B,aa]=E(r.startsWith("sidebar")||r.startsWith("panel")||r==="embedded"),[P,Ba]=E(s&&(r.startsWith("sidebar")||r.startsWith("panel"))),[O,ea]=E(""),[f,fa]=E(!1),[C,ua]=E(!1),[V,A]=E(null),[$a,Da]=E(o),[ba,ma]=E(!1),ia=Y(null),Oa=Y(`react-demo-${Date.now()}`),ha=I||Oa.current,na=Y(null),ra=Y(null),N=r==="embedded",d=r.startsWith("sidebar"),u=r.startsWith("panel"),La=r==="sidebar-left",X=r==="panel-left";$(()=>{(d||u||N)&&aa(!0)},[d,u,N]),$(()=>{if(!u||!ba)return;let i=l=>{let M=na.current?.parentElement;if(!M)return;let L=M.getBoundingClientRect(),S;X?S=l.clientX:S=L.width-l.clientX;let R=S/L.width*100,U=parseFloat(k),pa=parseFloat(t),H=`${Math.max(U,Math.min(pa,R))}%`;Da(H),p&&p(S)},x=()=>{ma(!1)};return document.addEventListener("mousemove",i),document.addEventListener("mouseup",x),()=>{document.removeEventListener("mousemove",i),document.removeEventListener("mouseup",x)}},[ba,u,X,k,t,p]),$(()=>{(async()=>{try{let x={},l=ca();l&&(x.Authorization=`Bearer ${l}`),(await fetch(`${K}/${c}/health`,{headers:x})).ok?(ua(!0),A(null)):A("Backend not responding")}catch{A("Failed to connect to backend"),ua(!1)}})()},[K,c]),$(()=>{ia.current?.scrollIntoView({behavior:"smooth"})},[T]),$(()=>{!f&&C&&setTimeout(()=>{ra.current?.focus()},100)},[f,C]);let ta=async()=>{let i=O.trim();if(!i||f)return;let x={id:`${Date.now()}-user`,role:"user",content:i,timestamp:new Date};Q(l=>[...l,x]),ea(""),fa(!0),A(null);try{let l=()=>typeof v=="function"?v():v,M=y.map(({name:F,description:H,parameters:sa})=>({type:"function",function:{name:F,description:H,parameters:sa}})),L=await fetch(`${K}/${c}/chat`,{method:"POST",headers:ga(),body:JSON.stringify({message:i,conversationId:ha,context:l(),tools:M.length>0?M:void 0,disableCache:!1})});if(!L.ok)throw new Error("Failed to get response");let R=await L.json(),U=0;for(;R.toolCalls&&R.toolCalls.length>0&&U<la;){U++;let H=(await Promise.all(R.toolCalls.map(async z=>{let _=z.function?.name||z.name,J=z.function?.arguments?JSON.parse(z.function.arguments):z.parameters,q=y.find(W=>W.name===_);if(!q)return{success:!1,error:`Tool '${_}' not found`};try{return await Promise.resolve(q.handler(J))}catch(W){return console.error(`Tool '${q.name}' failed:`,W),{success:!1,error:W.message}}}))).map((z,_)=>{let J=R.toolCalls[_];return`Tool "${J.function?.name||J.name}" result: ${JSON.stringify(z)}`}).join(`
7
- `),sa=U>=la,Ta=await fetch(`${K}/${c}/chat`,{method:"POST",headers:ga(),body:JSON.stringify({message:H,conversationId:ha,context:l(),tools:sa?void 0:M.length>0?M:void 0,disableCache:!1})});if(!Ta.ok)throw new Error("Failed to get AI response for tool results");R=await Ta.json()}let pa={id:`${Date.now()}-assistant`,role:"assistant",content:R.message||"I executed the tools successfully.",timestamp:new Date,usage:R.usage};Q(F=>[...F,pa])}catch(l){console.error("\u274C Error:",l),A(l instanceof Error?l.message:"Unknown error")}finally{fa(!1)}},xa=i=>{i.key==="Enter"&&!i.shiftKey&&(i.preventDefault(),ta())},va=()=>{Q([])},ya=()=>{Ba(!P)},wa=`inapp-ai-${g}`,oa=m&&m!=="light"?`inapp-ai-theme-${m}`:"",Sa=N?"inapp-ai-embedded":d?`inapp-ai-sidebar inapp-ai-${r}`:u?`inapp-ai-panel inapp-ai-${r}`:"inapp-ai-popup",Ua=P?"inapp-ai-folded":"",Fa={...a.buttonBackgroundColor&&{background:a.buttonBackgroundColor},...a.buttonTextColor&&{color:a.buttonTextColor},...a.buttonSize&&{width:a.buttonSize,height:a.buttonSize,fontSize:`calc(${a.buttonSize} * 0.5)`},...a.buttonBorderRadius&&{borderRadius:a.buttonBorderRadius},...a.boxShadow&&{boxShadow:a.boxShadow}},ka={...a.windowWidth&&!d&&!u&&{width:a.windowWidth},...a.windowHeight&&!d&&!u&&{height:a.windowHeight},...a.windowBorderRadius&&{borderRadius:a.windowBorderRadius},...a.fontFamily&&{fontFamily:a.fontFamily},...a.fontSize&&{fontSize:a.fontSize},...a.sidebarWidth&&d&&!P&&{width:a.sidebarWidth},...a.sidebarFoldedWidth&&d&&P&&{width:a.sidebarFoldedWidth},...u&&{width:$a}};return N&&B?n("div",{ref:na,role:"region","aria-label":"AI Assistant Chat",className:`inapp-ai-window inapp-ai-embedded ${oa}`,style:ka,children:[da&&e("div",{className:"inapp-ai-header",style:{...a.headerBackground&&{background:a.headerBackground},...a.headerTextColor&&{color:a.headerTextColor}},children:n("div",{className:"inapp-ai-header-title",children:[e("span",{className:"inapp-ai-header-icon",children:a.buttonIcon||"\u{1F916}"}),n("div",{children:[e("h3",{children:a.headerTitle||"AI Assistant"}),e("p",{children:C?n("span",{className:"inapp-ai-status-connected",children:[e("span",{className:"inapp-ai-status-dot"}),"Connected"]}):n("span",{className:"inapp-ai-status-disconnected",children:[e("span",{className:"inapp-ai-status-dot"}),"Disconnected"]})})]})]})}),V&&e(Ra,{error:V,onDismiss:()=>A(null)}),n("div",{className:"inapp-ai-messages",role:"log","aria-live":"polite","aria-label":"Chat messages",children:[T.length===0?n("div",{className:"inapp-ai-empty-state",role:"status",children:[e("div",{className:"inapp-ai-empty-icon","aria-hidden":"true",children:"\u{1F4AC}"}),e("h4",{children:"Start a conversation"}),e("p",{children:"How can I help you today?"})]}):T.map(i=>n("div",{className:`inapp-ai-message inapp-ai-message-${i.role}`,role:"article","aria-label":`${i.role==="user"?"User":"Assistant"} message`,children:[e("div",{className:"inapp-ai-message-icon","aria-hidden":"true",children:i.role==="user"?"\u{1F464}":"\u{1F916}"}),n("div",{className:"inapp-ai-message-content",children:[e("div",{className:"inapp-ai-message-text",style:{...i.role==="user"&&a.userMessageBackground&&{background:a.userMessageBackground},...i.role==="user"&&a.userMessageColor&&{color:a.userMessageColor},...i.role==="assistant"&&a.assistantMessageBackground&&{background:a.assistantMessageBackground},...i.role==="assistant"&&a.assistantMessageColor&&{color:a.assistantMessageColor},...a.borderRadius&&{borderRadius:a.borderRadius}},children:i.role==="assistant"?e(Ca,{components:{code:Ea},children:i.content}):i.content}),n("div",{className:"inapp-ai-message-time",children:[i.timestamp?new Date(i.timestamp).toLocaleTimeString():"",i.usage&&n("span",{className:"inapp-ai-message-tokens",children:[" \u2022 ",i.usage.totalTokens," tokens"]})]})]})]},i.id)),f&&n("div",{className:"inapp-ai-message inapp-ai-message-assistant",children:[e("div",{className:"inapp-ai-message-icon",children:e("div",{style:{animation:"pulse 2s infinite"},children:"\u{1F916}"})}),e("div",{className:"inapp-ai-message-content",children:n("div",{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[n("div",{className:"inapp-ai-typing-indicator",children:[e("span",{}),e("span",{}),e("span",{})]}),e(Na,{})]})})]}),e("div",{ref:ia})]}),n("div",{className:"inapp-ai-input-area",role:"form","aria-label":"Message input",children:[T.length>0&&n("button",{className:"inapp-ai-clear-btn",onClick:va,disabled:f,"aria-label":"Clear conversation history",tabIndex:0,children:[e("span",{"aria-hidden":"true",children:"\u{1F5D1}\uFE0F"})," Clear"]}),n("div",{className:"inapp-ai-input-wrapper",children:[e("input",{ref:ra,type:"text",className:"inapp-ai-input",placeholder:a.inputPlaceholder||"Type your message...",value:O,onChange:i=>ea(i.target.value),onKeyPress:xa,disabled:f||!C,"aria-label":"Message input","aria-describedby":"send-hint",tabIndex:0,style:{...a.inputBackground&&{background:a.inputBackground},...a.inputBorderColor&&{borderColor:a.inputBorderColor},...a.inputTextColor&&{color:a.inputTextColor}}}),n("button",{className:"inapp-ai-send-btn",onClick:ta,disabled:f||!C||!O.trim(),"aria-label":"Send message",tabIndex:0,children:[e("span",{"aria-hidden":"true",children:f?"\u23F3":"\u2B06"}),e("span",{className:"sr-only",children:f?"Sending...":"Send message"})]}),e("span",{id:"send-hint",className:"sr-only",children:"Press Enter to send"})]})]})]}):n(Ia,{children:[!d&&!u&&!N&&n("button",{className:`inapp-ai-button ${wa} ${oa}`,style:Fa,onClick:()=>aa(!B),"aria-label":B?"Close AI Assistant":"Open AI Assistant","aria-expanded":B,"aria-haspopup":"dialog",tabIndex:0,children:[B?"\u2715":a.buttonIcon||"\u{1F916}",!C&&e("span",{className:"inapp-ai-offline-indicator",role:"status","aria-label":"Backend disconnected"})]}),B&&n("div",{ref:na,role:N?"region":"dialog","aria-label":"AI Assistant Chat","aria-modal":!d&&!u&&!N?"true":void 0,className:`inapp-ai-window ${Sa} ${d||u||N?"":wa} ${oa} ${Ua}`,style:ka,children:[u&&e("div",{className:`inapp-ai-resize-handle ${X?"inapp-ai-resize-handle-right":"inapp-ai-resize-handle-left"}`,onMouseDown:()=>ma(!0),title:"Drag to resize"}),(d||u)&&P?n("div",{className:"inapp-ai-folded-content",onClick:ya,style:{cursor:"pointer"},title:"Click to unfold",children:[e("div",{className:"inapp-ai-folded-icon",children:a.buttonIcon||"\u{1F916}"}),e("div",{className:"inapp-ai-folded-text",children:"AI"}),T.length>0&&e("div",{className:"inapp-ai-message-count",children:T.length})]}):n(Ia,{children:[da&&n("div",{className:"inapp-ai-header",style:{...a.headerBackground&&{background:a.headerBackground},...a.headerTextColor&&{color:a.headerTextColor}},children:[n("div",{className:"inapp-ai-header-title",children:[e("span",{className:"inapp-ai-header-icon",children:a.buttonIcon||"\u{1F916}"}),n("div",{children:[e("h3",{children:a.headerTitle||"AI Assistant"}),e("p",{children:C?n("span",{className:"inapp-ai-status-connected",children:[e("span",{className:"inapp-ai-status-dot"}),"Connected"]}):n("span",{className:"inapp-ai-status-disconnected",children:[e("span",{className:"inapp-ai-status-dot"}),"Disconnected"]})})]})]}),(d||u)&&e("button",{className:"inapp-ai-header-fold-btn",onClick:ya,"aria-label":P?`Unfold ${d?"sidebar":"panel"}`:`Fold ${d?"sidebar":"panel"}`,title:P?`Unfold ${d?"sidebar":"panel"}`:`Fold ${d?"sidebar":"panel"}`,children:La||X?"\u25C0":"\u25B6"}),!d&&!u&&!N&&e("button",{className:"inapp-ai-close-btn",onClick:()=>aa(!1),"aria-label":"Close",children:"\u2715"})]}),V&&e(Ra,{error:V,onDismiss:()=>A(null)}),n("div",{className:"inapp-ai-messages",role:"log","aria-live":"polite","aria-label":"Chat messages",children:[T.length===0?n("div",{className:"inapp-ai-empty-state",role:"status",children:[e("div",{className:"inapp-ai-empty-icon","aria-hidden":"true",children:"\u{1F4AC}"}),e("h4",{children:"Start a conversation"}),e("p",{children:"How can I help you today?"})]}):T.map(i=>n("div",{className:`inapp-ai-message inapp-ai-message-${i.role}`,role:"article","aria-label":`${i.role==="user"?"User":"Assistant"} message`,children:[e("div",{className:"inapp-ai-message-icon","aria-hidden":"true",children:i.role==="user"?"\u{1F464}":"\u{1F916}"}),n("div",{className:"inapp-ai-message-content",children:[e("div",{className:"inapp-ai-message-text",style:{...i.role==="user"&&a.userMessageBackground&&{background:a.userMessageBackground},...i.role==="user"&&a.userMessageColor&&{color:a.userMessageColor},...i.role==="assistant"&&a.assistantMessageBackground&&{background:a.assistantMessageBackground},...i.role==="assistant"&&a.assistantMessageColor&&{color:a.assistantMessageColor},...a.borderRadius&&{borderRadius:a.borderRadius}},children:i.role==="assistant"?e(Ca,{components:{code:Ea},children:i.content}):i.content}),n("div",{className:"inapp-ai-message-time",children:[i.timestamp?.toLocaleTimeString()||"",i.usage&&n("span",{className:"inapp-ai-message-tokens",children:[" \u2022 ",i.usage.totalTokens," tokens"]})]})]})]},i.id)),f&&n("div",{className:"inapp-ai-message inapp-ai-message-assistant",children:[e("div",{className:"inapp-ai-message-icon",children:e("div",{style:{animation:"pulse 2s infinite"},children:"\u{1F916}"})}),e("div",{className:"inapp-ai-message-content",children:n("div",{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[n("div",{className:"inapp-ai-typing-indicator",children:[e("span",{}),e("span",{}),e("span",{})]}),e(Na,{})]})})]}),e("div",{ref:ia})]}),n("div",{className:"inapp-ai-input-area",role:"form","aria-label":"Message input",children:[T.length>0&&n("button",{className:"inapp-ai-clear-btn",onClick:va,disabled:f,"aria-label":"Clear conversation history",tabIndex:0,children:[e("span",{"aria-hidden":"true",children:"\u{1F5D1}\uFE0F"})," Clear"]}),n("div",{className:"inapp-ai-input-wrapper",children:[e("input",{ref:ra,type:"text",className:"inapp-ai-input",placeholder:a.inputPlaceholder||"Type your message...",value:O,onChange:i=>ea(i.target.value),onKeyPress:xa,disabled:f||!C,"aria-label":"Message input","aria-describedby":"send-hint",tabIndex:0,style:{...a.inputBackground&&{background:a.inputBackground},...a.inputBorderColor&&{borderColor:a.inputBorderColor},...a.inputTextColor&&{color:a.inputTextColor}}}),n("button",{className:"inapp-ai-send-btn",onClick:ta,disabled:f||!C||!O.trim(),"aria-label":"Send message",tabIndex:0,children:[e("span",{"aria-hidden":"true",children:f?"\u23F3":"\u2B06"}),e("span",{className:"sr-only",children:f?"Sending...":"Send message"})]}),e("span",{id:"send-hint",className:"sr-only",children:"Press Enter to send"})]})]})]})]})]})}import{useState as ja,useCallback as G,useEffect as Ka,useRef as Va}from"react";function Ma(b={}){let{initialTools:c=[],autoCleanup:g=!0}=b,r=Va(new Set),[s,m]=ja(()=>(c.forEach(t=>{r.current.has(t.name)?console.warn(`[useTools] Duplicate tool name in initialTools: "${t.name}". Only the first occurrence will be used.`):r.current.add(t.name)}),c.filter((t,o)=>c.findIndex(p=>p.name===t.name)===o))),v=G(t=>{if(!t.name)throw new Error("[useTools] Tool must have a name");if(r.current.has(t.name)){console.warn(`[useTools] Tool "${t.name}" is already registered. Use unregisterTool() first if you want to replace it.`);return}if(t.description||console.warn(`[useTools] Tool "${t.name}" is missing description`),t.parameters||console.warn(`[useTools] Tool "${t.name}" is missing parameters schema`),typeof t.handler!="function")throw new Error(`[useTools] Tool "${t.name}" must have a handler function`);r.current.add(t.name),m(o=>[...o,t])},[]),a=G(t=>{if(!r.current.has(t)){console.warn(`[useTools] Tool "${t}" is not registered`);return}r.current.delete(t),m(o=>o.filter(p=>p.name!==t))},[]),y=G(()=>{r.current.clear(),m([])},[]),k=G(t=>r.current.has(t),[]);return Ka(()=>{if(g)return()=>{r.current.clear()}},[g]),{tools:s,registerTool:v,unregisterTool:a,clearTools:y,hasTool:k}}import{createContext as Xa,useContext as _a,useState as Ja,useCallback as D,useMemo as qa}from"react";import{jsx as Ga}from"react/jsx-runtime";var Pa=Xa(null);function Aa({children:b,initialTools:c={}}){let[g,r]=Ja(()=>new Map(Object.entries(c))),s=D((o,p)=>{if(!o||typeof o!="string")throw new Error("[ToolRegistry] Namespace must be a non-empty string");if(!/^[a-zA-Z0-9_-]+$/.test(o))throw new Error(`[ToolRegistry] Invalid namespace "${o}". Use only alphanumeric characters, hyphens, and underscores.`);if(!Array.isArray(p))throw new Error("[ToolRegistry] Tools must be an array");let I=new Set;p.forEach(h=>{I.has(h.name)&&console.warn(`[ToolRegistry] Duplicate tool name in namespace "${o}": "${h.name}"`),I.add(h.name)}),r(h=>{let w=new Map(h);return w.set(o,p),w})},[]),m=D(o=>{r(p=>{if(!p.has(o))return console.warn(`[ToolRegistry] Namespace "${o}" is not registered`),p;let I=new Map(p);return I.delete(o),I})},[]),v=D(o=>g.get(o)||[],[g]),a=D(()=>{let o=[],p=new Set;for(let[I,h]of g.entries())for(let w of h){if(p.has(w.name)){console.warn(`[ToolRegistry] Tool name conflict: "${w.name}" exists in multiple namespaces. Only the first occurrence will be used.`);continue}p.add(w.name),o.push(w)}return o},[g]),y=D(()=>{r(new Map)},[]),k=D(()=>Array.from(g.keys()),[g]),t=qa(()=>({register:s,unregister:m,getTools:v,getAllTools:a,clear:y,getNamespaces:k}),[s,m,v,a,y,k]);return Ga(Pa.Provider,{value:t,children:b})}function za(){let b=_a(Pa);if(!b)throw new Error("[useToolRegistry] must be used within a ToolRegistryProvider. Wrap your component tree with <ToolRegistryProvider>.");return b}export{Ya as InAppAI,Aa as ToolRegistryProvider,za as useToolRegistry,Ma as useTools};
6
+ `})]})}function Aa({error:b,onDismiss:c}){let r=(s=>s.includes("not responding")||s.includes("connection")||s.includes("network")?{type:"connection",icon:"\u{1F50C}",title:"Connection Error"}:s.includes("timeout")?{type:"timeout",icon:"\u23F1\uFE0F",title:"Request Timeout"}:s.includes("rate limit")?{type:"rateLimit",icon:"\u{1F6A6}",title:"Rate Limit"}:s.includes("authentication")||s.includes("unauthorized")?{type:"auth",icon:"\u{1F512}",title:"Authentication Error"}:{type:"generic",icon:"\u26A0\uFE0F",title:"Error"})(b);return n("div",{className:"inapp-ai-error-banner",role:"alert","aria-live":"assertive",style:{display:"flex",alignItems:"flex-start",gap:"12px",padding:"14px 16px",background:"linear-gradient(135deg, #fff3cd 0%, #ffe9a6 100%)",borderLeft:"4px solid #ff9800",borderRadius:"0"},children:[e("span",{style:{fontSize:"20px"},children:r.icon}),n("div",{style:{flex:1,minWidth:0},children:[e("div",{style:{fontWeight:600,marginBottom:"4px",color:"#856404"},children:r.title}),e("div",{style:{fontSize:"13px",color:"#856404",wordBreak:"break-word"},children:b}),r.type==="connection"&&e("div",{style:{fontSize:"12px",marginTop:"6px",color:"#997404"},children:"\u{1F4A1} Make sure the backend server is running on the correct port"})]}),e("button",{onClick:c,style:{background:"none",border:"none",color:"#856404",cursor:"pointer",padding:"4px 8px",fontSize:"16px",lineHeight:1},"aria-label":"Dismiss error",children:"\u2715"})]})}function Ma({inline:b,className:c,children:g,...r}){let[s,m]=M(!1),y=/language-(\w+)/.exec(c||""),a=y?y[1]:"",w=String(g).replace(/\n$/,""),R=()=>{navigator.clipboard.writeText(w),m(!0),setTimeout(()=>m(!1),2e3)};return b?e("code",{className:c,...r,children:g}):n("div",{style:{position:"relative",marginTop:"8px",marginBottom:"8px"},children:[e("button",{onClick:R,style:{position:"absolute",top:"8px",right:"8px",padding:"4px 8px",background:s?"#28a745":"rgba(255, 255, 255, 0.1)",color:"white",border:"none",borderRadius:"4px",cursor:"pointer",fontSize:"12px",zIndex:1,transition:"background 0.2s"},"aria-label":"Copy code",children:s?"\u2713 Copied!":"\u{1F4CB} Copy"}),e(Ya,{language:a||"text",style:Ja,customStyle:{borderRadius:"8px",padding:"16px",fontSize:"13px",marginTop:0,marginBottom:0},...r,children:w})]})}function Ka({endpoint:b,agentId:c,position:g="bottom-right",displayMode:r="popup",defaultFolded:s=!1,theme:m="light",context:y,customStyles:a={},tools:w=[],panelMinWidth:R="20%",panelMaxWidth:t="33.33%",panelDefaultWidth:o="25%",onPanelResize:p,conversationId:P,messages:h,onMessagesChange:k,showHeader:ca=!0,authToken:_,maxToolRounds:ga=10}){if(h===void 0||k===void 0)throw new Error("InAppAI requires controlled mode. Please provide both `messages` and `onMessagesChange` props. See documentation for usage examples.");let q=b||typeof process<"u"&&process.env?.REACT_APP_INAPPAI_ENDPOINT||typeof import.meta.env<"u"&&import.meta.env?.VITE_INAPPAI_ENDPOINT||"https://api.inappai.com/api",fa=()=>_&&(typeof _=="function"?_():_)||null,ua=()=>{let i={"Content-Type":"application/json"},x=fa();return x&&(i.Authorization=`Bearer ${x}`),i},ea=X(h);W(()=>{ea.current=h},[h]);let T=h,ia=i=>{let x=ea.current,l=typeof i=="function"?i(x):i;ea.current=l,k(l)},[L,na]=M(r.startsWith("sidebar")||r.startsWith("panel")||r==="embedded"),[B,Oa]=M(s&&(r.startsWith("sidebar")||r.startsWith("panel"))),[Y,ra]=M(""),[f,ba]=M(!1),[E,ma]=M(!1),[G,$]=M(null),[La,Sa]=M(o),[ha,xa]=M(!1),ta=X(null),Ua=X(`react-demo-${Date.now()}`),va=P||Ua.current,oa=X(null),pa=X(null),I=r==="embedded",d=r.startsWith("sidebar"),u=r.startsWith("panel"),Fa=r==="sidebar-left",Z=r==="panel-left";W(()=>{(d||u||I)&&na(!0)},[d,u,I]),W(()=>{if(!u||!ha)return;let i=l=>{let z=oa.current?.parentElement;if(!z)return;let J=z.getBoundingClientRect(),D;Z?D=l.clientX:D=J.width-l.clientX;let Na=D/J.width*100,C=parseFloat(R),S=parseFloat(t),la=`${Math.max(C,Math.min(S,Na))}%`;Sa(la),p&&p(D)},x=()=>{xa(!1)};return document.addEventListener("mousemove",i),document.addEventListener("mouseup",x),()=>{document.removeEventListener("mousemove",i),document.removeEventListener("mouseup",x)}},[ha,u,Z,R,t,p]),W(()=>{(async()=>{try{let x={},l=fa();l&&(x.Authorization=`Bearer ${l}`),(await fetch(`${q}/${c}/health`,{headers:x})).ok?(ma(!0),$(null)):$("Backend not responding")}catch{$("Failed to connect to backend"),ma(!1)}})()},[q,c]),W(()=>{ta.current?.scrollIntoView({behavior:"smooth"})},[T]),W(()=>{!f&&E&&setTimeout(()=>{pa.current?.focus()},100)},[f,E]);let sa=async()=>{let i=Y.trim();if(!i||f)return;let x={id:`${Date.now()}-user`,role:"user",content:i,timestamp:new Date};ia(l=>[...l,x]),ra(""),ba(!0),$(null);try{let l=()=>typeof y=="function"?y():y,z=w.map(({name:v,description:U,parameters:K})=>({type:"function",function:{name:v,description:U,parameters:K}})),J=()=>T.map(v=>{let U={role:v.role,content:v.content};if(v.toolActions&&v.toolActions.length>0){let K=v.toolActions.map(V=>`${V.tool}(${JSON.stringify(V.args)}) \u2192 ${JSON.stringify(V.result)}`).join(", ");U.content+=`
7
+
8
+ [Tool actions: ${K}]`}return U}),D=await fetch(`${q}/${c}/chat`,{method:"POST",headers:ua(),body:JSON.stringify({message:i,messages:J(),conversationId:va,context:l(),tools:z.length>0?z:void 0,disableCache:!1})});if(!D.ok)throw new Error("Failed to get response");let C=await D.json(),S=0,Q=[];for(;C.toolCalls&&C.toolCalls.length>0&&S<ga;){S++;let v=await Promise.all(C.toolCalls.map(async A=>{let F=A.function?.name||A.name,N=A.function?.arguments?JSON.parse(A.function.arguments):A.parameters,H=w.find(O=>O.name===F);if(!H)return{success:!1,error:`Tool '${F}' not found`};try{return await Promise.resolve(H.handler(N))}catch(O){return console.error(`Tool '${H.name}' failed:`,O),{success:!1,error:O.message}}}));v.forEach((A,F)=>{let N=C.toolCalls[F],H=N.function?.name||N.name,O=N.function?.arguments?JSON.parse(N.function.arguments):N.parameters||{};Q.push({tool:H,args:O,result:A})});let U=v.map((A,F)=>{let N=C.toolCalls[F];return`Tool "${N.function?.name||N.name}" result: ${JSON.stringify(A)}`}).join(`
9
+ `),K=`[TOOL EXECUTION COMPLETE - Round ${S}]
10
+ The following ${v.length} tool call(s) have been executed successfully. Do NOT re-execute them.
11
+ `+U+`
12
+ If all requested actions are complete, respond to the user with a summary. Only make additional tool calls if new/different actions are needed.`,V=S>=ga,Ra=await fetch(`${q}/${c}/chat`,{method:"POST",headers:ua(),body:JSON.stringify({message:K,messages:J(),conversationId:va,context:l(),tools:V?void 0:z.length>0?z:void 0,disableCache:!1})});if(!Ra.ok)throw new Error("Failed to get AI response for tool results");C=await Ra.json()}let la={id:`${Date.now()}-assistant`,role:"assistant",content:C.message||"I executed the tools successfully.",timestamp:new Date,usage:C.usage,toolActions:Q.length>0?Q:void 0};ia(v=>[...v,la])}catch(l){console.error("\u274C Error:",l),$(l instanceof Error?l.message:"Unknown error")}finally{ba(!1)}},ya=i=>{i.key==="Enter"&&!i.shiftKey&&(i.preventDefault(),sa())},wa=()=>{ia([])},ka=()=>{Oa(!B)},Ta=`inapp-ai-${g}`,da=m&&m!=="light"?`inapp-ai-theme-${m}`:"",Ha=I?"inapp-ai-embedded":d?`inapp-ai-sidebar inapp-ai-${r}`:u?`inapp-ai-panel inapp-ai-${r}`:"inapp-ai-popup",Wa=B?"inapp-ai-folded":"",ja={...a.buttonBackgroundColor&&{background:a.buttonBackgroundColor},...a.buttonTextColor&&{color:a.buttonTextColor},...a.buttonSize&&{width:a.buttonSize,height:a.buttonSize,fontSize:`calc(${a.buttonSize} * 0.5)`},...a.buttonBorderRadius&&{borderRadius:a.buttonBorderRadius},...a.boxShadow&&{boxShadow:a.boxShadow}},Ca={...a.windowWidth&&!d&&!u&&{width:a.windowWidth},...a.windowHeight&&!d&&!u&&{height:a.windowHeight},...a.windowBorderRadius&&{borderRadius:a.windowBorderRadius},...a.fontFamily&&{fontFamily:a.fontFamily},...a.fontSize&&{fontSize:a.fontSize},...a.sidebarWidth&&d&&!B&&{width:a.sidebarWidth},...a.sidebarFoldedWidth&&d&&B&&{width:a.sidebarFoldedWidth},...u&&{width:La}};return I&&L?n("div",{ref:oa,role:"region","aria-label":"AI Assistant Chat",className:`inapp-ai-window inapp-ai-embedded ${da}`,style:Ca,children:[ca&&e("div",{className:"inapp-ai-header",style:{...a.headerBackground&&{background:a.headerBackground},...a.headerTextColor&&{color:a.headerTextColor}},children:n("div",{className:"inapp-ai-header-title",children:[e("span",{className:"inapp-ai-header-icon",children:a.buttonIcon||"\u{1F916}"}),n("div",{children:[e("h3",{children:a.headerTitle||"AI Assistant"}),e("p",{children:E?n("span",{className:"inapp-ai-status-connected",children:[e("span",{className:"inapp-ai-status-dot"}),"Connected"]}):n("span",{className:"inapp-ai-status-disconnected",children:[e("span",{className:"inapp-ai-status-dot"}),"Disconnected"]})})]})]})}),G&&e(Aa,{error:G,onDismiss:()=>$(null)}),n("div",{className:"inapp-ai-messages",role:"log","aria-live":"polite","aria-label":"Chat messages",children:[T.length===0?n("div",{className:"inapp-ai-empty-state",role:"status",children:[e("div",{className:"inapp-ai-empty-icon","aria-hidden":"true",children:"\u{1F4AC}"}),e("h4",{children:"Start a conversation"}),e("p",{children:"How can I help you today?"})]}):T.map(i=>n("div",{className:`inapp-ai-message inapp-ai-message-${i.role}`,role:"article","aria-label":`${i.role==="user"?"User":"Assistant"} message`,children:[e("div",{className:"inapp-ai-message-icon","aria-hidden":"true",children:i.role==="user"?"\u{1F464}":"\u{1F916}"}),n("div",{className:"inapp-ai-message-content",children:[e("div",{className:"inapp-ai-message-text",style:{...i.role==="user"&&a.userMessageBackground&&{background:a.userMessageBackground},...i.role==="user"&&a.userMessageColor&&{color:a.userMessageColor},...i.role==="assistant"&&a.assistantMessageBackground&&{background:a.assistantMessageBackground},...i.role==="assistant"&&a.assistantMessageColor&&{color:a.assistantMessageColor},...a.borderRadius&&{borderRadius:a.borderRadius}},children:i.role==="assistant"?e(Ea,{components:{code:Ma},children:i.content}):i.content}),n("div",{className:"inapp-ai-message-time",children:[i.timestamp?new Date(i.timestamp).toLocaleTimeString():"",i.usage&&n("span",{className:"inapp-ai-message-tokens",children:[" \u2022 ",i.usage.totalTokens," tokens"]})]})]})]},i.id)),f&&n("div",{className:"inapp-ai-message inapp-ai-message-assistant",children:[e("div",{className:"inapp-ai-message-icon",children:e("div",{style:{animation:"pulse 2s infinite"},children:"\u{1F916}"})}),e("div",{className:"inapp-ai-message-content",children:n("div",{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[n("div",{className:"inapp-ai-typing-indicator",children:[e("span",{}),e("span",{}),e("span",{})]}),e(Ia,{})]})})]}),e("div",{ref:ta})]}),n("div",{className:"inapp-ai-input-area",role:"form","aria-label":"Message input",children:[T.length>0&&n("button",{className:"inapp-ai-clear-btn",onClick:wa,disabled:f,"aria-label":"Clear conversation history",tabIndex:0,children:[e("span",{"aria-hidden":"true",children:"\u{1F5D1}\uFE0F"})," Clear"]}),n("div",{className:"inapp-ai-input-wrapper",children:[e("input",{ref:pa,type:"text",className:"inapp-ai-input",placeholder:a.inputPlaceholder||"Type your message...",value:Y,onChange:i=>ra(i.target.value),onKeyPress:ya,disabled:f||!E,"aria-label":"Message input","aria-describedby":"send-hint",tabIndex:0,style:{...a.inputBackground&&{background:a.inputBackground},...a.inputBorderColor&&{borderColor:a.inputBorderColor},...a.inputTextColor&&{color:a.inputTextColor}}}),n("button",{className:"inapp-ai-send-btn",onClick:sa,disabled:f||!E||!Y.trim(),"aria-label":"Send message",tabIndex:0,children:[e("span",{"aria-hidden":"true",children:f?"\u23F3":"\u2B06"}),e("span",{className:"sr-only",children:f?"Sending...":"Send message"})]}),e("span",{id:"send-hint",className:"sr-only",children:"Press Enter to send"})]})]})]}):n(Pa,{children:[!d&&!u&&!I&&n("button",{className:`inapp-ai-button ${Ta} ${da}`,style:ja,onClick:()=>na(!L),"aria-label":L?"Close AI Assistant":"Open AI Assistant","aria-expanded":L,"aria-haspopup":"dialog",tabIndex:0,children:[L?"\u2715":a.buttonIcon||"\u{1F916}",!E&&e("span",{className:"inapp-ai-offline-indicator",role:"status","aria-label":"Backend disconnected"})]}),L&&n("div",{ref:oa,role:I?"region":"dialog","aria-label":"AI Assistant Chat","aria-modal":!d&&!u&&!I?"true":void 0,className:`inapp-ai-window ${Ha} ${d||u||I?"":Ta} ${da} ${Wa}`,style:Ca,children:[u&&e("div",{className:`inapp-ai-resize-handle ${Z?"inapp-ai-resize-handle-right":"inapp-ai-resize-handle-left"}`,onMouseDown:()=>xa(!0),title:"Drag to resize"}),(d||u)&&B?n("div",{className:"inapp-ai-folded-content",onClick:ka,style:{cursor:"pointer"},title:"Click to unfold",children:[e("div",{className:"inapp-ai-folded-icon",children:a.buttonIcon||"\u{1F916}"}),e("div",{className:"inapp-ai-folded-text",children:"AI"}),T.length>0&&e("div",{className:"inapp-ai-message-count",children:T.length})]}):n(Pa,{children:[ca&&n("div",{className:"inapp-ai-header",style:{...a.headerBackground&&{background:a.headerBackground},...a.headerTextColor&&{color:a.headerTextColor}},children:[n("div",{className:"inapp-ai-header-title",children:[e("span",{className:"inapp-ai-header-icon",children:a.buttonIcon||"\u{1F916}"}),n("div",{children:[e("h3",{children:a.headerTitle||"AI Assistant"}),e("p",{children:E?n("span",{className:"inapp-ai-status-connected",children:[e("span",{className:"inapp-ai-status-dot"}),"Connected"]}):n("span",{className:"inapp-ai-status-disconnected",children:[e("span",{className:"inapp-ai-status-dot"}),"Disconnected"]})})]})]}),(d||u)&&e("button",{className:"inapp-ai-header-fold-btn",onClick:ka,"aria-label":B?`Unfold ${d?"sidebar":"panel"}`:`Fold ${d?"sidebar":"panel"}`,title:B?`Unfold ${d?"sidebar":"panel"}`:`Fold ${d?"sidebar":"panel"}`,children:Fa||Z?"\u25C0":"\u25B6"}),!d&&!u&&!I&&e("button",{className:"inapp-ai-close-btn",onClick:()=>na(!1),"aria-label":"Close",children:"\u2715"})]}),G&&e(Aa,{error:G,onDismiss:()=>$(null)}),n("div",{className:"inapp-ai-messages",role:"log","aria-live":"polite","aria-label":"Chat messages",children:[T.length===0?n("div",{className:"inapp-ai-empty-state",role:"status",children:[e("div",{className:"inapp-ai-empty-icon","aria-hidden":"true",children:"\u{1F4AC}"}),e("h4",{children:"Start a conversation"}),e("p",{children:"How can I help you today?"})]}):T.map(i=>n("div",{className:`inapp-ai-message inapp-ai-message-${i.role}`,role:"article","aria-label":`${i.role==="user"?"User":"Assistant"} message`,children:[e("div",{className:"inapp-ai-message-icon","aria-hidden":"true",children:i.role==="user"?"\u{1F464}":"\u{1F916}"}),n("div",{className:"inapp-ai-message-content",children:[e("div",{className:"inapp-ai-message-text",style:{...i.role==="user"&&a.userMessageBackground&&{background:a.userMessageBackground},...i.role==="user"&&a.userMessageColor&&{color:a.userMessageColor},...i.role==="assistant"&&a.assistantMessageBackground&&{background:a.assistantMessageBackground},...i.role==="assistant"&&a.assistantMessageColor&&{color:a.assistantMessageColor},...a.borderRadius&&{borderRadius:a.borderRadius}},children:i.role==="assistant"?e(Ea,{components:{code:Ma},children:i.content}):i.content}),n("div",{className:"inapp-ai-message-time",children:[i.timestamp?.toLocaleTimeString()||"",i.usage&&n("span",{className:"inapp-ai-message-tokens",children:[" \u2022 ",i.usage.totalTokens," tokens"]})]})]})]},i.id)),f&&n("div",{className:"inapp-ai-message inapp-ai-message-assistant",children:[e("div",{className:"inapp-ai-message-icon",children:e("div",{style:{animation:"pulse 2s infinite"},children:"\u{1F916}"})}),e("div",{className:"inapp-ai-message-content",children:n("div",{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[n("div",{className:"inapp-ai-typing-indicator",children:[e("span",{}),e("span",{}),e("span",{})]}),e(Ia,{})]})})]}),e("div",{ref:ta})]}),n("div",{className:"inapp-ai-input-area",role:"form","aria-label":"Message input",children:[T.length>0&&n("button",{className:"inapp-ai-clear-btn",onClick:wa,disabled:f,"aria-label":"Clear conversation history",tabIndex:0,children:[e("span",{"aria-hidden":"true",children:"\u{1F5D1}\uFE0F"})," Clear"]}),n("div",{className:"inapp-ai-input-wrapper",children:[e("input",{ref:pa,type:"text",className:"inapp-ai-input",placeholder:a.inputPlaceholder||"Type your message...",value:Y,onChange:i=>ra(i.target.value),onKeyPress:ya,disabled:f||!E,"aria-label":"Message input","aria-describedby":"send-hint",tabIndex:0,style:{...a.inputBackground&&{background:a.inputBackground},...a.inputBorderColor&&{borderColor:a.inputBorderColor},...a.inputTextColor&&{color:a.inputTextColor}}}),n("button",{className:"inapp-ai-send-btn",onClick:sa,disabled:f||!E||!Y.trim(),"aria-label":"Send message",tabIndex:0,children:[e("span",{"aria-hidden":"true",children:f?"\u23F3":"\u2B06"}),e("span",{className:"sr-only",children:f?"Sending...":"Send message"})]}),e("span",{id:"send-hint",className:"sr-only",children:"Press Enter to send"})]})]})]})]})]})}import{useState as Va,useCallback as aa,useEffect as Xa,useRef as _a}from"react";function za(b={}){let{initialTools:c=[],autoCleanup:g=!0}=b,r=_a(new Set),[s,m]=Va(()=>(c.forEach(t=>{r.current.has(t.name)?console.warn(`[useTools] Duplicate tool name in initialTools: "${t.name}". Only the first occurrence will be used.`):r.current.add(t.name)}),c.filter((t,o)=>c.findIndex(p=>p.name===t.name)===o))),y=aa(t=>{if(!t.name)throw new Error("[useTools] Tool must have a name");if(r.current.has(t.name)){console.warn(`[useTools] Tool "${t.name}" is already registered. Use unregisterTool() first if you want to replace it.`);return}if(t.description||console.warn(`[useTools] Tool "${t.name}" is missing description`),t.parameters||console.warn(`[useTools] Tool "${t.name}" is missing parameters schema`),typeof t.handler!="function")throw new Error(`[useTools] Tool "${t.name}" must have a handler function`);r.current.add(t.name),m(o=>[...o,t])},[]),a=aa(t=>{if(!r.current.has(t)){console.warn(`[useTools] Tool "${t}" is not registered`);return}r.current.delete(t),m(o=>o.filter(p=>p.name!==t))},[]),w=aa(()=>{r.current.clear(),m([])},[]),R=aa(t=>r.current.has(t),[]);return Xa(()=>{if(g)return()=>{r.current.clear()}},[g]),{tools:s,registerTool:y,unregisterTool:a,clearTools:w,hasTool:R}}import{createContext as qa,useContext as Ga,useState as Za,useCallback as j,useMemo as Qa}from"react";import{jsx as ae}from"react/jsx-runtime";var Ba=qa(null);function $a({children:b,initialTools:c={}}){let[g,r]=Za(()=>new Map(Object.entries(c))),s=j((o,p)=>{if(!o||typeof o!="string")throw new Error("[ToolRegistry] Namespace must be a non-empty string");if(!/^[a-zA-Z0-9_-]+$/.test(o))throw new Error(`[ToolRegistry] Invalid namespace "${o}". Use only alphanumeric characters, hyphens, and underscores.`);if(!Array.isArray(p))throw new Error("[ToolRegistry] Tools must be an array");let P=new Set;p.forEach(h=>{P.has(h.name)&&console.warn(`[ToolRegistry] Duplicate tool name in namespace "${o}": "${h.name}"`),P.add(h.name)}),r(h=>{let k=new Map(h);return k.set(o,p),k})},[]),m=j(o=>{r(p=>{if(!p.has(o))return console.warn(`[ToolRegistry] Namespace "${o}" is not registered`),p;let P=new Map(p);return P.delete(o),P})},[]),y=j(o=>g.get(o)||[],[g]),a=j(()=>{let o=[],p=new Set;for(let[P,h]of g.entries())for(let k of h){if(p.has(k.name)){console.warn(`[ToolRegistry] Tool name conflict: "${k.name}" exists in multiple namespaces. Only the first occurrence will be used.`);continue}p.add(k.name),o.push(k)}return o},[g]),w=j(()=>{r(new Map)},[]),R=j(()=>Array.from(g.keys()),[g]),t=Qa(()=>({register:s,unregister:m,getTools:y,getAllTools:a,clear:w,getNamespaces:R}),[s,m,y,a,w,R]);return ae(Ba.Provider,{value:t,children:b})}function Da(){let b=Ga(Ba);if(!b)throw new Error("[useToolRegistry] must be used within a ToolRegistryProvider. Wrap your component tree with <ToolRegistryProvider>.");return b}export{Ka as InAppAI,$a as ToolRegistryProvider,Da as useToolRegistry,za as useTools};
8
13
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/InAppAI.tsx","../src/hooks/useTools.ts","../src/hooks/useToolRegistry.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';\nimport type { CustomStyles, Tool, InAppAIProps, Message } from '../types';\nimport './themes.css';\nimport './InAppAI.css';\n\n// Internal message type (extends exported Message with required timestamp)\ninterface InternalMessage extends Message {\n timestamp: Date;\n}\n\n// Loading skeleton component\nfunction LoadingSkeleton() {\n return (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '8px',\n padding: '12px 16px',\n background: 'var(--inapp-ai-assistant-bg)',\n borderRadius: 'var(--inapp-ai-border-radius)',\n }}\n >\n <div\n style={{\n height: '14px',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '90%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.1s',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '75%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.2s',\n borderRadius: '4px',\n }}\n />\n <style>{`\n @keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n `}</style>\n </div>\n );\n}\n\n// Error message component with enhanced display\ninterface ErrorMessageProps {\n error: string;\n onDismiss: () => void;\n}\n\nfunction ErrorMessage({ error, onDismiss }: ErrorMessageProps) {\n // Determine error type based on message\n const getErrorType = (msg: string) => {\n if (msg.includes('not responding') || msg.includes('connection') || msg.includes('network')) {\n return { type: 'connection', icon: '🔌', title: 'Connection Error' };\n }\n if (msg.includes('timeout')) {\n return { type: 'timeout', icon: '⏱️', title: 'Request Timeout' };\n }\n if (msg.includes('rate limit')) {\n return { type: 'rateLimit', icon: '🚦', title: 'Rate Limit' };\n }\n if (msg.includes('authentication') || msg.includes('unauthorized')) {\n return { type: 'auth', icon: '🔒', title: 'Authentication Error' };\n }\n return { type: 'generic', icon: '⚠️', title: 'Error' };\n };\n\n const errorInfo = getErrorType(error);\n\n return (\n <div\n className=\"inapp-ai-error-banner\"\n role=\"alert\"\n aria-live=\"assertive\"\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n gap: '12px',\n padding: '14px 16px',\n background: 'linear-gradient(135deg, #fff3cd 0%, #ffe9a6 100%)',\n borderLeft: '4px solid #ff9800',\n borderRadius: '0',\n }}\n >\n <span style={{ fontSize: '20px' }}>{errorInfo.icon}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontWeight: 600, marginBottom: '4px', color: '#856404' }}>\n {errorInfo.title}\n </div>\n <div style={{ fontSize: '13px', color: '#856404', wordBreak: 'break-word' }}>\n {error}\n </div>\n {errorInfo.type === 'connection' && (\n <div style={{ fontSize: '12px', marginTop: '6px', color: '#997404' }}>\n 💡 Make sure the backend server is running on the correct port\n </div>\n )}\n </div>\n <button\n onClick={onDismiss}\n style={{\n background: 'none',\n border: 'none',\n color: '#856404',\n cursor: 'pointer',\n padding: '4px 8px',\n fontSize: '16px',\n lineHeight: 1,\n }}\n aria-label=\"Dismiss error\"\n >\n ✕\n </button>\n </div>\n );\n}\n\n// Code block component with syntax highlighting and copy button\ninterface CodeBlockProps {\n inline?: boolean;\n className?: string;\n children?: React.ReactNode;\n}\n\nfunction CodeBlock({ inline, className, children, ...props }: CodeBlockProps) {\n const [copied, setCopied] = useState(false);\n\n // Extract language from className (format: language-js, language-python, etc.)\n const match = /language-(\\w+)/.exec(className || '');\n const language = match ? match[1] : '';\n\n const codeString = String(children).replace(/\\n$/, '');\n\n const handleCopy = () => {\n navigator.clipboard.writeText(codeString);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n // Inline code (backticks)\n if (inline) {\n return <code className={className} {...props}>{children}</code>;\n }\n\n // Code block with syntax highlighting\n return (\n <div style={{ position: 'relative', marginTop: '8px', marginBottom: '8px' }}>\n <button\n onClick={handleCopy}\n style={{\n position: 'absolute',\n top: '8px',\n right: '8px',\n padding: '4px 8px',\n background: copied ? '#28a745' : 'rgba(255, 255, 255, 0.1)',\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n zIndex: 1,\n transition: 'background 0.2s',\n }}\n aria-label=\"Copy code\"\n >\n {copied ? '✓ Copied!' : '📋 Copy'}\n </button>\n {/* @ts-ignore - React 18 JSX type compatibility */}\n <SyntaxHighlighter\n language={language || 'text'}\n style={vscDarkPlus}\n customStyle={{\n borderRadius: '8px',\n padding: '16px',\n fontSize: '13px',\n marginTop: 0,\n marginBottom: 0,\n }}\n {...props}\n >\n {codeString}\n </SyntaxHighlighter>\n </div>\n );\n}\n\nexport function InAppAI({\n endpoint,\n agentId,\n position = 'bottom-right',\n displayMode = 'popup',\n defaultFolded = false,\n theme = 'light',\n context,\n customStyles = {},\n tools = [],\n panelMinWidth = '20%',\n panelMaxWidth = '33.33%',\n panelDefaultWidth = '25%',\n onPanelResize,\n // Controlled mode props\n conversationId: externalConversationId,\n messages: externalMessages,\n onMessagesChange,\n showHeader = true,\n // Authentication\n authToken,\n // Tool execution\n maxToolRounds = 10,\n}: InAppAIProps) {\n // Require controlled mode - messages and onMessagesChange are required\n if (externalMessages === undefined || onMessagesChange === undefined) {\n throw new Error(\n 'InAppAI requires controlled mode. Please provide both `messages` and `onMessagesChange` props. ' +\n 'See documentation for usage examples.'\n );\n }\n\n // Determine API endpoint (environment variable > prop > default)\n const apiEndpoint = endpoint ||\n (typeof process !== 'undefined' && (process as any).env?.REACT_APP_INAPPAI_ENDPOINT) ||\n (typeof (import.meta as any).env !== 'undefined' && (import.meta as any).env?.VITE_INAPPAI_ENDPOINT) ||\n 'https://api.inappai.com/api';\n\n // Helper to get auth token (supports both static string and function)\n const getAuthToken = (): string | null => {\n if (!authToken) return null;\n const token = typeof authToken === 'function' ? authToken() : authToken;\n return token || null;\n };\n\n // Helper to build headers with optional Authorization\n const buildHeaders = (): Record<string, string> => {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const token = getAuthToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n return headers;\n };\n\n // Keep a ref to the latest external messages to avoid stale closures\n const externalMessagesRef = useRef<Message[]>(externalMessages);\n useEffect(() => {\n externalMessagesRef.current = externalMessages;\n }, [externalMessages]);\n\n // Messages from props, setMessages calls onMessagesChange\n const messages = externalMessages;\n const setMessages = (updater: Message[] | ((prev: Message[]) => Message[])) => {\n const currentMessages = externalMessagesRef.current;\n const newMessages = typeof updater === 'function' ? updater(currentMessages) : updater;\n externalMessagesRef.current = newMessages; // Update ref immediately for chained calls\n onMessagesChange(newMessages);\n };\n\n const [isOpen, setIsOpen] = useState(displayMode.startsWith('sidebar') || displayMode.startsWith('panel') || displayMode === 'embedded');\n const [isFolded, setIsFolded] = useState(defaultFolded && (displayMode.startsWith('sidebar') || displayMode.startsWith('panel')));\n const [inputValue, setInputValue] = useState('');\n const [isLoading, setIsLoading] = useState(false);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [panelWidth, setPanelWidth] = useState(panelDefaultWidth);\n const [isResizing, setIsResizing] = useState(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const internalConversationId = useRef(`react-demo-${Date.now()}`);\n const conversationId = externalConversationId || internalConversationId.current;\n const resizeRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const isEmbedded = displayMode === 'embedded';\n const isSidebar = displayMode.startsWith('sidebar');\n const isPanel = displayMode.startsWith('panel');\n const isLeftSidebar = displayMode === 'sidebar-left';\n // const isRightSidebar = displayMode === 'sidebar-right';\n const isLeftPanel = displayMode === 'panel-left';\n // const isRightPanel = displayMode === 'panel-right';\n\n // For sidebar, panel, and embedded mode, always stay open\n useEffect(() => {\n if (isSidebar || isPanel || isEmbedded) {\n setIsOpen(true);\n }\n }, [isSidebar, isPanel, isEmbedded]);\n\n // Panel resize handlers\n useEffect(() => {\n if (!isPanel || !isResizing) return;\n\n const handleMouseMove = (e: MouseEvent) => {\n const container = resizeRef.current?.parentElement;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n let newWidth: number;\n\n if (isLeftPanel) {\n newWidth = e.clientX;\n } else {\n newWidth = containerRect.width - e.clientX;\n }\n\n // Convert to percentage\n const widthPercent = (newWidth / containerRect.width) * 100;\n\n // Parse min/max widths\n const minPercent = parseFloat(panelMinWidth);\n const maxPercent = parseFloat(panelMaxWidth);\n\n // Clamp width between min and max\n const clampedPercent = Math.max(minPercent, Math.min(maxPercent, widthPercent));\n const newWidthStr = `${clampedPercent}%`;\n\n setPanelWidth(newWidthStr);\n if (onPanelResize) {\n onPanelResize(newWidth);\n }\n };\n\n const handleMouseUp = () => {\n setIsResizing(false);\n };\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n }, [isResizing, isPanel, isLeftPanel, panelMinWidth, panelMaxWidth, onPanelResize]);\n\n // Check backend connection once on mount\n useEffect(() => {\n const checkConnection = async () => {\n try {\n const headers: Record<string, string> = {};\n const token = getAuthToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n const response = await fetch(`${apiEndpoint}/${agentId}/health`, { headers });\n if (response.ok) {\n setIsConnected(true);\n setError(null);\n } else {\n setError('Backend not responding');\n }\n } catch (err) {\n setError('Failed to connect to backend');\n setIsConnected(false);\n }\n };\n\n checkConnection();\n }, [apiEndpoint, agentId]);\n\n // Auto-scroll to bottom\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n // Refocus input after loading completes\n useEffect(() => {\n if (!isLoading && isConnected) {\n // Small delay to ensure DOM has updated\n setTimeout(() => {\n inputRef.current?.focus();\n }, 100);\n }\n }, [isLoading, isConnected]);\n\n const sendMessage = async () => {\n const message = inputValue.trim();\n if (!message || isLoading) return;\n\n // Add user message\n const userMessage: Message = {\n id: `${Date.now()}-user`,\n role: 'user',\n content: message,\n timestamp: new Date(),\n };\n\n setMessages(prev => [...prev, userMessage]);\n setInputValue('');\n setIsLoading(true);\n setError(null);\n\n try {\n // Helper to get fresh context (supports both static and function contexts)\n const getContext = () => typeof context === 'function' ? context() : context;\n\n // Prepare tool definitions (without handlers) for backend in OpenAI format\n const toolDefinitions = tools.map(({ name, description, parameters }) => ({\n type: 'function' as const,\n function: {\n name,\n description,\n parameters,\n },\n }));\n\n const response = await fetch(`${apiEndpoint}/${agentId}/chat`, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({\n message,\n conversationId,\n context: getContext(),\n tools: toolDefinitions.length > 0 ? toolDefinitions : undefined,\n disableCache: false,\n }),\n });\n\n if (!response.ok) {\n throw new Error('Failed to get response');\n }\n\n const data = await response.json();\n\n // Iterative tool execution loop\n // The AI may return tool calls that, once executed, lead to more tool calls.\n // We loop until the AI returns a text-only response or we hit maxToolRounds.\n let currentData = data;\n let round = 0;\n\n while (\n currentData.toolCalls &&\n currentData.toolCalls.length > 0 &&\n round < maxToolRounds\n ) {\n round++;\n\n // Execute all tool calls in parallel\n const results = await Promise.all(\n currentData.toolCalls.map(async (toolCall: any) => {\n const toolName = toolCall.function?.name || toolCall.name;\n const toolArgs = toolCall.function?.arguments\n ? JSON.parse(toolCall.function.arguments)\n : toolCall.parameters;\n\n const tool = tools.find(t => t.name === toolName);\n if (!tool) {\n return { success: false, error: `Tool '${toolName}' not found` };\n }\n try {\n const result = await Promise.resolve(tool.handler(toolArgs));\n return result;\n } catch (error: any) {\n console.error(`Tool '${tool.name}' failed:`, error);\n return { success: false, error: error.message };\n }\n })\n );\n\n // Format tool results as text message\n const toolResultsMessage = results\n .map((r: any, idx: number) => {\n const toolCall = currentData.toolCalls[idx];\n const toolName = toolCall.function?.name || toolCall.name;\n return `Tool \"${toolName}\" result: ${JSON.stringify(r)}`;\n })\n .join('\\n');\n\n // On last allowed round, omit tools to force a text-only response\n const isLastAllowedRound = round >= maxToolRounds;\n\n // Send tool results back with tools and fresh context\n const followUpResponse = await fetch(`${apiEndpoint}/${agentId}/chat`, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({\n message: toolResultsMessage,\n conversationId,\n context: getContext(),\n tools: isLastAllowedRound ? undefined : (toolDefinitions.length > 0 ? toolDefinitions : undefined),\n disableCache: false,\n }),\n });\n\n if (!followUpResponse.ok) {\n throw new Error('Failed to get AI response for tool results');\n }\n\n currentData = await followUpResponse.json();\n }\n\n // Display final text response\n const assistantMessage: Message = {\n id: `${Date.now()}-assistant`,\n role: 'assistant',\n content: currentData.message || 'I executed the tools successfully.',\n timestamp: new Date(),\n usage: currentData.usage,\n };\n\n setMessages(prev => [...prev, assistantMessage]);\n } catch (err) {\n console.error('❌ Error:', err);\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleKeyPress = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n sendMessage();\n }\n };\n\n const clearMessages = () => {\n setMessages([]);\n };\n\n const toggleFolded = () => {\n setIsFolded(!isFolded);\n };\n\n const positionClass = `inapp-ai-${position}`;\n // Don't add a theme class for 'light' since it's the default\n const themeClass = theme && theme !== 'light' ? `inapp-ai-theme-${theme}` : '';\n const modeClass = isEmbedded\n ? 'inapp-ai-embedded'\n : isSidebar\n ? `inapp-ai-sidebar inapp-ai-${displayMode}`\n : isPanel\n ? `inapp-ai-panel inapp-ai-${displayMode}`\n : 'inapp-ai-popup';\n const foldedClass = isFolded ? 'inapp-ai-folded' : '';\n\n // Build custom button style\n const buttonStyle: React.CSSProperties = {\n ...(customStyles.buttonBackgroundColor && { background: customStyles.buttonBackgroundColor }),\n ...(customStyles.buttonTextColor && { color: customStyles.buttonTextColor }),\n ...(customStyles.buttonSize && {\n width: customStyles.buttonSize,\n height: customStyles.buttonSize,\n fontSize: `calc(${customStyles.buttonSize} * 0.5)`\n }),\n ...(customStyles.buttonBorderRadius && { borderRadius: customStyles.buttonBorderRadius }),\n ...(customStyles.boxShadow && { boxShadow: customStyles.boxShadow }),\n };\n\n // Build custom window style\n const windowStyle: React.CSSProperties = {\n ...(customStyles.windowWidth && !isSidebar && !isPanel && { width: customStyles.windowWidth }),\n ...(customStyles.windowHeight && !isSidebar && !isPanel && { height: customStyles.windowHeight }),\n ...(customStyles.windowBorderRadius && { borderRadius: customStyles.windowBorderRadius }),\n ...(customStyles.fontFamily && { fontFamily: customStyles.fontFamily }),\n ...(customStyles.fontSize && { fontSize: customStyles.fontSize }),\n ...(customStyles.sidebarWidth && isSidebar && !isFolded && { width: customStyles.sidebarWidth }),\n ...(customStyles.sidebarFoldedWidth && isSidebar && isFolded && { width: customStyles.sidebarFoldedWidth }),\n ...(isPanel && { width: panelWidth }),\n };\n\n // For embedded mode, return the chat window directly without fragment\n // This ensures proper flex layout inheritance from parent\n if (isEmbedded && isOpen) {\n return (\n <div\n ref={resizeRef}\n role=\"region\"\n aria-label=\"AI Assistant Chat\"\n className={`inapp-ai-window inapp-ai-embedded ${themeClass}`}\n style={windowStyle}\n >\n {/* Header (can be hidden via showHeader prop) */}\n {showHeader && (\n <div className=\"inapp-ai-header\" style={{\n ...(customStyles.headerBackground && { background: customStyles.headerBackground }),\n ...(customStyles.headerTextColor && { color: customStyles.headerTextColor }),\n }}>\n <div className=\"inapp-ai-header-title\">\n <span className=\"inapp-ai-header-icon\">{customStyles.buttonIcon || '🤖'}</span>\n <div>\n <h3>{customStyles.headerTitle || 'AI Assistant'}</h3>\n <p>\n {isConnected ? (\n <span className=\"inapp-ai-status-connected\">\n <span className=\"inapp-ai-status-dot\" />\n Connected\n </span>\n ) : (\n <span className=\"inapp-ai-status-disconnected\">\n <span className=\"inapp-ai-status-dot\" />\n Disconnected\n </span>\n )}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Error Banner */}\n {error && (\n <ErrorMessage error={error} onDismiss={() => setError(null)} />\n )}\n\n {/* Messages */}\n <div\n className=\"inapp-ai-messages\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Chat messages\"\n >\n {messages.length === 0 ? (\n <div className=\"inapp-ai-empty-state\" role=\"status\">\n <div className=\"inapp-ai-empty-icon\" aria-hidden=\"true\">💬</div>\n <h4>Start a conversation</h4>\n <p>How can I help you today?</p>\n </div>\n ) : (\n messages.map((msg) => (\n <div\n key={msg.id}\n className={`inapp-ai-message inapp-ai-message-${msg.role}`}\n role=\"article\"\n aria-label={`${msg.role === 'user' ? 'User' : 'Assistant'} message`}\n >\n <div className=\"inapp-ai-message-icon\" aria-hidden=\"true\">\n {msg.role === 'user' ? '👤' : '🤖'}\n </div>\n <div className=\"inapp-ai-message-content\">\n <div className=\"inapp-ai-message-text\" style={{\n ...(msg.role === 'user' && customStyles.userMessageBackground && { background: customStyles.userMessageBackground }),\n ...(msg.role === 'user' && customStyles.userMessageColor && { color: customStyles.userMessageColor }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageBackground && { background: customStyles.assistantMessageBackground }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageColor && { color: customStyles.assistantMessageColor }),\n ...(customStyles.borderRadius && { borderRadius: customStyles.borderRadius }),\n }}>\n {msg.role === 'assistant' ? (\n <ReactMarkdown\n components={{\n code: CodeBlock as any,\n }}\n >\n {msg.content}\n </ReactMarkdown>\n ) : (\n msg.content\n )}\n </div>\n <div className=\"inapp-ai-message-time\">\n {msg.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : ''}\n {msg.usage && (\n <span className=\"inapp-ai-message-tokens\">\n {' • '}{msg.usage.totalTokens} tokens\n </span>\n )}\n </div>\n </div>\n </div>\n ))\n )}\n {isLoading && (\n <div className=\"inapp-ai-message inapp-ai-message-assistant\">\n <div className=\"inapp-ai-message-icon\">\n <div style={{ animation: 'pulse 2s infinite' }}>🤖</div>\n </div>\n <div className=\"inapp-ai-message-content\">\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div className=\"inapp-ai-typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n <LoadingSkeleton />\n </div>\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input Area */}\n <div className=\"inapp-ai-input-area\" role=\"form\" aria-label=\"Message input\">\n {messages.length > 0 && (\n <button\n className=\"inapp-ai-clear-btn\"\n onClick={clearMessages}\n disabled={isLoading}\n aria-label=\"Clear conversation history\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">🗑️</span> Clear\n </button>\n )}\n <div className=\"inapp-ai-input-wrapper\">\n <input\n ref={inputRef}\n type=\"text\"\n className=\"inapp-ai-input\"\n placeholder={customStyles.inputPlaceholder || \"Type your message...\"}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n disabled={isLoading || !isConnected}\n aria-label=\"Message input\"\n aria-describedby=\"send-hint\"\n tabIndex={0}\n style={{\n ...(customStyles.inputBackground && { background: customStyles.inputBackground }),\n ...(customStyles.inputBorderColor && { borderColor: customStyles.inputBorderColor }),\n ...(customStyles.inputTextColor && { color: customStyles.inputTextColor }),\n }}\n />\n <button\n className=\"inapp-ai-send-btn\"\n onClick={sendMessage}\n disabled={isLoading || !isConnected || !inputValue.trim()}\n aria-label=\"Send message\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">{isLoading ? '⏳' : '⬆'}</span>\n <span className=\"sr-only\">\n {isLoading ? 'Sending...' : 'Send message'}\n </span>\n </button>\n <span id=\"send-hint\" className=\"sr-only\">\n Press Enter to send\n </span>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <>\n {/* Chat Button (only for popup mode, not embedded) */}\n {!isSidebar && !isPanel && !isEmbedded && (\n <button\n className={`inapp-ai-button ${positionClass} ${themeClass}`}\n style={buttonStyle}\n onClick={() => setIsOpen(!isOpen)}\n aria-label={isOpen ? \"Close AI Assistant\" : \"Open AI Assistant\"}\n aria-expanded={isOpen}\n aria-haspopup=\"dialog\"\n tabIndex={0}\n >\n {isOpen ? '✕' : (customStyles.buttonIcon || '🤖')}\n {!isConnected && (\n <span\n className=\"inapp-ai-offline-indicator\"\n role=\"status\"\n aria-label=\"Backend disconnected\"\n />\n )}\n </button>\n )}\n\n {/* Chat Window */}\n {isOpen && (\n <div\n ref={resizeRef}\n role={isEmbedded ? \"region\" : \"dialog\"}\n aria-label=\"AI Assistant Chat\"\n aria-modal={!isSidebar && !isPanel && !isEmbedded ? \"true\" : undefined}\n className={`inapp-ai-window ${modeClass} ${isSidebar || isPanel || isEmbedded ? '' : positionClass} ${themeClass} ${foldedClass}`}\n style={windowStyle}\n >\n {/* Resize Handle for Panel */}\n {isPanel && (\n <div\n className={`inapp-ai-resize-handle ${isLeftPanel ? 'inapp-ai-resize-handle-right' : 'inapp-ai-resize-handle-left'}`}\n onMouseDown={() => setIsResizing(true)}\n title=\"Drag to resize\"\n />\n )}\n\n {/* Folded State Content */}\n {(isSidebar || isPanel) && isFolded ? (\n <div\n className=\"inapp-ai-folded-content\"\n onClick={toggleFolded}\n style={{ cursor: 'pointer' }}\n title=\"Click to unfold\"\n >\n <div className=\"inapp-ai-folded-icon\">\n {customStyles.buttonIcon || '🤖'}\n </div>\n <div className=\"inapp-ai-folded-text\">\n AI\n </div>\n {messages.length > 0 && (\n <div className=\"inapp-ai-message-count\">\n {messages.length}\n </div>\n )}\n </div>\n ) : (\n <>\n {/* Header (can be hidden via showHeader prop) */}\n {showHeader && (\n <div className=\"inapp-ai-header\" style={{\n ...(customStyles.headerBackground && { background: customStyles.headerBackground }),\n ...(customStyles.headerTextColor && { color: customStyles.headerTextColor }),\n }}>\n <div className=\"inapp-ai-header-title\">\n <span className=\"inapp-ai-header-icon\">{customStyles.buttonIcon || '🤖'}</span>\n <div>\n <h3>{customStyles.headerTitle || 'AI Assistant'}</h3>\n <p>\n {isConnected ? (\n <span className=\"inapp-ai-status-connected\">\n <span className=\"inapp-ai-status-dot\" />\n Connected\n </span>\n ) : (\n <span className=\"inapp-ai-status-disconnected\">\n <span className=\"inapp-ai-status-dot\" />\n Disconnected\n </span>\n )}\n </p>\n </div>\n </div>\n {/* Fold button for panels (in header top right) */}\n {(isSidebar || isPanel) && (\n <button\n className=\"inapp-ai-header-fold-btn\"\n onClick={toggleFolded}\n aria-label={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n title={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n >\n {(isLeftSidebar || isLeftPanel) ? '◀' : '▶'}\n </button>\n )}\n {/* Close button for popup mode */}\n {!isSidebar && !isPanel && !isEmbedded && (\n <button\n className=\"inapp-ai-close-btn\"\n onClick={() => setIsOpen(false)}\n aria-label=\"Close\"\n >\n ✕\n </button>\n )}\n </div>\n )}\n\n {/* Error Banner */}\n {error && (\n <ErrorMessage error={error} onDismiss={() => setError(null)} />\n )}\n\n {/* Messages */}\n <div\n className=\"inapp-ai-messages\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Chat messages\"\n >\n {messages.length === 0 ? (\n <div className=\"inapp-ai-empty-state\" role=\"status\">\n <div className=\"inapp-ai-empty-icon\" aria-hidden=\"true\">💬</div>\n <h4>Start a conversation</h4>\n <p>How can I help you today?</p>\n </div>\n ) : (\n messages.map((msg) => (\n <div\n key={msg.id}\n className={`inapp-ai-message inapp-ai-message-${msg.role}`}\n role=\"article\"\n aria-label={`${msg.role === 'user' ? 'User' : 'Assistant'} message`}\n >\n <div className=\"inapp-ai-message-icon\" aria-hidden=\"true\">\n {msg.role === 'user' ? '👤' : '🤖'}\n </div>\n <div className=\"inapp-ai-message-content\">\n <div className=\"inapp-ai-message-text\" style={{\n ...(msg.role === 'user' && customStyles.userMessageBackground && { background: customStyles.userMessageBackground }),\n ...(msg.role === 'user' && customStyles.userMessageColor && { color: customStyles.userMessageColor }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageBackground && { background: customStyles.assistantMessageBackground }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageColor && { color: customStyles.assistantMessageColor }),\n ...(customStyles.borderRadius && { borderRadius: customStyles.borderRadius }),\n }}>\n {msg.role === 'assistant' ? (\n <ReactMarkdown\n components={{\n code: CodeBlock as any,\n }}\n >\n {msg.content}\n </ReactMarkdown>\n ) : (\n msg.content\n )}\n </div>\n <div className=\"inapp-ai-message-time\">\n {msg.timestamp?.toLocaleTimeString() || ''}\n {msg.usage && (\n <span className=\"inapp-ai-message-tokens\">\n {' • '}{msg.usage.totalTokens} tokens\n </span>\n )}\n </div>\n </div>\n </div>\n ))\n )}\n {isLoading && (\n <div className=\"inapp-ai-message inapp-ai-message-assistant\">\n <div className=\"inapp-ai-message-icon\">\n <div style={{ animation: 'pulse 2s infinite' }}>🤖</div>\n </div>\n <div className=\"inapp-ai-message-content\">\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div className=\"inapp-ai-typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n <LoadingSkeleton />\n </div>\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input Area */}\n <div className=\"inapp-ai-input-area\" role=\"form\" aria-label=\"Message input\">\n {messages.length > 0 && (\n <button\n className=\"inapp-ai-clear-btn\"\n onClick={clearMessages}\n disabled={isLoading}\n aria-label=\"Clear conversation history\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">🗑️</span> Clear\n </button>\n )}\n <div className=\"inapp-ai-input-wrapper\">\n <input\n ref={inputRef}\n type=\"text\"\n className=\"inapp-ai-input\"\n placeholder={customStyles.inputPlaceholder || \"Type your message...\"}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n disabled={isLoading || !isConnected}\n aria-label=\"Message input\"\n aria-describedby=\"send-hint\"\n tabIndex={0}\n style={{\n ...(customStyles.inputBackground && { background: customStyles.inputBackground }),\n ...(customStyles.inputBorderColor && { borderColor: customStyles.inputBorderColor }),\n ...(customStyles.inputTextColor && { color: customStyles.inputTextColor }),\n }}\n />\n <button\n className=\"inapp-ai-send-btn\"\n onClick={sendMessage}\n disabled={isLoading || !isConnected || !inputValue.trim()}\n aria-label=\"Send message\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">{isLoading ? '⏳' : '⬆'}</span>\n <span className=\"sr-only\">\n {isLoading ? 'Sending...' : 'Send message'}\n </span>\n </button>\n <span id=\"send-hint\" className=\"sr-only\">\n Press Enter to send\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n )}\n </>\n );\n}\n","import { useState, useCallback, useEffect, useRef } from 'react';\nimport type { Tool } from '../types';\n\n/**\n * Options for the useTools hook\n */\nexport interface UseToolsOptions {\n /**\n * Initial tools to register\n */\n initialTools?: Tool[];\n\n /**\n * Whether to automatically cleanup tools on unmount\n * @default true\n */\n autoCleanup?: boolean;\n}\n\n/**\n * Return value from the useTools hook\n */\nexport interface UseToolsReturn {\n /**\n * Current array of registered tools\n */\n tools: Tool[];\n\n /**\n * Register a new tool\n * @param tool - Tool to register\n * @throws {Error} If tool with same name already exists\n */\n registerTool: (tool: Tool) => void;\n\n /**\n * Unregister a tool by name\n * @param name - Name of the tool to unregister\n */\n unregisterTool: (name: string) => void;\n\n /**\n * Clear all registered tools\n */\n clearTools: () => void;\n\n /**\n * Check if a tool with the given name exists\n * @param name - Tool name to check\n * @returns true if tool exists, false otherwise\n */\n hasTool: (name: string) => boolean;\n}\n\n/**\n * Hook for managing tools dynamically in React components\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { tools, registerTool, unregisterTool } = useTools();\n * const [todos, setTodos] = useState([]);\n *\n * useEffect(() => {\n * registerTool({\n * name: 'addTodo',\n * description: 'Add a new todo',\n * parameters: {\n * type: 'object',\n * properties: {\n * task: { type: 'string' }\n * }\n * },\n * handler: async ({ task }) => {\n * setTodos(prev => [...prev, { id: Date.now(), text: task }]);\n * return { success: true };\n * }\n * });\n *\n * return () => unregisterTool('addTodo');\n * }, [registerTool, unregisterTool, setTodos]);\n *\n * return <InAppAI endpoint=\"...\" tools={tools} />;\n * }\n * ```\n *\n * @param options - Configuration options\n * @returns Tool management functions and current tools array\n */\nexport function useTools(options: UseToolsOptions = {}): UseToolsReturn {\n const { initialTools = [], autoCleanup = true } = options;\n\n // Use ref to track tool names for validation\n const toolNamesRef = useRef<Set<string>>(new Set());\n\n // State for tools array\n const [tools, setTools] = useState<Tool[]>(() => {\n // Initialize with initial tools\n initialTools.forEach(tool => {\n if (toolNamesRef.current.has(tool.name)) {\n console.warn(\n `[useTools] Duplicate tool name in initialTools: \"${tool.name}\". ` +\n `Only the first occurrence will be used.`\n );\n } else {\n toolNamesRef.current.add(tool.name);\n }\n });\n\n return initialTools.filter((tool, index) => {\n // Filter out duplicates\n return initialTools.findIndex(t => t.name === tool.name) === index;\n });\n });\n\n /**\n * Register a new tool\n */\n const registerTool = useCallback((tool: Tool) => {\n if (!tool.name) {\n throw new Error('[useTools] Tool must have a name');\n }\n\n if (toolNamesRef.current.has(tool.name)) {\n console.warn(\n `[useTools] Tool \"${tool.name}\" is already registered. ` +\n `Use unregisterTool() first if you want to replace it.`\n );\n return;\n }\n\n // Validate tool structure\n if (!tool.description) {\n console.warn(`[useTools] Tool \"${tool.name}\" is missing description`);\n }\n\n if (!tool.parameters) {\n console.warn(`[useTools] Tool \"${tool.name}\" is missing parameters schema`);\n }\n\n if (typeof tool.handler !== 'function') {\n throw new Error(`[useTools] Tool \"${tool.name}\" must have a handler function`);\n }\n\n toolNamesRef.current.add(tool.name);\n setTools(prev => [...prev, tool]);\n }, []);\n\n /**\n * Unregister a tool by name\n */\n const unregisterTool = useCallback((name: string) => {\n if (!toolNamesRef.current.has(name)) {\n console.warn(`[useTools] Tool \"${name}\" is not registered`);\n return;\n }\n\n toolNamesRef.current.delete(name);\n setTools(prev => prev.filter(tool => tool.name !== name));\n }, []);\n\n /**\n * Clear all tools\n */\n const clearTools = useCallback(() => {\n toolNamesRef.current.clear();\n setTools([]);\n }, []);\n\n /**\n * Check if a tool exists\n */\n const hasTool = useCallback((name: string): boolean => {\n return toolNamesRef.current.has(name);\n }, []);\n\n /**\n * Cleanup on unmount if autoCleanup is enabled\n */\n useEffect(() => {\n if (autoCleanup) {\n return () => {\n toolNamesRef.current.clear();\n };\n }\n }, [autoCleanup]);\n\n return {\n tools,\n registerTool,\n unregisterTool,\n clearTools,\n hasTool,\n };\n}\n","import React, { createContext, useContext, useState, useCallback, useMemo } from 'react';\nimport type { Tool } from '../types';\n\n/**\n * Tool registry interface\n */\nexport interface ToolRegistry {\n /**\n * Register tools under a namespace\n * @param namespace - Unique namespace identifier\n * @param tools - Array of tools to register\n */\n register(namespace: string, tools: Tool[]): void;\n\n /**\n * Unregister all tools for a namespace\n * @param namespace - Namespace to unregister\n */\n unregister(namespace: string): void;\n\n /**\n * Get tools for a specific namespace\n * @param namespace - Namespace to query\n * @returns Array of tools for the namespace, or empty array if not found\n */\n getTools(namespace: string): Tool[];\n\n /**\n * Get all registered tools from all namespaces\n * @returns Combined array of all tools\n */\n getAllTools(): Tool[];\n\n /**\n * Clear all registered tools from all namespaces\n */\n clear(): void;\n\n /**\n * Get all registered namespace names\n * @returns Array of namespace names\n */\n getNamespaces(): string[];\n}\n\n/**\n * Context for the tool registry\n */\nconst ToolRegistryContext = createContext<ToolRegistry | null>(null);\n\n/**\n * Props for ToolRegistryProvider\n */\nexport interface ToolRegistryProviderProps {\n /**\n * Child components\n */\n children: React.ReactNode;\n\n /**\n * Initial tools organized by namespace\n */\n initialTools?: Record<string, Tool[]>;\n}\n\n/**\n * Provider component for global tool registry\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <ToolRegistryProvider>\n * <MyApp />\n * </ToolRegistryProvider>\n * );\n * }\n * ```\n */\nexport function ToolRegistryProvider({\n children,\n initialTools = {},\n}: ToolRegistryProviderProps): React.ReactElement {\n // State: Map of namespace -> tools\n const [toolsMap, setToolsMap] = useState<Map<string, Tool[]>>(\n () => new Map(Object.entries(initialTools))\n );\n\n /**\n * Register tools under a namespace\n */\n const register = useCallback((namespace: string, tools: Tool[]) => {\n // Validate namespace\n if (!namespace || typeof namespace !== 'string') {\n throw new Error('[ToolRegistry] Namespace must be a non-empty string');\n }\n\n if (!/^[a-zA-Z0-9_-]+$/.test(namespace)) {\n throw new Error(\n `[ToolRegistry] Invalid namespace \"${namespace}\". ` +\n `Use only alphanumeric characters, hyphens, and underscores.`\n );\n }\n\n // Validate tools\n if (!Array.isArray(tools)) {\n throw new Error('[ToolRegistry] Tools must be an array');\n }\n\n // Check for duplicate tool names within the namespace\n const toolNames = new Set<string>();\n tools.forEach(tool => {\n if (toolNames.has(tool.name)) {\n console.warn(\n `[ToolRegistry] Duplicate tool name in namespace \"${namespace}\": \"${tool.name}\"`\n );\n }\n toolNames.add(tool.name);\n });\n\n setToolsMap(prev => {\n const next = new Map(prev);\n next.set(namespace, tools);\n return next;\n });\n }, []);\n\n /**\n * Unregister a namespace\n */\n const unregister = useCallback((namespace: string) => {\n setToolsMap(prev => {\n if (!prev.has(namespace)) {\n console.warn(`[ToolRegistry] Namespace \"${namespace}\" is not registered`);\n return prev;\n }\n\n const next = new Map(prev);\n next.delete(namespace);\n return next;\n });\n }, []);\n\n /**\n * Get tools for a specific namespace\n */\n const getTools = useCallback(\n (namespace: string): Tool[] => {\n return toolsMap.get(namespace) || [];\n },\n [toolsMap]\n );\n\n /**\n * Get all tools from all namespaces\n */\n const getAllTools = useCallback((): Tool[] => {\n const allTools: Tool[] = [];\n const seenNames = new Set<string>();\n\n // Iterate through namespaces\n for (const [namespace, tools] of toolsMap.entries()) {\n for (const tool of tools) {\n // Warn about name conflicts across namespaces\n if (seenNames.has(tool.name)) {\n console.warn(\n `[ToolRegistry] Tool name conflict: \"${tool.name}\" exists in multiple namespaces. ` +\n `Only the first occurrence will be used.`\n );\n continue;\n }\n\n seenNames.add(tool.name);\n allTools.push(tool);\n }\n }\n\n return allTools;\n }, [toolsMap]);\n\n /**\n * Clear all tools\n */\n const clear = useCallback(() => {\n setToolsMap(new Map());\n }, []);\n\n /**\n * Get all namespace names\n */\n const getNamespaces = useCallback((): string[] => {\n return Array.from(toolsMap.keys());\n }, [toolsMap]);\n\n // Memoize the registry value\n const registry = useMemo<ToolRegistry>(\n () => ({\n register,\n unregister,\n getTools,\n getAllTools,\n clear,\n getNamespaces,\n }),\n [register, unregister, getTools, getAllTools, clear, getNamespaces]\n );\n\n return (\n <ToolRegistryContext.Provider value={registry}>\n {children}\n </ToolRegistryContext.Provider>\n );\n}\n\n/**\n * Hook to access the tool registry\n *\n * @example\n * ```tsx\n * function TodoPage() {\n * const registry = useToolRegistry();\n * const [todos, setTodos] = useState([]);\n *\n * useEffect(() => {\n * registry.register('todos', [\n * {\n * name: 'addTodo',\n * description: 'Add a todo',\n * parameters: { type: 'object', properties: { task: { type: 'string' } } },\n * handler: async ({ task }) => {\n * setTodos(prev => [...prev, { id: Date.now(), text: task }]);\n * return { success: true };\n * }\n * }\n * ]);\n *\n * return () => registry.unregister('todos');\n * }, [registry, todos, setTodos]);\n *\n * return <TodoList />;\n * }\n *\n * function ChatWidget() {\n * const registry = useToolRegistry();\n * const allTools = registry.getAllTools();\n *\n * return <InAppAI endpoint=\"...\" tools={allTools} />;\n * }\n * ```\n *\n * @throws {Error} If used outside of ToolRegistryProvider\n * @returns Tool registry instance\n */\nexport function useToolRegistry(): ToolRegistry {\n const registry = useContext(ToolRegistryContext);\n\n if (!registry) {\n throw new Error(\n '[useToolRegistry] must be used within a ToolRegistryProvider. ' +\n 'Wrap your component tree with <ToolRegistryProvider>.'\n );\n }\n\n return registry;\n}\n"],"mappings":"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","names":["useState","useEffect","useRef","ReactMarkdown","SyntaxHighlighter","vscDarkPlus","Fragment","jsx","jsxs","LoadingSkeleton","ErrorMessage","error","onDismiss","errorInfo","msg","CodeBlock","inline","className","children","props","copied","setCopied","useState","match","language","codeString","handleCopy","SyntaxHighlighter","vscDarkPlus","InAppAI","endpoint","agentId","position","displayMode","defaultFolded","theme","context","customStyles","tools","panelMinWidth","panelMaxWidth","panelDefaultWidth","onPanelResize","externalConversationId","externalMessages","onMessagesChange","showHeader","authToken","maxToolRounds","apiEndpoint","getAuthToken","buildHeaders","headers","token","externalMessagesRef","useRef","useEffect","messages","setMessages","updater","currentMessages","newMessages","isOpen","setIsOpen","isFolded","setIsFolded","inputValue","setInputValue","isLoading","setIsLoading","isConnected","setIsConnected","setError","panelWidth","setPanelWidth","isResizing","setIsResizing","messagesEndRef","internalConversationId","conversationId","resizeRef","inputRef","isEmbedded","isSidebar","isPanel","isLeftSidebar","isLeftPanel","handleMouseMove","e","container","containerRect","newWidth","widthPercent","minPercent","maxPercent","newWidthStr","handleMouseUp","sendMessage","message","userMessage","prev","getContext","toolDefinitions","name","description","parameters","response","currentData","round","toolResultsMessage","toolCall","toolName","toolArgs","tool","t","r","idx","isLastAllowedRound","followUpResponse","assistantMessage","err","handleKeyPress","clearMessages","toggleFolded","positionClass","themeClass","modeClass","foldedClass","buttonStyle","windowStyle","ReactMarkdown","useState","useCallback","useEffect","useRef","useTools","options","initialTools","autoCleanup","toolNamesRef","tools","setTools","tool","index","t","registerTool","prev","unregisterTool","name","clearTools","hasTool","createContext","useContext","useState","useCallback","useMemo","jsx","ToolRegistryContext","ToolRegistryProvider","children","initialTools","toolsMap","setToolsMap","register","namespace","tools","toolNames","tool","prev","next","unregister","getTools","getAllTools","allTools","seenNames","clear","getNamespaces","registry","useToolRegistry"]}
1
+ {"version":3,"sources":["../src/components/InAppAI.tsx","../src/hooks/useTools.ts","../src/hooks/useToolRegistry.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';\nimport type { CustomStyles, Tool, InAppAIProps, Message } from '../types';\nimport './themes.css';\nimport './InAppAI.css';\n\n// Internal message type (extends exported Message with required timestamp)\ninterface InternalMessage extends Message {\n timestamp: Date;\n}\n\n// Loading skeleton component\nfunction LoadingSkeleton() {\n return (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '8px',\n padding: '12px 16px',\n background: 'var(--inapp-ai-assistant-bg)',\n borderRadius: 'var(--inapp-ai-border-radius)',\n }}\n >\n <div\n style={{\n height: '14px',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '90%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.1s',\n borderRadius: '4px',\n }}\n />\n <div\n style={{\n height: '14px',\n width: '75%',\n background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',\n backgroundSize: '200% 100%',\n animation: 'shimmer 1.5s infinite',\n animationDelay: '0.2s',\n borderRadius: '4px',\n }}\n />\n <style>{`\n @keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n `}</style>\n </div>\n );\n}\n\n// Error message component with enhanced display\ninterface ErrorMessageProps {\n error: string;\n onDismiss: () => void;\n}\n\nfunction ErrorMessage({ error, onDismiss }: ErrorMessageProps) {\n // Determine error type based on message\n const getErrorType = (msg: string) => {\n if (msg.includes('not responding') || msg.includes('connection') || msg.includes('network')) {\n return { type: 'connection', icon: '🔌', title: 'Connection Error' };\n }\n if (msg.includes('timeout')) {\n return { type: 'timeout', icon: '⏱️', title: 'Request Timeout' };\n }\n if (msg.includes('rate limit')) {\n return { type: 'rateLimit', icon: '🚦', title: 'Rate Limit' };\n }\n if (msg.includes('authentication') || msg.includes('unauthorized')) {\n return { type: 'auth', icon: '🔒', title: 'Authentication Error' };\n }\n return { type: 'generic', icon: '⚠️', title: 'Error' };\n };\n\n const errorInfo = getErrorType(error);\n\n return (\n <div\n className=\"inapp-ai-error-banner\"\n role=\"alert\"\n aria-live=\"assertive\"\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n gap: '12px',\n padding: '14px 16px',\n background: 'linear-gradient(135deg, #fff3cd 0%, #ffe9a6 100%)',\n borderLeft: '4px solid #ff9800',\n borderRadius: '0',\n }}\n >\n <span style={{ fontSize: '20px' }}>{errorInfo.icon}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontWeight: 600, marginBottom: '4px', color: '#856404' }}>\n {errorInfo.title}\n </div>\n <div style={{ fontSize: '13px', color: '#856404', wordBreak: 'break-word' }}>\n {error}\n </div>\n {errorInfo.type === 'connection' && (\n <div style={{ fontSize: '12px', marginTop: '6px', color: '#997404' }}>\n 💡 Make sure the backend server is running on the correct port\n </div>\n )}\n </div>\n <button\n onClick={onDismiss}\n style={{\n background: 'none',\n border: 'none',\n color: '#856404',\n cursor: 'pointer',\n padding: '4px 8px',\n fontSize: '16px',\n lineHeight: 1,\n }}\n aria-label=\"Dismiss error\"\n >\n ✕\n </button>\n </div>\n );\n}\n\n// Code block component with syntax highlighting and copy button\ninterface CodeBlockProps {\n inline?: boolean;\n className?: string;\n children?: React.ReactNode;\n}\n\nfunction CodeBlock({ inline, className, children, ...props }: CodeBlockProps) {\n const [copied, setCopied] = useState(false);\n\n // Extract language from className (format: language-js, language-python, etc.)\n const match = /language-(\\w+)/.exec(className || '');\n const language = match ? match[1] : '';\n\n const codeString = String(children).replace(/\\n$/, '');\n\n const handleCopy = () => {\n navigator.clipboard.writeText(codeString);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n // Inline code (backticks)\n if (inline) {\n return <code className={className} {...props}>{children}</code>;\n }\n\n // Code block with syntax highlighting\n return (\n <div style={{ position: 'relative', marginTop: '8px', marginBottom: '8px' }}>\n <button\n onClick={handleCopy}\n style={{\n position: 'absolute',\n top: '8px',\n right: '8px',\n padding: '4px 8px',\n background: copied ? '#28a745' : 'rgba(255, 255, 255, 0.1)',\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n zIndex: 1,\n transition: 'background 0.2s',\n }}\n aria-label=\"Copy code\"\n >\n {copied ? '✓ Copied!' : '📋 Copy'}\n </button>\n {/* @ts-ignore - React 18 JSX type compatibility */}\n <SyntaxHighlighter\n language={language || 'text'}\n style={vscDarkPlus}\n customStyle={{\n borderRadius: '8px',\n padding: '16px',\n fontSize: '13px',\n marginTop: 0,\n marginBottom: 0,\n }}\n {...props}\n >\n {codeString}\n </SyntaxHighlighter>\n </div>\n );\n}\n\nexport function InAppAI({\n endpoint,\n agentId,\n position = 'bottom-right',\n displayMode = 'popup',\n defaultFolded = false,\n theme = 'light',\n context,\n customStyles = {},\n tools = [],\n panelMinWidth = '20%',\n panelMaxWidth = '33.33%',\n panelDefaultWidth = '25%',\n onPanelResize,\n // Controlled mode props\n conversationId: externalConversationId,\n messages: externalMessages,\n onMessagesChange,\n showHeader = true,\n // Authentication\n authToken,\n // Tool execution\n maxToolRounds = 10,\n}: InAppAIProps) {\n // Require controlled mode - messages and onMessagesChange are required\n if (externalMessages === undefined || onMessagesChange === undefined) {\n throw new Error(\n 'InAppAI requires controlled mode. Please provide both `messages` and `onMessagesChange` props. ' +\n 'See documentation for usage examples.'\n );\n }\n\n // Determine API endpoint (environment variable > prop > default)\n const apiEndpoint = endpoint ||\n (typeof process !== 'undefined' && (process as any).env?.REACT_APP_INAPPAI_ENDPOINT) ||\n (typeof (import.meta as any).env !== 'undefined' && (import.meta as any).env?.VITE_INAPPAI_ENDPOINT) ||\n 'https://api.inappai.com/api';\n\n // Helper to get auth token (supports both static string and function)\n const getAuthToken = (): string | null => {\n if (!authToken) return null;\n const token = typeof authToken === 'function' ? authToken() : authToken;\n return token || null;\n };\n\n // Helper to build headers with optional Authorization\n const buildHeaders = (): Record<string, string> => {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const token = getAuthToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n return headers;\n };\n\n // Keep a ref to the latest external messages to avoid stale closures\n const externalMessagesRef = useRef<Message[]>(externalMessages);\n useEffect(() => {\n externalMessagesRef.current = externalMessages;\n }, [externalMessages]);\n\n // Messages from props, setMessages calls onMessagesChange\n const messages = externalMessages;\n const setMessages = (updater: Message[] | ((prev: Message[]) => Message[])) => {\n const currentMessages = externalMessagesRef.current;\n const newMessages = typeof updater === 'function' ? updater(currentMessages) : updater;\n externalMessagesRef.current = newMessages; // Update ref immediately for chained calls\n onMessagesChange(newMessages);\n };\n\n const [isOpen, setIsOpen] = useState(displayMode.startsWith('sidebar') || displayMode.startsWith('panel') || displayMode === 'embedded');\n const [isFolded, setIsFolded] = useState(defaultFolded && (displayMode.startsWith('sidebar') || displayMode.startsWith('panel')));\n const [inputValue, setInputValue] = useState('');\n const [isLoading, setIsLoading] = useState(false);\n const [isConnected, setIsConnected] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [panelWidth, setPanelWidth] = useState(panelDefaultWidth);\n const [isResizing, setIsResizing] = useState(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const internalConversationId = useRef(`react-demo-${Date.now()}`);\n const conversationId = externalConversationId || internalConversationId.current;\n const resizeRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const isEmbedded = displayMode === 'embedded';\n const isSidebar = displayMode.startsWith('sidebar');\n const isPanel = displayMode.startsWith('panel');\n const isLeftSidebar = displayMode === 'sidebar-left';\n // const isRightSidebar = displayMode === 'sidebar-right';\n const isLeftPanel = displayMode === 'panel-left';\n // const isRightPanel = displayMode === 'panel-right';\n\n // For sidebar, panel, and embedded mode, always stay open\n useEffect(() => {\n if (isSidebar || isPanel || isEmbedded) {\n setIsOpen(true);\n }\n }, [isSidebar, isPanel, isEmbedded]);\n\n // Panel resize handlers\n useEffect(() => {\n if (!isPanel || !isResizing) return;\n\n const handleMouseMove = (e: MouseEvent) => {\n const container = resizeRef.current?.parentElement;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n let newWidth: number;\n\n if (isLeftPanel) {\n newWidth = e.clientX;\n } else {\n newWidth = containerRect.width - e.clientX;\n }\n\n // Convert to percentage\n const widthPercent = (newWidth / containerRect.width) * 100;\n\n // Parse min/max widths\n const minPercent = parseFloat(panelMinWidth);\n const maxPercent = parseFloat(panelMaxWidth);\n\n // Clamp width between min and max\n const clampedPercent = Math.max(minPercent, Math.min(maxPercent, widthPercent));\n const newWidthStr = `${clampedPercent}%`;\n\n setPanelWidth(newWidthStr);\n if (onPanelResize) {\n onPanelResize(newWidth);\n }\n };\n\n const handleMouseUp = () => {\n setIsResizing(false);\n };\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n }, [isResizing, isPanel, isLeftPanel, panelMinWidth, panelMaxWidth, onPanelResize]);\n\n // Check backend connection once on mount\n useEffect(() => {\n const checkConnection = async () => {\n try {\n const headers: Record<string, string> = {};\n const token = getAuthToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n const response = await fetch(`${apiEndpoint}/${agentId}/health`, { headers });\n if (response.ok) {\n setIsConnected(true);\n setError(null);\n } else {\n setError('Backend not responding');\n }\n } catch (err) {\n setError('Failed to connect to backend');\n setIsConnected(false);\n }\n };\n\n checkConnection();\n }, [apiEndpoint, agentId]);\n\n // Auto-scroll to bottom\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n // Refocus input after loading completes\n useEffect(() => {\n if (!isLoading && isConnected) {\n // Small delay to ensure DOM has updated\n setTimeout(() => {\n inputRef.current?.focus();\n }, 100);\n }\n }, [isLoading, isConnected]);\n\n const sendMessage = async () => {\n const message = inputValue.trim();\n if (!message || isLoading) return;\n\n // Add user message\n const userMessage: Message = {\n id: `${Date.now()}-user`,\n role: 'user',\n content: message,\n timestamp: new Date(),\n };\n\n setMessages(prev => [...prev, userMessage]);\n setInputValue('');\n setIsLoading(true);\n setError(null);\n\n try {\n // Helper to get fresh context (supports both static and function contexts)\n const getContext = () => typeof context === 'function' ? context() : context;\n\n // Prepare tool definitions (without handlers) for backend in OpenAI format\n const toolDefinitions = tools.map(({ name, description, parameters }) => ({\n type: 'function' as const,\n function: {\n name,\n description,\n parameters,\n },\n }));\n\n // Build compact conversation history with tool action context\n const buildMessagesPayload = () => messages.map(m => {\n const msg: { role: string; content: string } = {\n role: m.role,\n content: m.content,\n };\n if (m.toolActions && m.toolActions.length > 0) {\n const actionsSummary = m.toolActions\n .map(a => `${a.tool}(${JSON.stringify(a.args)}) → ${JSON.stringify(a.result)}`)\n .join(', ');\n msg.content += `\\n\\n[Tool actions: ${actionsSummary}]`;\n }\n return msg;\n });\n\n const response = await fetch(`${apiEndpoint}/${agentId}/chat`, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({\n message,\n messages: buildMessagesPayload(),\n conversationId,\n context: getContext(),\n tools: toolDefinitions.length > 0 ? toolDefinitions : undefined,\n disableCache: false,\n }),\n });\n\n if (!response.ok) {\n throw new Error('Failed to get response');\n }\n\n const data = await response.json();\n\n // Iterative tool execution loop\n // The AI may return tool calls that, once executed, lead to more tool calls.\n // We loop until the AI returns a text-only response or we hit maxToolRounds.\n let currentData = data;\n let round = 0;\n const allToolActions: Array<{ tool: string; args: Record<string, any>; result: any }> = [];\n\n while (\n currentData.toolCalls &&\n currentData.toolCalls.length > 0 &&\n round < maxToolRounds\n ) {\n round++;\n\n // Execute all tool calls in parallel\n const results = await Promise.all(\n currentData.toolCalls.map(async (toolCall: any) => {\n const toolName = toolCall.function?.name || toolCall.name;\n const toolArgs = toolCall.function?.arguments\n ? JSON.parse(toolCall.function.arguments)\n : toolCall.parameters;\n\n const tool = tools.find(t => t.name === toolName);\n if (!tool) {\n return { success: false, error: `Tool '${toolName}' not found` };\n }\n try {\n const result = await Promise.resolve(tool.handler(toolArgs));\n return result;\n } catch (error: any) {\n console.error(`Tool '${tool.name}' failed:`, error);\n return { success: false, error: error.message };\n }\n })\n );\n\n // Collect tool actions for conversation memory\n results.forEach((result: any, idx: number) => {\n const toolCall = currentData.toolCalls[idx];\n const toolName = toolCall.function?.name || toolCall.name;\n const toolArgs = toolCall.function?.arguments\n ? JSON.parse(toolCall.function.arguments)\n : toolCall.parameters || {};\n allToolActions.push({ tool: toolName, args: toolArgs, result });\n });\n\n // Format tool results with clear completion markers\n const toolResultLines = results\n .map((r: any, idx: number) => {\n const toolCall = currentData.toolCalls[idx];\n const toolName = toolCall.function?.name || toolCall.name;\n return `Tool \"${toolName}\" result: ${JSON.stringify(r)}`;\n })\n .join('\\n');\n\n const toolResultsMessage = `[TOOL EXECUTION COMPLETE - Round ${round}]\\n` +\n `The following ${results.length} tool call(s) have been executed successfully. Do NOT re-execute them.\\n` +\n toolResultLines + '\\n' +\n `If all requested actions are complete, respond to the user with a summary. Only make additional tool calls if new/different actions are needed.`;\n\n // On last allowed round, omit tools to force a text-only response\n const isLastAllowedRound = round >= maxToolRounds;\n\n // Send tool results back with tools, fresh context, and conversation history\n const followUpResponse = await fetch(`${apiEndpoint}/${agentId}/chat`, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({\n message: toolResultsMessage,\n messages: buildMessagesPayload(),\n conversationId,\n context: getContext(),\n tools: isLastAllowedRound ? undefined : (toolDefinitions.length > 0 ? toolDefinitions : undefined),\n disableCache: false,\n }),\n });\n\n if (!followUpResponse.ok) {\n throw new Error('Failed to get AI response for tool results');\n }\n\n currentData = await followUpResponse.json();\n }\n\n // Display final text response\n const assistantMessage: Message = {\n id: `${Date.now()}-assistant`,\n role: 'assistant',\n content: currentData.message || 'I executed the tools successfully.',\n timestamp: new Date(),\n usage: currentData.usage,\n toolActions: allToolActions.length > 0 ? allToolActions : undefined,\n };\n\n setMessages(prev => [...prev, assistantMessage]);\n } catch (err) {\n console.error('❌ Error:', err);\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleKeyPress = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n sendMessage();\n }\n };\n\n const clearMessages = () => {\n setMessages([]);\n };\n\n const toggleFolded = () => {\n setIsFolded(!isFolded);\n };\n\n const positionClass = `inapp-ai-${position}`;\n // Don't add a theme class for 'light' since it's the default\n const themeClass = theme && theme !== 'light' ? `inapp-ai-theme-${theme}` : '';\n const modeClass = isEmbedded\n ? 'inapp-ai-embedded'\n : isSidebar\n ? `inapp-ai-sidebar inapp-ai-${displayMode}`\n : isPanel\n ? `inapp-ai-panel inapp-ai-${displayMode}`\n : 'inapp-ai-popup';\n const foldedClass = isFolded ? 'inapp-ai-folded' : '';\n\n // Build custom button style\n const buttonStyle: React.CSSProperties = {\n ...(customStyles.buttonBackgroundColor && { background: customStyles.buttonBackgroundColor }),\n ...(customStyles.buttonTextColor && { color: customStyles.buttonTextColor }),\n ...(customStyles.buttonSize && {\n width: customStyles.buttonSize,\n height: customStyles.buttonSize,\n fontSize: `calc(${customStyles.buttonSize} * 0.5)`\n }),\n ...(customStyles.buttonBorderRadius && { borderRadius: customStyles.buttonBorderRadius }),\n ...(customStyles.boxShadow && { boxShadow: customStyles.boxShadow }),\n };\n\n // Build custom window style\n const windowStyle: React.CSSProperties = {\n ...(customStyles.windowWidth && !isSidebar && !isPanel && { width: customStyles.windowWidth }),\n ...(customStyles.windowHeight && !isSidebar && !isPanel && { height: customStyles.windowHeight }),\n ...(customStyles.windowBorderRadius && { borderRadius: customStyles.windowBorderRadius }),\n ...(customStyles.fontFamily && { fontFamily: customStyles.fontFamily }),\n ...(customStyles.fontSize && { fontSize: customStyles.fontSize }),\n ...(customStyles.sidebarWidth && isSidebar && !isFolded && { width: customStyles.sidebarWidth }),\n ...(customStyles.sidebarFoldedWidth && isSidebar && isFolded && { width: customStyles.sidebarFoldedWidth }),\n ...(isPanel && { width: panelWidth }),\n };\n\n // For embedded mode, return the chat window directly without fragment\n // This ensures proper flex layout inheritance from parent\n if (isEmbedded && isOpen) {\n return (\n <div\n ref={resizeRef}\n role=\"region\"\n aria-label=\"AI Assistant Chat\"\n className={`inapp-ai-window inapp-ai-embedded ${themeClass}`}\n style={windowStyle}\n >\n {/* Header (can be hidden via showHeader prop) */}\n {showHeader && (\n <div className=\"inapp-ai-header\" style={{\n ...(customStyles.headerBackground && { background: customStyles.headerBackground }),\n ...(customStyles.headerTextColor && { color: customStyles.headerTextColor }),\n }}>\n <div className=\"inapp-ai-header-title\">\n <span className=\"inapp-ai-header-icon\">{customStyles.buttonIcon || '🤖'}</span>\n <div>\n <h3>{customStyles.headerTitle || 'AI Assistant'}</h3>\n <p>\n {isConnected ? (\n <span className=\"inapp-ai-status-connected\">\n <span className=\"inapp-ai-status-dot\" />\n Connected\n </span>\n ) : (\n <span className=\"inapp-ai-status-disconnected\">\n <span className=\"inapp-ai-status-dot\" />\n Disconnected\n </span>\n )}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Error Banner */}\n {error && (\n <ErrorMessage error={error} onDismiss={() => setError(null)} />\n )}\n\n {/* Messages */}\n <div\n className=\"inapp-ai-messages\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Chat messages\"\n >\n {messages.length === 0 ? (\n <div className=\"inapp-ai-empty-state\" role=\"status\">\n <div className=\"inapp-ai-empty-icon\" aria-hidden=\"true\">💬</div>\n <h4>Start a conversation</h4>\n <p>How can I help you today?</p>\n </div>\n ) : (\n messages.map((msg) => (\n <div\n key={msg.id}\n className={`inapp-ai-message inapp-ai-message-${msg.role}`}\n role=\"article\"\n aria-label={`${msg.role === 'user' ? 'User' : 'Assistant'} message`}\n >\n <div className=\"inapp-ai-message-icon\" aria-hidden=\"true\">\n {msg.role === 'user' ? '👤' : '🤖'}\n </div>\n <div className=\"inapp-ai-message-content\">\n <div className=\"inapp-ai-message-text\" style={{\n ...(msg.role === 'user' && customStyles.userMessageBackground && { background: customStyles.userMessageBackground }),\n ...(msg.role === 'user' && customStyles.userMessageColor && { color: customStyles.userMessageColor }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageBackground && { background: customStyles.assistantMessageBackground }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageColor && { color: customStyles.assistantMessageColor }),\n ...(customStyles.borderRadius && { borderRadius: customStyles.borderRadius }),\n }}>\n {msg.role === 'assistant' ? (\n <ReactMarkdown\n components={{\n code: CodeBlock as any,\n }}\n >\n {msg.content}\n </ReactMarkdown>\n ) : (\n msg.content\n )}\n </div>\n <div className=\"inapp-ai-message-time\">\n {msg.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : ''}\n {msg.usage && (\n <span className=\"inapp-ai-message-tokens\">\n {' • '}{msg.usage.totalTokens} tokens\n </span>\n )}\n </div>\n </div>\n </div>\n ))\n )}\n {isLoading && (\n <div className=\"inapp-ai-message inapp-ai-message-assistant\">\n <div className=\"inapp-ai-message-icon\">\n <div style={{ animation: 'pulse 2s infinite' }}>🤖</div>\n </div>\n <div className=\"inapp-ai-message-content\">\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div className=\"inapp-ai-typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n <LoadingSkeleton />\n </div>\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input Area */}\n <div className=\"inapp-ai-input-area\" role=\"form\" aria-label=\"Message input\">\n {messages.length > 0 && (\n <button\n className=\"inapp-ai-clear-btn\"\n onClick={clearMessages}\n disabled={isLoading}\n aria-label=\"Clear conversation history\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">🗑️</span> Clear\n </button>\n )}\n <div className=\"inapp-ai-input-wrapper\">\n <input\n ref={inputRef}\n type=\"text\"\n className=\"inapp-ai-input\"\n placeholder={customStyles.inputPlaceholder || \"Type your message...\"}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n disabled={isLoading || !isConnected}\n aria-label=\"Message input\"\n aria-describedby=\"send-hint\"\n tabIndex={0}\n style={{\n ...(customStyles.inputBackground && { background: customStyles.inputBackground }),\n ...(customStyles.inputBorderColor && { borderColor: customStyles.inputBorderColor }),\n ...(customStyles.inputTextColor && { color: customStyles.inputTextColor }),\n }}\n />\n <button\n className=\"inapp-ai-send-btn\"\n onClick={sendMessage}\n disabled={isLoading || !isConnected || !inputValue.trim()}\n aria-label=\"Send message\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">{isLoading ? '⏳' : '⬆'}</span>\n <span className=\"sr-only\">\n {isLoading ? 'Sending...' : 'Send message'}\n </span>\n </button>\n <span id=\"send-hint\" className=\"sr-only\">\n Press Enter to send\n </span>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <>\n {/* Chat Button (only for popup mode, not embedded) */}\n {!isSidebar && !isPanel && !isEmbedded && (\n <button\n className={`inapp-ai-button ${positionClass} ${themeClass}`}\n style={buttonStyle}\n onClick={() => setIsOpen(!isOpen)}\n aria-label={isOpen ? \"Close AI Assistant\" : \"Open AI Assistant\"}\n aria-expanded={isOpen}\n aria-haspopup=\"dialog\"\n tabIndex={0}\n >\n {isOpen ? '✕' : (customStyles.buttonIcon || '🤖')}\n {!isConnected && (\n <span\n className=\"inapp-ai-offline-indicator\"\n role=\"status\"\n aria-label=\"Backend disconnected\"\n />\n )}\n </button>\n )}\n\n {/* Chat Window */}\n {isOpen && (\n <div\n ref={resizeRef}\n role={isEmbedded ? \"region\" : \"dialog\"}\n aria-label=\"AI Assistant Chat\"\n aria-modal={!isSidebar && !isPanel && !isEmbedded ? \"true\" : undefined}\n className={`inapp-ai-window ${modeClass} ${isSidebar || isPanel || isEmbedded ? '' : positionClass} ${themeClass} ${foldedClass}`}\n style={windowStyle}\n >\n {/* Resize Handle for Panel */}\n {isPanel && (\n <div\n className={`inapp-ai-resize-handle ${isLeftPanel ? 'inapp-ai-resize-handle-right' : 'inapp-ai-resize-handle-left'}`}\n onMouseDown={() => setIsResizing(true)}\n title=\"Drag to resize\"\n />\n )}\n\n {/* Folded State Content */}\n {(isSidebar || isPanel) && isFolded ? (\n <div\n className=\"inapp-ai-folded-content\"\n onClick={toggleFolded}\n style={{ cursor: 'pointer' }}\n title=\"Click to unfold\"\n >\n <div className=\"inapp-ai-folded-icon\">\n {customStyles.buttonIcon || '🤖'}\n </div>\n <div className=\"inapp-ai-folded-text\">\n AI\n </div>\n {messages.length > 0 && (\n <div className=\"inapp-ai-message-count\">\n {messages.length}\n </div>\n )}\n </div>\n ) : (\n <>\n {/* Header (can be hidden via showHeader prop) */}\n {showHeader && (\n <div className=\"inapp-ai-header\" style={{\n ...(customStyles.headerBackground && { background: customStyles.headerBackground }),\n ...(customStyles.headerTextColor && { color: customStyles.headerTextColor }),\n }}>\n <div className=\"inapp-ai-header-title\">\n <span className=\"inapp-ai-header-icon\">{customStyles.buttonIcon || '🤖'}</span>\n <div>\n <h3>{customStyles.headerTitle || 'AI Assistant'}</h3>\n <p>\n {isConnected ? (\n <span className=\"inapp-ai-status-connected\">\n <span className=\"inapp-ai-status-dot\" />\n Connected\n </span>\n ) : (\n <span className=\"inapp-ai-status-disconnected\">\n <span className=\"inapp-ai-status-dot\" />\n Disconnected\n </span>\n )}\n </p>\n </div>\n </div>\n {/* Fold button for panels (in header top right) */}\n {(isSidebar || isPanel) && (\n <button\n className=\"inapp-ai-header-fold-btn\"\n onClick={toggleFolded}\n aria-label={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n title={isFolded ? `Unfold ${isSidebar ? 'sidebar' : 'panel'}` : `Fold ${isSidebar ? 'sidebar' : 'panel'}`}\n >\n {(isLeftSidebar || isLeftPanel) ? '◀' : '▶'}\n </button>\n )}\n {/* Close button for popup mode */}\n {!isSidebar && !isPanel && !isEmbedded && (\n <button\n className=\"inapp-ai-close-btn\"\n onClick={() => setIsOpen(false)}\n aria-label=\"Close\"\n >\n ✕\n </button>\n )}\n </div>\n )}\n\n {/* Error Banner */}\n {error && (\n <ErrorMessage error={error} onDismiss={() => setError(null)} />\n )}\n\n {/* Messages */}\n <div\n className=\"inapp-ai-messages\"\n role=\"log\"\n aria-live=\"polite\"\n aria-label=\"Chat messages\"\n >\n {messages.length === 0 ? (\n <div className=\"inapp-ai-empty-state\" role=\"status\">\n <div className=\"inapp-ai-empty-icon\" aria-hidden=\"true\">💬</div>\n <h4>Start a conversation</h4>\n <p>How can I help you today?</p>\n </div>\n ) : (\n messages.map((msg) => (\n <div\n key={msg.id}\n className={`inapp-ai-message inapp-ai-message-${msg.role}`}\n role=\"article\"\n aria-label={`${msg.role === 'user' ? 'User' : 'Assistant'} message`}\n >\n <div className=\"inapp-ai-message-icon\" aria-hidden=\"true\">\n {msg.role === 'user' ? '👤' : '🤖'}\n </div>\n <div className=\"inapp-ai-message-content\">\n <div className=\"inapp-ai-message-text\" style={{\n ...(msg.role === 'user' && customStyles.userMessageBackground && { background: customStyles.userMessageBackground }),\n ...(msg.role === 'user' && customStyles.userMessageColor && { color: customStyles.userMessageColor }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageBackground && { background: customStyles.assistantMessageBackground }),\n ...(msg.role === 'assistant' && customStyles.assistantMessageColor && { color: customStyles.assistantMessageColor }),\n ...(customStyles.borderRadius && { borderRadius: customStyles.borderRadius }),\n }}>\n {msg.role === 'assistant' ? (\n <ReactMarkdown\n components={{\n code: CodeBlock as any,\n }}\n >\n {msg.content}\n </ReactMarkdown>\n ) : (\n msg.content\n )}\n </div>\n <div className=\"inapp-ai-message-time\">\n {msg.timestamp?.toLocaleTimeString() || ''}\n {msg.usage && (\n <span className=\"inapp-ai-message-tokens\">\n {' • '}{msg.usage.totalTokens} tokens\n </span>\n )}\n </div>\n </div>\n </div>\n ))\n )}\n {isLoading && (\n <div className=\"inapp-ai-message inapp-ai-message-assistant\">\n <div className=\"inapp-ai-message-icon\">\n <div style={{ animation: 'pulse 2s infinite' }}>🤖</div>\n </div>\n <div className=\"inapp-ai-message-content\">\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n <div className=\"inapp-ai-typing-indicator\">\n <span></span>\n <span></span>\n <span></span>\n </div>\n <LoadingSkeleton />\n </div>\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input Area */}\n <div className=\"inapp-ai-input-area\" role=\"form\" aria-label=\"Message input\">\n {messages.length > 0 && (\n <button\n className=\"inapp-ai-clear-btn\"\n onClick={clearMessages}\n disabled={isLoading}\n aria-label=\"Clear conversation history\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">🗑️</span> Clear\n </button>\n )}\n <div className=\"inapp-ai-input-wrapper\">\n <input\n ref={inputRef}\n type=\"text\"\n className=\"inapp-ai-input\"\n placeholder={customStyles.inputPlaceholder || \"Type your message...\"}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyPress={handleKeyPress}\n disabled={isLoading || !isConnected}\n aria-label=\"Message input\"\n aria-describedby=\"send-hint\"\n tabIndex={0}\n style={{\n ...(customStyles.inputBackground && { background: customStyles.inputBackground }),\n ...(customStyles.inputBorderColor && { borderColor: customStyles.inputBorderColor }),\n ...(customStyles.inputTextColor && { color: customStyles.inputTextColor }),\n }}\n />\n <button\n className=\"inapp-ai-send-btn\"\n onClick={sendMessage}\n disabled={isLoading || !isConnected || !inputValue.trim()}\n aria-label=\"Send message\"\n tabIndex={0}\n >\n <span aria-hidden=\"true\">{isLoading ? '⏳' : '⬆'}</span>\n <span className=\"sr-only\">\n {isLoading ? 'Sending...' : 'Send message'}\n </span>\n </button>\n <span id=\"send-hint\" className=\"sr-only\">\n Press Enter to send\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n )}\n </>\n );\n}\n","import { useState, useCallback, useEffect, useRef } from 'react';\nimport type { Tool } from '../types';\n\n/**\n * Options for the useTools hook\n */\nexport interface UseToolsOptions {\n /**\n * Initial tools to register\n */\n initialTools?: Tool[];\n\n /**\n * Whether to automatically cleanup tools on unmount\n * @default true\n */\n autoCleanup?: boolean;\n}\n\n/**\n * Return value from the useTools hook\n */\nexport interface UseToolsReturn {\n /**\n * Current array of registered tools\n */\n tools: Tool[];\n\n /**\n * Register a new tool\n * @param tool - Tool to register\n * @throws {Error} If tool with same name already exists\n */\n registerTool: (tool: Tool) => void;\n\n /**\n * Unregister a tool by name\n * @param name - Name of the tool to unregister\n */\n unregisterTool: (name: string) => void;\n\n /**\n * Clear all registered tools\n */\n clearTools: () => void;\n\n /**\n * Check if a tool with the given name exists\n * @param name - Tool name to check\n * @returns true if tool exists, false otherwise\n */\n hasTool: (name: string) => boolean;\n}\n\n/**\n * Hook for managing tools dynamically in React components\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { tools, registerTool, unregisterTool } = useTools();\n * const [todos, setTodos] = useState([]);\n *\n * useEffect(() => {\n * registerTool({\n * name: 'addTodo',\n * description: 'Add a new todo',\n * parameters: {\n * type: 'object',\n * properties: {\n * task: { type: 'string' }\n * }\n * },\n * handler: async ({ task }) => {\n * setTodos(prev => [...prev, { id: Date.now(), text: task }]);\n * return { success: true };\n * }\n * });\n *\n * return () => unregisterTool('addTodo');\n * }, [registerTool, unregisterTool, setTodos]);\n *\n * return <InAppAI endpoint=\"...\" tools={tools} />;\n * }\n * ```\n *\n * @param options - Configuration options\n * @returns Tool management functions and current tools array\n */\nexport function useTools(options: UseToolsOptions = {}): UseToolsReturn {\n const { initialTools = [], autoCleanup = true } = options;\n\n // Use ref to track tool names for validation\n const toolNamesRef = useRef<Set<string>>(new Set());\n\n // State for tools array\n const [tools, setTools] = useState<Tool[]>(() => {\n // Initialize with initial tools\n initialTools.forEach(tool => {\n if (toolNamesRef.current.has(tool.name)) {\n console.warn(\n `[useTools] Duplicate tool name in initialTools: \"${tool.name}\". ` +\n `Only the first occurrence will be used.`\n );\n } else {\n toolNamesRef.current.add(tool.name);\n }\n });\n\n return initialTools.filter((tool, index) => {\n // Filter out duplicates\n return initialTools.findIndex(t => t.name === tool.name) === index;\n });\n });\n\n /**\n * Register a new tool\n */\n const registerTool = useCallback((tool: Tool) => {\n if (!tool.name) {\n throw new Error('[useTools] Tool must have a name');\n }\n\n if (toolNamesRef.current.has(tool.name)) {\n console.warn(\n `[useTools] Tool \"${tool.name}\" is already registered. ` +\n `Use unregisterTool() first if you want to replace it.`\n );\n return;\n }\n\n // Validate tool structure\n if (!tool.description) {\n console.warn(`[useTools] Tool \"${tool.name}\" is missing description`);\n }\n\n if (!tool.parameters) {\n console.warn(`[useTools] Tool \"${tool.name}\" is missing parameters schema`);\n }\n\n if (typeof tool.handler !== 'function') {\n throw new Error(`[useTools] Tool \"${tool.name}\" must have a handler function`);\n }\n\n toolNamesRef.current.add(tool.name);\n setTools(prev => [...prev, tool]);\n }, []);\n\n /**\n * Unregister a tool by name\n */\n const unregisterTool = useCallback((name: string) => {\n if (!toolNamesRef.current.has(name)) {\n console.warn(`[useTools] Tool \"${name}\" is not registered`);\n return;\n }\n\n toolNamesRef.current.delete(name);\n setTools(prev => prev.filter(tool => tool.name !== name));\n }, []);\n\n /**\n * Clear all tools\n */\n const clearTools = useCallback(() => {\n toolNamesRef.current.clear();\n setTools([]);\n }, []);\n\n /**\n * Check if a tool exists\n */\n const hasTool = useCallback((name: string): boolean => {\n return toolNamesRef.current.has(name);\n }, []);\n\n /**\n * Cleanup on unmount if autoCleanup is enabled\n */\n useEffect(() => {\n if (autoCleanup) {\n return () => {\n toolNamesRef.current.clear();\n };\n }\n }, [autoCleanup]);\n\n return {\n tools,\n registerTool,\n unregisterTool,\n clearTools,\n hasTool,\n };\n}\n","import React, { createContext, useContext, useState, useCallback, useMemo } from 'react';\nimport type { Tool } from '../types';\n\n/**\n * Tool registry interface\n */\nexport interface ToolRegistry {\n /**\n * Register tools under a namespace\n * @param namespace - Unique namespace identifier\n * @param tools - Array of tools to register\n */\n register(namespace: string, tools: Tool[]): void;\n\n /**\n * Unregister all tools for a namespace\n * @param namespace - Namespace to unregister\n */\n unregister(namespace: string): void;\n\n /**\n * Get tools for a specific namespace\n * @param namespace - Namespace to query\n * @returns Array of tools for the namespace, or empty array if not found\n */\n getTools(namespace: string): Tool[];\n\n /**\n * Get all registered tools from all namespaces\n * @returns Combined array of all tools\n */\n getAllTools(): Tool[];\n\n /**\n * Clear all registered tools from all namespaces\n */\n clear(): void;\n\n /**\n * Get all registered namespace names\n * @returns Array of namespace names\n */\n getNamespaces(): string[];\n}\n\n/**\n * Context for the tool registry\n */\nconst ToolRegistryContext = createContext<ToolRegistry | null>(null);\n\n/**\n * Props for ToolRegistryProvider\n */\nexport interface ToolRegistryProviderProps {\n /**\n * Child components\n */\n children: React.ReactNode;\n\n /**\n * Initial tools organized by namespace\n */\n initialTools?: Record<string, Tool[]>;\n}\n\n/**\n * Provider component for global tool registry\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <ToolRegistryProvider>\n * <MyApp />\n * </ToolRegistryProvider>\n * );\n * }\n * ```\n */\nexport function ToolRegistryProvider({\n children,\n initialTools = {},\n}: ToolRegistryProviderProps): React.ReactElement {\n // State: Map of namespace -> tools\n const [toolsMap, setToolsMap] = useState<Map<string, Tool[]>>(\n () => new Map(Object.entries(initialTools))\n );\n\n /**\n * Register tools under a namespace\n */\n const register = useCallback((namespace: string, tools: Tool[]) => {\n // Validate namespace\n if (!namespace || typeof namespace !== 'string') {\n throw new Error('[ToolRegistry] Namespace must be a non-empty string');\n }\n\n if (!/^[a-zA-Z0-9_-]+$/.test(namespace)) {\n throw new Error(\n `[ToolRegistry] Invalid namespace \"${namespace}\". ` +\n `Use only alphanumeric characters, hyphens, and underscores.`\n );\n }\n\n // Validate tools\n if (!Array.isArray(tools)) {\n throw new Error('[ToolRegistry] Tools must be an array');\n }\n\n // Check for duplicate tool names within the namespace\n const toolNames = new Set<string>();\n tools.forEach(tool => {\n if (toolNames.has(tool.name)) {\n console.warn(\n `[ToolRegistry] Duplicate tool name in namespace \"${namespace}\": \"${tool.name}\"`\n );\n }\n toolNames.add(tool.name);\n });\n\n setToolsMap(prev => {\n const next = new Map(prev);\n next.set(namespace, tools);\n return next;\n });\n }, []);\n\n /**\n * Unregister a namespace\n */\n const unregister = useCallback((namespace: string) => {\n setToolsMap(prev => {\n if (!prev.has(namespace)) {\n console.warn(`[ToolRegistry] Namespace \"${namespace}\" is not registered`);\n return prev;\n }\n\n const next = new Map(prev);\n next.delete(namespace);\n return next;\n });\n }, []);\n\n /**\n * Get tools for a specific namespace\n */\n const getTools = useCallback(\n (namespace: string): Tool[] => {\n return toolsMap.get(namespace) || [];\n },\n [toolsMap]\n );\n\n /**\n * Get all tools from all namespaces\n */\n const getAllTools = useCallback((): Tool[] => {\n const allTools: Tool[] = [];\n const seenNames = new Set<string>();\n\n // Iterate through namespaces\n for (const [namespace, tools] of toolsMap.entries()) {\n for (const tool of tools) {\n // Warn about name conflicts across namespaces\n if (seenNames.has(tool.name)) {\n console.warn(\n `[ToolRegistry] Tool name conflict: \"${tool.name}\" exists in multiple namespaces. ` +\n `Only the first occurrence will be used.`\n );\n continue;\n }\n\n seenNames.add(tool.name);\n allTools.push(tool);\n }\n }\n\n return allTools;\n }, [toolsMap]);\n\n /**\n * Clear all tools\n */\n const clear = useCallback(() => {\n setToolsMap(new Map());\n }, []);\n\n /**\n * Get all namespace names\n */\n const getNamespaces = useCallback((): string[] => {\n return Array.from(toolsMap.keys());\n }, [toolsMap]);\n\n // Memoize the registry value\n const registry = useMemo<ToolRegistry>(\n () => ({\n register,\n unregister,\n getTools,\n getAllTools,\n clear,\n getNamespaces,\n }),\n [register, unregister, getTools, getAllTools, clear, getNamespaces]\n );\n\n return (\n <ToolRegistryContext.Provider value={registry}>\n {children}\n </ToolRegistryContext.Provider>\n );\n}\n\n/**\n * Hook to access the tool registry\n *\n * @example\n * ```tsx\n * function TodoPage() {\n * const registry = useToolRegistry();\n * const [todos, setTodos] = useState([]);\n *\n * useEffect(() => {\n * registry.register('todos', [\n * {\n * name: 'addTodo',\n * description: 'Add a todo',\n * parameters: { type: 'object', properties: { task: { type: 'string' } } },\n * handler: async ({ task }) => {\n * setTodos(prev => [...prev, { id: Date.now(), text: task }]);\n * return { success: true };\n * }\n * }\n * ]);\n *\n * return () => registry.unregister('todos');\n * }, [registry, todos, setTodos]);\n *\n * return <TodoList />;\n * }\n *\n * function ChatWidget() {\n * const registry = useToolRegistry();\n * const allTools = registry.getAllTools();\n *\n * return <InAppAI endpoint=\"...\" tools={allTools} />;\n * }\n * ```\n *\n * @throws {Error} If used outside of ToolRegistryProvider\n * @returns Tool registry instance\n */\nexport function useToolRegistry(): ToolRegistry {\n const registry = useContext(ToolRegistryContext);\n\n if (!registry) {\n throw new Error(\n '[useToolRegistry] must be used within a ToolRegistryProvider. ' +\n 'Wrap your component tree with <ToolRegistryProvider>.'\n );\n }\n\n return registry;\n}\n"],"mappings":"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","names":["useState","useEffect","useRef","ReactMarkdown","SyntaxHighlighter","vscDarkPlus","Fragment","jsx","jsxs","LoadingSkeleton","ErrorMessage","error","onDismiss","errorInfo","msg","CodeBlock","inline","className","children","props","copied","setCopied","useState","match","language","codeString","handleCopy","SyntaxHighlighter","vscDarkPlus","InAppAI","endpoint","agentId","position","displayMode","defaultFolded","theme","context","customStyles","tools","panelMinWidth","panelMaxWidth","panelDefaultWidth","onPanelResize","externalConversationId","externalMessages","onMessagesChange","showHeader","authToken","maxToolRounds","apiEndpoint","getAuthToken","buildHeaders","headers","token","externalMessagesRef","useRef","useEffect","messages","setMessages","updater","currentMessages","newMessages","isOpen","setIsOpen","isFolded","setIsFolded","inputValue","setInputValue","isLoading","setIsLoading","isConnected","setIsConnected","setError","panelWidth","setPanelWidth","isResizing","setIsResizing","messagesEndRef","internalConversationId","conversationId","resizeRef","inputRef","isEmbedded","isSidebar","isPanel","isLeftSidebar","isLeftPanel","handleMouseMove","e","container","containerRect","newWidth","widthPercent","minPercent","maxPercent","newWidthStr","handleMouseUp","sendMessage","message","userMessage","prev","getContext","toolDefinitions","name","description","parameters","buildMessagesPayload","m","actionsSummary","a","response","currentData","round","allToolActions","results","toolCall","toolName","toolArgs","tool","t","result","idx","toolResultLines","r","toolResultsMessage","isLastAllowedRound","followUpResponse","assistantMessage","err","handleKeyPress","clearMessages","toggleFolded","positionClass","themeClass","modeClass","foldedClass","buttonStyle","windowStyle","ReactMarkdown","useState","useCallback","useEffect","useRef","useTools","options","initialTools","autoCleanup","toolNamesRef","tools","setTools","tool","index","t","registerTool","prev","unregisterTool","name","clearTools","hasTool","createContext","useContext","useState","useCallback","useMemo","jsx","ToolRegistryContext","ToolRegistryProvider","children","initialTools","toolsMap","setToolsMap","register","namespace","tools","toolNames","tool","prev","next","unregister","getTools","getAllTools","allTools","seenNames","clear","getNamespaces","registry","useToolRegistry"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inappai/react",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Beautiful, customizable AI chat component for React applications",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",