vanilla-agent 0.2.0 → 1.1.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.
@@ -1,2 +1,2 @@
1
- "use strict";var SiteAgentInstaller=(()=>{(function(){"use strict";if(window.__siteAgentInstallerLoaded)return;window.__siteAgentInstallerLoaded=!0;let n=window.siteAgentConfig||{},o=n.version||"latest",l=n.cdn||"jsdelivr",d=n.autoInit!==!1,c=()=>{if(n.cssUrl&&n.jsUrl)return{cssUrl:n.cssUrl,jsUrl:n.jsUrl};let t=`/npm/vanilla-agent@${o}/dist`;return l==="unpkg"?{cssUrl:`https://unpkg.com${t}/widget.css`,jsUrl:`https://unpkg.com${t}/index.global.js`}:{cssUrl:`https://cdn.jsdelivr.net${t}/widget.css`,jsUrl:`https://cdn.jsdelivr.net${t}/index.global.js`}},{cssUrl:a,jsUrl:r}=c(),g=()=>!!document.head.querySelector("link[data-vanilla-agent]")||!!document.head.querySelector('link[href*="widget.css"]'),u=()=>!!window.ChatWidget,f=()=>new Promise((i,t)=>{if(g()){i();return}let e=document.createElement("link");e.rel="stylesheet",e.href=a,e.setAttribute("data-vanilla-agent","true"),e.onload=()=>i(),e.onerror=()=>t(new Error(`Failed to load CSS from ${a}`)),document.head.appendChild(e)}),h=()=>new Promise((i,t)=>{if(u()){i();return}let e=document.createElement("script");e.src=r,e.async=!0,e.onload=()=>i(),e.onerror=()=>t(new Error(`Failed to load JS from ${r}`)),document.head.appendChild(e)}),w=()=>{if(!window.ChatWidget||!window.ChatWidget.initChatWidget){console.warn("ChatWidget not available. Make sure the script loaded successfully.");return}let i=n.target||"body",t={...n.config};if(n.apiUrl&&!t.apiUrl&&(t.apiUrl=n.apiUrl),!(!t.apiUrl&&Object.keys(t).length===0))try{window.ChatWidget.initChatWidget({target:i,config:t})}catch(e){console.error("Failed to initialize ChatWidget:",e)}},s=async()=>{try{await f(),await h(),d&&(n.config||n.apiUrl)&&setTimeout(w,0)}catch(i){console.error("Failed to install ChatWidget:",i)}};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s):s()})();})();
1
+ "use strict";var SiteAgentInstaller=(()=>{(function(){"use strict";if(window.__siteAgentInstallerLoaded)return;window.__siteAgentInstallerLoaded=!0;let n=window.siteAgentConfig||{},o=n.version||"latest",l=n.cdn||"jsdelivr",d=n.autoInit!==!1,c=()=>{if(n.cssUrl&&n.jsUrl)return{cssUrl:n.cssUrl,jsUrl:n.jsUrl};let e=`/npm/vanilla-agent@${o}/dist`;return l==="unpkg"?{cssUrl:`https://unpkg.com${e}/widget.css`,jsUrl:`https://unpkg.com${e}/index.global.js`}:{cssUrl:`https://cdn.jsdelivr.net${e}/widget.css`,jsUrl:`https://cdn.jsdelivr.net${e}/index.global.js`}},{cssUrl:r,jsUrl:s}=c(),g=()=>!!document.head.querySelector("link[data-vanilla-agent]")||!!document.head.querySelector('link[href*="widget.css"]'),u=()=>!!window.AgentWidget,f=()=>new Promise((i,e)=>{if(g()){i();return}let t=document.createElement("link");t.rel="stylesheet",t.href=r,t.setAttribute("data-vanilla-agent","true"),t.onload=()=>i(),t.onerror=()=>e(new Error(`Failed to load CSS from ${r}`)),document.head.appendChild(t)}),w=()=>new Promise((i,e)=>{if(u()){i();return}let t=document.createElement("script");t.src=s,t.async=!0,t.onload=()=>i(),t.onerror=()=>e(new Error(`Failed to load JS from ${s}`)),document.head.appendChild(t)}),p=()=>{if(!window.AgentWidget||!window.AgentWidget.initAgentWidget){console.warn("AgentWidget not available. Make sure the script loaded successfully.");return}let i=n.target||"body",e={...n.config};if(n.apiUrl&&!e.apiUrl&&(e.apiUrl=n.apiUrl),!(!e.apiUrl&&Object.keys(e).length===0))try{window.AgentWidget.initAgentWidget({target:i,config:e})}catch(t){console.error("Failed to initialize AgentWidget:",t)}},a=async()=>{try{await f(),await w(),d&&(n.config||n.apiUrl)&&setTimeout(p,0)}catch(i){console.error("Failed to install AgentWidget:",i)}};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",a):a()})();})();
2
2
  //# sourceMappingURL=install.global.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/install.ts"],"sourcesContent":["/**\n * Standalone installer script for easy script tag installation\n * This script automatically loads CSS and JS, then initializes the widget\n * if configuration is provided via window.siteAgentConfig\n */\n\ninterface SiteAgentInstallConfig {\n version?: string;\n cdn?: \"unpkg\" | \"jsdelivr\";\n cssUrl?: string;\n jsUrl?: string;\n target?: string | HTMLElement;\n config?: any;\n autoInit?: boolean;\n}\n\ndeclare global {\n interface Window {\n siteAgentConfig?: SiteAgentInstallConfig;\n ChatWidget?: any;\n }\n}\n\n(function() {\n \"use strict\";\n\n // Prevent double installation\n if ((window as any).__siteAgentInstallerLoaded) {\n return;\n }\n (window as any).__siteAgentInstallerLoaded = true;\n\n const config: SiteAgentInstallConfig = window.siteAgentConfig || {};\n const version = config.version || \"latest\";\n const cdn = config.cdn || \"jsdelivr\";\n const autoInit = config.autoInit !== false; // Default to true\n\n // Determine CDN base URL\n const getCdnBase = () => {\n if (config.cssUrl && config.jsUrl) {\n return { cssUrl: config.cssUrl, jsUrl: config.jsUrl };\n }\n \n const packageName = \"vanilla-agent\";\n const basePath = `/npm/${packageName}@${version}/dist`;\n \n if (cdn === \"unpkg\") {\n return {\n cssUrl: `https://unpkg.com${basePath}/widget.css`,\n jsUrl: `https://unpkg.com${basePath}/index.global.js`\n };\n } else {\n return {\n cssUrl: `https://cdn.jsdelivr.net${basePath}/widget.css`,\n jsUrl: `https://cdn.jsdelivr.net${basePath}/index.global.js`\n };\n }\n };\n\n const { cssUrl, jsUrl } = getCdnBase();\n\n // Check if CSS is already loaded\n const isCssLoaded = () => {\n return !!document.head.querySelector('link[data-vanilla-agent]') ||\n !!document.head.querySelector(`link[href*=\"widget.css\"]`);\n };\n\n // Check if JS is already loaded\n const isJsLoaded = () => {\n return !!(window as any).ChatWidget;\n };\n\n // Load CSS\n const loadCSS = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (isCssLoaded()) {\n resolve();\n return;\n }\n\n const link = document.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.href = cssUrl;\n link.setAttribute(\"data-vanilla-agent\", \"true\");\n link.onload = () => resolve();\n link.onerror = () => reject(new Error(`Failed to load CSS from ${cssUrl}`));\n document.head.appendChild(link);\n });\n };\n\n // Load JS\n const loadJS = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (isJsLoaded()) {\n resolve();\n return;\n }\n\n const script = document.createElement(\"script\");\n script.src = jsUrl;\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`Failed to load JS from ${jsUrl}`));\n document.head.appendChild(script);\n });\n };\n\n // Initialize widget\n const initWidget = () => {\n if (!window.ChatWidget || !window.ChatWidget.initChatWidget) {\n console.warn(\"ChatWidget not available. Make sure the script loaded successfully.\");\n return;\n }\n\n const target = config.target || \"body\";\n // Merge apiUrl from top-level config into widget config if present\n const widgetConfig = { ...config.config };\n if ((config as any).apiUrl && !widgetConfig.apiUrl) {\n widgetConfig.apiUrl = (config as any).apiUrl;\n }\n\n // Only initialize if config is provided\n if (!widgetConfig.apiUrl && Object.keys(widgetConfig).length === 0) {\n return;\n }\n\n try {\n window.ChatWidget.initChatWidget({\n target,\n config: widgetConfig\n });\n } catch (error) {\n console.error(\"Failed to initialize ChatWidget:\", error);\n }\n };\n\n // Main installation flow\n const install = async () => {\n try {\n await loadCSS();\n await loadJS();\n \n if (autoInit && (config.config || (config as any).apiUrl)) {\n // Wait a tick to ensure ChatWidget is fully initialized\n setTimeout(initWidget, 0);\n }\n } catch (error) {\n console.error(\"Failed to install ChatWidget:\", error);\n }\n };\n\n // Start installation\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", install);\n } else {\n install();\n }\n})();\n\n"],"mappings":"2CAuBC,UAAW,CACV,aAGA,GAAK,OAAe,2BAClB,OAED,OAAe,2BAA6B,GAE7C,IAAMA,EAAiC,OAAO,iBAAmB,CAAC,EAC5DC,EAAUD,EAAO,SAAW,SAC5BE,EAAMF,EAAO,KAAO,WACpBG,EAAWH,EAAO,WAAa,GAG/BI,EAAa,IAAM,CACvB,GAAIJ,EAAO,QAAUA,EAAO,MAC1B,MAAO,CAAE,OAAQA,EAAO,OAAQ,MAAOA,EAAO,KAAM,EAItD,IAAMK,EAAW,sBAAuBJ,CAAO,QAE/C,OAAIC,IAAQ,QACH,CACL,OAAQ,oBAAoBG,CAAQ,cACpC,MAAO,oBAAoBA,CAAQ,kBACrC,EAEO,CACL,OAAQ,2BAA2BA,CAAQ,cAC3C,MAAO,2BAA2BA,CAAQ,kBAC5C,CAEJ,EAEM,CAAE,OAAAC,EAAQ,MAAAC,CAAM,EAAIH,EAAW,EAG/BI,EAAc,IACX,CAAC,CAAC,SAAS,KAAK,cAAc,0BAA0B,GACxD,CAAC,CAAC,SAAS,KAAK,cAAc,0BAA0B,EAI3DC,EAAa,IACV,CAAC,CAAE,OAAe,WAIrBC,EAAU,IACP,IAAI,QAAQ,CAACC,EAASC,IAAW,CACtC,GAAIJ,EAAY,EAAG,CACjBG,EAAQ,EACR,MACF,CAEA,IAAME,EAAO,SAAS,cAAc,MAAM,EAC1CA,EAAK,IAAM,aACXA,EAAK,KAAOP,EACZO,EAAK,aAAa,qBAAsB,MAAM,EAC9CA,EAAK,OAAS,IAAMF,EAAQ,EAC5BE,EAAK,QAAU,IAAMD,EAAO,IAAI,MAAM,2BAA2BN,CAAM,EAAE,CAAC,EAC1E,SAAS,KAAK,YAAYO,CAAI,CAChC,CAAC,EAIGC,EAAS,IACN,IAAI,QAAQ,CAACH,EAASC,IAAW,CACtC,GAAIH,EAAW,EAAG,CAChBE,EAAQ,EACR,MACF,CAEA,IAAMI,EAAS,SAAS,cAAc,QAAQ,EAC9CA,EAAO,IAAMR,EACbQ,EAAO,MAAQ,GACfA,EAAO,OAAS,IAAMJ,EAAQ,EAC9BI,EAAO,QAAU,IAAMH,EAAO,IAAI,MAAM,0BAA0BL,CAAK,EAAE,CAAC,EAC1E,SAAS,KAAK,YAAYQ,CAAM,CAClC,CAAC,EAIGC,EAAa,IAAM,CACvB,GAAI,CAAC,OAAO,YAAc,CAAC,OAAO,WAAW,eAAgB,CAC3D,QAAQ,KAAK,qEAAqE,EAClF,MACF,CAEA,IAAMC,EAASjB,EAAO,QAAU,OAE1BkB,EAAe,CAAE,GAAGlB,EAAO,MAAO,EAMxC,GALKA,EAAe,QAAU,CAACkB,EAAa,SAC1CA,EAAa,OAAUlB,EAAe,QAIpC,GAACkB,EAAa,QAAU,OAAO,KAAKA,CAAY,EAAE,SAAW,GAIjE,GAAI,CACF,OAAO,WAAW,eAAe,CAC/B,OAAAD,EACA,OAAQC,CACV,CAAC,CACH,OAASC,EAAO,CACd,QAAQ,MAAM,mCAAoCA,CAAK,CACzD,CACF,EAGMC,EAAU,SAAY,CAC1B,GAAI,CACF,MAAMV,EAAQ,EACd,MAAMI,EAAO,EAETX,IAAaH,EAAO,QAAWA,EAAe,SAEhD,WAAWgB,EAAY,CAAC,CAE5B,OAASG,EAAO,CACd,QAAQ,MAAM,gCAAiCA,CAAK,CACtD,CACF,EAGI,SAAS,aAAe,UAC1B,SAAS,iBAAiB,mBAAoBC,CAAO,EAErDA,EAAQ,CAEZ,GAAG","names":["config","version","cdn","autoInit","getCdnBase","basePath","cssUrl","jsUrl","isCssLoaded","isJsLoaded","loadCSS","resolve","reject","link","loadJS","script","initWidget","target","widgetConfig","error","install"]}
1
+ {"version":3,"sources":["../src/install.ts"],"sourcesContent":["/**\n * Standalone installer script for easy script tag installation\n * This script automatically loads CSS and JS, then initializes the widget\n * if configuration is provided via window.siteAgentConfig\n */\n\ninterface SiteAgentInstallConfig {\n version?: string;\n cdn?: \"unpkg\" | \"jsdelivr\";\n cssUrl?: string;\n jsUrl?: string;\n target?: string | HTMLElement;\n config?: any;\n autoInit?: boolean;\n}\n\ndeclare global {\n interface Window {\n siteAgentConfig?: SiteAgentInstallConfig;\n AgentWidget?: any;\n }\n}\n\n(function() {\n \"use strict\";\n\n // Prevent double installation\n if ((window as any).__siteAgentInstallerLoaded) {\n return;\n }\n (window as any).__siteAgentInstallerLoaded = true;\n\n const config: SiteAgentInstallConfig = window.siteAgentConfig || {};\n const version = config.version || \"latest\";\n const cdn = config.cdn || \"jsdelivr\";\n const autoInit = config.autoInit !== false; // Default to true\n\n // Determine CDN base URL\n const getCdnBase = () => {\n if (config.cssUrl && config.jsUrl) {\n return { cssUrl: config.cssUrl, jsUrl: config.jsUrl };\n }\n \n const packageName = \"vanilla-agent\";\n const basePath = `/npm/${packageName}@${version}/dist`;\n \n if (cdn === \"unpkg\") {\n return {\n cssUrl: `https://unpkg.com${basePath}/widget.css`,\n jsUrl: `https://unpkg.com${basePath}/index.global.js`\n };\n } else {\n return {\n cssUrl: `https://cdn.jsdelivr.net${basePath}/widget.css`,\n jsUrl: `https://cdn.jsdelivr.net${basePath}/index.global.js`\n };\n }\n };\n\n const { cssUrl, jsUrl } = getCdnBase();\n\n // Check if CSS is already loaded\n const isCssLoaded = () => {\n return !!document.head.querySelector('link[data-vanilla-agent]') ||\n !!document.head.querySelector(`link[href*=\"widget.css\"]`);\n };\n\n // Check if JS is already loaded\n const isJsLoaded = () => {\n return !!(window as any).AgentWidget;\n };\n\n // Load CSS\n const loadCSS = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (isCssLoaded()) {\n resolve();\n return;\n }\n\n const link = document.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.href = cssUrl;\n link.setAttribute(\"data-vanilla-agent\", \"true\");\n link.onload = () => resolve();\n link.onerror = () => reject(new Error(`Failed to load CSS from ${cssUrl}`));\n document.head.appendChild(link);\n });\n };\n\n // Load JS\n const loadJS = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (isJsLoaded()) {\n resolve();\n return;\n }\n\n const script = document.createElement(\"script\");\n script.src = jsUrl;\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`Failed to load JS from ${jsUrl}`));\n document.head.appendChild(script);\n });\n };\n\n // Initialize widget\n const initWidget = () => {\n if (!window.AgentWidget || !window.AgentWidget.initAgentWidget) {\n console.warn(\"AgentWidget not available. Make sure the script loaded successfully.\");\n return;\n }\n\n const target = config.target || \"body\";\n // Merge apiUrl from top-level config into widget config if present\n const widgetConfig = { ...config.config };\n if ((config as any).apiUrl && !widgetConfig.apiUrl) {\n widgetConfig.apiUrl = (config as any).apiUrl;\n }\n\n // Only initialize if config is provided\n if (!widgetConfig.apiUrl && Object.keys(widgetConfig).length === 0) {\n return;\n }\n\n try {\n window.AgentWidget.initAgentWidget({\n target,\n config: widgetConfig\n });\n } catch (error) {\n console.error(\"Failed to initialize AgentWidget:\", error);\n }\n };\n\n // Main installation flow\n const install = async () => {\n try {\n await loadCSS();\n await loadJS();\n \n if (autoInit && (config.config || (config as any).apiUrl)) {\n // Wait a tick to ensure AgentWidget is fully initialized\n setTimeout(initWidget, 0);\n }\n } catch (error) {\n console.error(\"Failed to install AgentWidget:\", error);\n }\n };\n\n // Start installation\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", install);\n } else {\n install();\n }\n})();\n\n"],"mappings":"2CAuBC,UAAW,CACV,aAGA,GAAK,OAAe,2BAClB,OAED,OAAe,2BAA6B,GAE7C,IAAMA,EAAiC,OAAO,iBAAmB,CAAC,EAC5DC,EAAUD,EAAO,SAAW,SAC5BE,EAAMF,EAAO,KAAO,WACpBG,EAAWH,EAAO,WAAa,GAG/BI,EAAa,IAAM,CACvB,GAAIJ,EAAO,QAAUA,EAAO,MAC1B,MAAO,CAAE,OAAQA,EAAO,OAAQ,MAAOA,EAAO,KAAM,EAItD,IAAMK,EAAW,sBAAuBJ,CAAO,QAE/C,OAAIC,IAAQ,QACH,CACL,OAAQ,oBAAoBG,CAAQ,cACpC,MAAO,oBAAoBA,CAAQ,kBACrC,EAEO,CACL,OAAQ,2BAA2BA,CAAQ,cAC3C,MAAO,2BAA2BA,CAAQ,kBAC5C,CAEJ,EAEM,CAAE,OAAAC,EAAQ,MAAAC,CAAM,EAAIH,EAAW,EAG/BI,EAAc,IACX,CAAC,CAAC,SAAS,KAAK,cAAc,0BAA0B,GACxD,CAAC,CAAC,SAAS,KAAK,cAAc,0BAA0B,EAI3DC,EAAa,IACV,CAAC,CAAE,OAAe,YAIrBC,EAAU,IACP,IAAI,QAAQ,CAACC,EAASC,IAAW,CACtC,GAAIJ,EAAY,EAAG,CACjBG,EAAQ,EACR,MACF,CAEA,IAAME,EAAO,SAAS,cAAc,MAAM,EAC1CA,EAAK,IAAM,aACXA,EAAK,KAAOP,EACZO,EAAK,aAAa,qBAAsB,MAAM,EAC9CA,EAAK,OAAS,IAAMF,EAAQ,EAC5BE,EAAK,QAAU,IAAMD,EAAO,IAAI,MAAM,2BAA2BN,CAAM,EAAE,CAAC,EAC1E,SAAS,KAAK,YAAYO,CAAI,CAChC,CAAC,EAIGC,EAAS,IACN,IAAI,QAAQ,CAACH,EAASC,IAAW,CACtC,GAAIH,EAAW,EAAG,CAChBE,EAAQ,EACR,MACF,CAEA,IAAMI,EAAS,SAAS,cAAc,QAAQ,EAC9CA,EAAO,IAAMR,EACbQ,EAAO,MAAQ,GACfA,EAAO,OAAS,IAAMJ,EAAQ,EAC9BI,EAAO,QAAU,IAAMH,EAAO,IAAI,MAAM,0BAA0BL,CAAK,EAAE,CAAC,EAC1E,SAAS,KAAK,YAAYQ,CAAM,CAClC,CAAC,EAIGC,EAAa,IAAM,CACvB,GAAI,CAAC,OAAO,aAAe,CAAC,OAAO,YAAY,gBAAiB,CAC9D,QAAQ,KAAK,sEAAsE,EACnF,MACF,CAEA,IAAMC,EAASjB,EAAO,QAAU,OAE1BkB,EAAe,CAAE,GAAGlB,EAAO,MAAO,EAMxC,GALKA,EAAe,QAAU,CAACkB,EAAa,SAC1CA,EAAa,OAAUlB,EAAe,QAIpC,GAACkB,EAAa,QAAU,OAAO,KAAKA,CAAY,EAAE,SAAW,GAIjE,GAAI,CACF,OAAO,YAAY,gBAAgB,CACjC,OAAAD,EACA,OAAQC,CACV,CAAC,CACH,OAASC,EAAO,CACd,QAAQ,MAAM,oCAAqCA,CAAK,CAC1D,CACF,EAGMC,EAAU,SAAY,CAC1B,GAAI,CACF,MAAMV,EAAQ,EACd,MAAMI,EAAO,EAETX,IAAaH,EAAO,QAAWA,EAAe,SAEhD,WAAWgB,EAAY,CAAC,CAE5B,OAASG,EAAO,CACd,QAAQ,MAAM,iCAAkCA,CAAK,CACvD,CACF,EAGI,SAAS,aAAe,UAC1B,SAAS,iBAAiB,mBAAoBC,CAAO,EAErDA,EAAQ,CAEZ,GAAG","names":["config","version","cdn","autoInit","getCdnBase","basePath","cssUrl","jsUrl","isCssLoaded","isJsLoaded","loadCSS","resolve","reject","link","loadJS","script","initWidget","target","widgetConfig","error","install"]}
package/dist/widget.css CHANGED
@@ -572,6 +572,10 @@
572
572
  width: 360px;
573
573
  }
574
574
 
575
+ .tvw-w-\[400px\] {
576
+ width: 400px;
577
+ }
578
+
575
579
  .tvw-h-\[640px\] {
576
580
  height: 640px;
577
581
  }
@@ -687,6 +691,35 @@ form:focus-within textarea {
687
691
  opacity: 1;
688
692
  }
689
693
 
694
+ /* Clear chat button tooltip */
695
+ .tvw-clear-chat-button-wrapper {
696
+ position: relative;
697
+ display: inline-flex;
698
+ align-items: center;
699
+ justify-content: center;
700
+ }
701
+
702
+ .tvw-clear-chat-tooltip {
703
+ background-color: #111827;
704
+ color: #ffffff;
705
+ padding: 6px 12px;
706
+ border-radius: 0.5rem;
707
+ font-size: 12px;
708
+ white-space: nowrap;
709
+ pointer-events: none;
710
+ z-index: 10000;
711
+ }
712
+
713
+ .tvw-clear-chat-tooltip-arrow {
714
+ content: "";
715
+ position: absolute;
716
+ top: 100%;
717
+ left: 50%;
718
+ transform: translateX(-50%);
719
+ border: 4px solid transparent;
720
+ border-top-color: #111827;
721
+ }
722
+
690
723
  /* Typing indicator animation */
691
724
  @keyframes typing {
692
725
  0%, 100% {
@@ -750,3 +783,43 @@ form:focus-within textarea {
750
783
  .tvw-voice-recording svg {
751
784
  animation: voice-recording-pulse 1.5s ease-in-out infinite;
752
785
  }
786
+
787
+ /* Markdown content overflow handling */
788
+ #vanilla-agent-root pre {
789
+ overflow-x: auto;
790
+ max-width: 100%;
791
+ word-wrap: break-word;
792
+ word-break: break-word;
793
+ white-space: pre-wrap;
794
+ background-color: #f3f4f6;
795
+ padding: 0.75rem;
796
+ border-radius: 0.375rem;
797
+ margin: 0.5rem 0;
798
+ font-size: 0.875rem;
799
+ line-height: 1.5;
800
+ border: 1px solid #e5e7eb;
801
+ }
802
+
803
+ #vanilla-agent-root code {
804
+ word-break: break-word;
805
+ word-wrap: break-word;
806
+ white-space: pre-wrap;
807
+ overflow-wrap: break-word;
808
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
809
+ font-size: 0.875em;
810
+ }
811
+
812
+ #vanilla-agent-root pre code {
813
+ font-size: inherit;
814
+ background-color: transparent;
815
+ padding: 0;
816
+ border-radius: 0;
817
+ }
818
+
819
+ #vanilla-agent-root img {
820
+ max-width: 100%;
821
+ height: auto;
822
+ display: block;
823
+ margin: 0.5rem 0;
824
+ border-radius: 0.375rem;
825
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanilla-agent",
3
- "version": "0.2.0",
3
+ "version": "1.1.0",
4
4
  "description": "Themeable, plugable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -53,7 +53,7 @@
53
53
  "scripts": {
54
54
  "build": "rimraf dist && npm run build:styles && npm run build:client && npm run build:installer",
55
55
  "build:styles": "node -e \"const fs=require('fs');fs.mkdirSync('dist',{recursive:true});fs.copyFileSync('src/styles/widget.css','dist/widget.css');\"",
56
- "build:client": "tsup src/index.ts --format esm,cjs,iife --global-name ChatWidget --minify --sourcemap --splitting false --dts --loader \".css=text\"",
56
+ "build:client": "tsup src/index.ts --format esm,cjs,iife --global-name AgentWidget --minify --sourcemap --splitting false --dts --loader \".css=text\"",
57
57
  "build:installer": "tsup src/install.ts --format iife --global-name SiteAgentInstaller --out-dir dist --minify --sourcemap --no-splitting",
58
58
  "lint": "eslint . --ext .ts",
59
59
  "typecheck": "tsc --noEmit"
package/src/client.ts CHANGED
@@ -1,20 +1,20 @@
1
- import { ChatWidgetConfig, ChatWidgetMessage, ChatWidgetEvent } from "./types";
1
+ import { AgentWidgetConfig, AgentWidgetMessage, AgentWidgetEvent } from "./types";
2
2
 
3
3
  type DispatchOptions = {
4
- messages: ChatWidgetMessage[];
4
+ messages: AgentWidgetMessage[];
5
5
  signal?: AbortSignal;
6
6
  };
7
7
 
8
- type SSEHandler = (event: ChatWidgetEvent) => void;
8
+ type SSEHandler = (event: AgentWidgetEvent) => void;
9
9
 
10
10
  const DEFAULT_ENDPOINT = "https://api.travrse.ai/v1/dispatch";
11
11
 
12
- export class ChatWidgetClient {
12
+ export class AgentWidgetClient {
13
13
  private readonly apiUrl: string;
14
14
  private readonly headers: Record<string, string>;
15
15
  private readonly debug: boolean;
16
16
 
17
- constructor(private config: ChatWidgetConfig = {}) {
17
+ constructor(private config: AgentWidgetConfig = {}) {
18
18
  this.apiUrl = config.apiUrl ?? DEFAULT_ENDPOINT;
19
19
  this.headers = {
20
20
  "Content-Type": "application/json",
@@ -51,7 +51,7 @@ export class ChatWidgetClient {
51
51
 
52
52
  if (this.debug) {
53
53
  // eslint-disable-next-line no-console
54
- console.debug("[ChatWidgetClient] dispatch body", body);
54
+ console.debug("[AgentWidgetClient] dispatch body", body);
55
55
  }
56
56
 
57
57
  const response = await fetch(this.apiUrl, {
@@ -89,7 +89,7 @@ export class ChatWidgetClient {
89
89
  let sequenceCounter = 0;
90
90
  const nextSequence = () => baseSequence + sequenceCounter++;
91
91
 
92
- const cloneMessage = (msg: ChatWidgetMessage): ChatWidgetMessage => {
92
+ const cloneMessage = (msg: AgentWidgetMessage): AgentWidgetMessage => {
93
93
  const reasoning = msg.reasoning
94
94
  ? {
95
95
  ...msg.reasoning,
@@ -117,16 +117,16 @@ export class ChatWidgetClient {
117
117
  };
118
118
  };
119
119
 
120
- const emitMessage = (msg: ChatWidgetMessage) => {
120
+ const emitMessage = (msg: AgentWidgetMessage) => {
121
121
  onEvent({
122
122
  type: "message",
123
123
  message: cloneMessage(msg)
124
124
  });
125
125
  };
126
126
 
127
- let assistantMessage: ChatWidgetMessage | null = null;
128
- const reasoningMessages = new Map<string, ChatWidgetMessage>();
129
- const toolMessages = new Map<string, ChatWidgetMessage>();
127
+ let assistantMessage: AgentWidgetMessage | null = null;
128
+ const reasoningMessages = new Map<string, AgentWidgetMessage>();
129
+ const toolMessages = new Map<string, AgentWidgetMessage>();
130
130
  const reasoningContext = {
131
131
  lastId: null as string | null,
132
132
  byStep: new Map<string, string>()
@@ -224,7 +224,7 @@ export class ChatWidgetClient {
224
224
  return existing;
225
225
  }
226
226
 
227
- const message: ChatWidgetMessage = {
227
+ const message: AgentWidgetMessage = {
228
228
  id: `reason-${reasoningId}`,
229
229
  role: "assistant",
230
230
  content: "",
@@ -286,7 +286,7 @@ export class ChatWidgetClient {
286
286
  return existing;
287
287
  }
288
288
 
289
- const message: ChatWidgetMessage = {
289
+ const message: AgentWidgetMessage = {
290
290
  id: `tool-${toolId}`,
291
291
  role: "assistant",
292
292
  content: "",
@@ -556,7 +556,7 @@ export class ChatWidgetClient {
556
556
  } else {
557
557
  const existingAssistant = assistantMessage;
558
558
  if (existingAssistant) {
559
- const assistantFinal = existingAssistant as ChatWidgetMessage;
559
+ const assistantFinal = existingAssistant as AgentWidgetMessage;
560
560
  assistantFinal.streaming = false;
561
561
  emitMessage(assistantFinal);
562
562
  }
@@ -1,6 +1,6 @@
1
1
  import { createElement } from "../utils/dom";
2
- import { ChatWidgetMessage, ChatWidgetConfig } from "../types";
3
- import { ChatWidgetSession } from "../session";
2
+ import { AgentWidgetMessage, AgentWidgetConfig } from "../types";
3
+ import { AgentWidgetSession } from "../session";
4
4
 
5
5
  export const formDefinitions: Record<
6
6
  string,
@@ -40,9 +40,9 @@ export const formDefinitions: Record<
40
40
 
41
41
  export const enhanceWithForms = (
42
42
  bubble: HTMLElement,
43
- message: ChatWidgetMessage,
44
- config: ChatWidgetConfig,
45
- session: ChatWidgetSession
43
+ message: AgentWidgetMessage,
44
+ config: AgentWidgetConfig,
45
+ session: AgentWidgetSession
46
46
  ) => {
47
47
  const placeholders = bubble.querySelectorAll<HTMLElement>("[data-tv-form]");
48
48
  if (placeholders.length) {
@@ -163,3 +163,5 @@ export const enhanceWithForms = (
163
163
 
164
164
 
165
165
 
166
+
167
+
@@ -1,16 +1,16 @@
1
1
  import { createElement } from "../utils/dom";
2
- import { ChatWidgetConfig } from "../types";
2
+ import { AgentWidgetConfig } from "../types";
3
3
  import { positionMap } from "../utils/positioning";
4
4
  import { renderLucideIcon } from "../utils/icons";
5
5
 
6
6
  export interface LauncherButton {
7
7
  element: HTMLButtonElement;
8
- update: (config: ChatWidgetConfig) => void;
8
+ update: (config: AgentWidgetConfig) => void;
9
9
  destroy: () => void;
10
10
  }
11
11
 
12
12
  export const createLauncherButton = (
13
- config: ChatWidgetConfig | undefined,
13
+ config: AgentWidgetConfig | undefined,
14
14
  onToggle: () => void
15
15
  ): LauncherButton => {
16
16
  const button = createElement("button") as HTMLButtonElement;
@@ -26,7 +26,7 @@ export const createLauncherButton = (
26
26
  `;
27
27
  button.addEventListener("click", onToggle);
28
28
 
29
- const update = (newConfig: ChatWidgetConfig) => {
29
+ const update = (newConfig: AgentWidgetConfig) => {
30
30
  const launcher = newConfig.launcher ?? {};
31
31
 
32
32
  const titleEl = button.querySelector("[data-role='launcher-title']");
@@ -1,14 +1,43 @@
1
1
  import { createElement } from "../utils/dom";
2
- import { ChatWidgetMessage } from "../types";
2
+ import { AgentWidgetMessage } from "../types";
3
3
 
4
4
  export type MessageTransform = (context: {
5
5
  text: string;
6
- message: ChatWidgetMessage;
6
+ message: AgentWidgetMessage;
7
7
  streaming: boolean;
8
8
  }) => string;
9
9
 
10
+ // Create typing indicator element
11
+ export const createTypingIndicator = (): HTMLElement => {
12
+ const container = document.createElement("div");
13
+ container.className = "tvw-flex tvw-items-center tvw-space-x-1 tvw-h-5 tvw-mt-2";
14
+
15
+ const dot1 = document.createElement("div");
16
+ dot1.className = "tvw-bg-cw-primary tvw-animate-typing tvw-rounded-full tvw-h-1.5 tvw-w-1.5";
17
+ dot1.style.animationDelay = "0ms";
18
+
19
+ const dot2 = document.createElement("div");
20
+ dot2.className = "tvw-bg-cw-primary tvw-animate-typing tvw-rounded-full tvw-h-1.5 tvw-w-1.5";
21
+ dot2.style.animationDelay = "250ms";
22
+
23
+ const dot3 = document.createElement("div");
24
+ dot3.className = "tvw-bg-cw-primary tvw-animate-typing tvw-rounded-full tvw-h-1.5 tvw-w-1.5";
25
+ dot3.style.animationDelay = "500ms";
26
+
27
+ const srOnly = document.createElement("span");
28
+ srOnly.className = "tvw-sr-only";
29
+ srOnly.textContent = "Loading";
30
+
31
+ container.appendChild(dot1);
32
+ container.appendChild(dot2);
33
+ container.appendChild(dot3);
34
+ container.appendChild(srOnly);
35
+
36
+ return container;
37
+ };
38
+
10
39
  export const createStandardBubble = (
11
- message: ChatWidgetMessage,
40
+ message: AgentWidgetMessage,
12
41
  transform: MessageTransform
13
42
  ): HTMLElement => {
14
43
  const classes = [
@@ -39,11 +68,21 @@ export const createStandardBubble = (
39
68
  }
40
69
 
41
70
  const bubble = createElement("div", classes.join(" "));
42
- bubble.innerHTML = transform({
71
+
72
+ // Add message content
73
+ const contentDiv = document.createElement("div");
74
+ contentDiv.innerHTML = transform({
43
75
  text: message.content,
44
76
  message,
45
77
  streaming: Boolean(message.streaming)
46
78
  });
79
+ bubble.appendChild(contentDiv);
80
+
81
+ // Add typing indicator if this is a streaming assistant message with content
82
+ if (message.streaming && message.role === "assistant" && message.content && message.content.trim()) {
83
+ const typingIndicator = createTypingIndicator();
84
+ bubble.appendChild(typingIndicator);
85
+ }
47
86
 
48
87
  return bubble;
49
88
  };
@@ -1,5 +1,5 @@
1
1
  import { createElement, createFragment } from "../utils/dom";
2
- import { ChatWidgetMessage } from "../types";
2
+ import { AgentWidgetMessage } from "../types";
3
3
  import { MessageTransform } from "./message-bubble";
4
4
  import { createStandardBubble } from "./message-bubble";
5
5
  import { createReasoningBubble } from "./reasoning-bubble";
@@ -7,7 +7,7 @@ import { createToolBubble } from "./tool-bubble";
7
7
 
8
8
  export const renderMessages = (
9
9
  container: HTMLElement,
10
- messages: ChatWidgetMessage[],
10
+ messages: AgentWidgetMessage[],
11
11
  transform: MessageTransform,
12
12
  showReasoning: boolean,
13
13
  showToolCalls: boolean
@@ -41,3 +41,5 @@ export const renderMessages = (
41
41
 
42
42
 
43
43
 
44
+
45
+