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.
- package/README.md +53 -21
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +98 -61
- package/dist/index.d.ts +98 -61
- package/dist/index.global.js +36 -30
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/install.global.js +1 -1
- package/dist/install.global.js.map +1 -1
- package/dist/widget.css +73 -0
- package/package.json +2 -2
- package/src/client.ts +14 -14
- package/src/components/forms.ts +7 -5
- package/src/components/launcher.ts +4 -4
- package/src/components/message-bubble.ts +43 -4
- package/src/components/messages.ts +4 -2
- package/src/components/panel.ts +254 -13
- package/src/components/reasoning-bubble.ts +2 -2
- package/src/components/suggestions.ts +4 -4
- package/src/components/tool-bubble.ts +2 -2
- package/src/defaults.ts +180 -0
- package/src/index.ts +21 -18
- package/src/install.ts +8 -8
- package/src/plugins/registry.ts +7 -5
- package/src/plugins/types.ts +13 -11
- package/src/runtime/init.ts +11 -8
- package/src/session.ts +32 -23
- package/src/styles/widget.css +73 -0
- package/src/types.ts +56 -31
- package/src/ui.ts +338 -53
- package/src/utils/constants.ts +4 -2
- package/src/utils/dom.ts +2 -0
- package/src/utils/formatting.ts +8 -6
- package/src/utils/icons.ts +1 -1
- package/src/utils/positioning.ts +2 -0
- package/src/utils/theme.ts +4 -2
package/dist/install.global.js
CHANGED
|
@@ -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
|
|
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
|
|
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": "
|
|
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
|
|
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 {
|
|
1
|
+
import { AgentWidgetConfig, AgentWidgetMessage, AgentWidgetEvent } from "./types";
|
|
2
2
|
|
|
3
3
|
type DispatchOptions = {
|
|
4
|
-
messages:
|
|
4
|
+
messages: AgentWidgetMessage[];
|
|
5
5
|
signal?: AbortSignal;
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
type SSEHandler = (event:
|
|
8
|
+
type SSEHandler = (event: AgentWidgetEvent) => void;
|
|
9
9
|
|
|
10
10
|
const DEFAULT_ENDPOINT = "https://api.travrse.ai/v1/dispatch";
|
|
11
11
|
|
|
12
|
-
export class
|
|
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:
|
|
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("[
|
|
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:
|
|
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:
|
|
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:
|
|
128
|
-
const reasoningMessages = new Map<string,
|
|
129
|
-
const toolMessages = new Map<string,
|
|
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:
|
|
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:
|
|
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
|
|
559
|
+
const assistantFinal = existingAssistant as AgentWidgetMessage;
|
|
560
560
|
assistantFinal.streaming = false;
|
|
561
561
|
emitMessage(assistantFinal);
|
|
562
562
|
}
|
package/src/components/forms.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createElement } from "../utils/dom";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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:
|
|
44
|
-
config:
|
|
45
|
-
session:
|
|
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 {
|
|
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:
|
|
8
|
+
update: (config: AgentWidgetConfig) => void;
|
|
9
9
|
destroy: () => void;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export const createLauncherButton = (
|
|
13
|
-
config:
|
|
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:
|
|
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 {
|
|
2
|
+
import { AgentWidgetMessage } from "../types";
|
|
3
3
|
|
|
4
4
|
export type MessageTransform = (context: {
|
|
5
5
|
text: string;
|
|
6
|
-
message:
|
|
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:
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
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
|
+
|