vanilla-agent 0.2.0 → 1.0.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,16 +1,16 @@
1
1
  import { createElement } from "../utils/dom";
2
- import { ChatWidgetSession } from "../session";
3
- import { ChatWidgetMessage } from "../types";
2
+ import { AgentWidgetSession } from "../session";
3
+ import { AgentWidgetMessage } from "../types";
4
4
 
5
5
  export interface SuggestionButtons {
6
6
  buttons: HTMLButtonElement[];
7
- render: (chips: string[] | undefined, session: ChatWidgetSession, textarea: HTMLTextAreaElement, messages?: ChatWidgetMessage[]) => void;
7
+ render: (chips: string[] | undefined, session: AgentWidgetSession, textarea: HTMLTextAreaElement, messages?: AgentWidgetMessage[]) => void;
8
8
  }
9
9
 
10
10
  export const createSuggestions = (container: HTMLElement): SuggestionButtons => {
11
11
  const suggestionButtons: HTMLButtonElement[] = [];
12
12
 
13
- const render = (chips: string[] | undefined, session: ChatWidgetSession, textarea: HTMLTextAreaElement, messages?: ChatWidgetMessage[]) => {
13
+ const render = (chips: string[] | undefined, session: AgentWidgetSession, textarea: HTMLTextAreaElement, messages?: AgentWidgetMessage[]) => {
14
14
  container.innerHTML = "";
15
15
  suggestionButtons.length = 0;
16
16
  if (!chips || !chips.length) return;
@@ -1,11 +1,11 @@
1
1
  import { createElement } from "../utils/dom";
2
- import { ChatWidgetMessage } from "../types";
2
+ import { AgentWidgetMessage } from "../types";
3
3
  import { formatUnknownValue, describeToolTitle } from "../utils/formatting";
4
4
 
5
5
  // Expansion state per widget instance
6
6
  const toolExpansionState = new Set<string>();
7
7
 
8
- export const createToolBubble = (message: ChatWidgetMessage): HTMLElement => {
8
+ export const createToolBubble = (message: AgentWidgetMessage): HTMLElement => {
9
9
  const tool = message.toolCall;
10
10
  const bubble = createElement(
11
11
  "div",
@@ -0,0 +1,180 @@
1
+ import type { AgentWidgetConfig } from "./types";
2
+
3
+ /**
4
+ * Default widget configuration
5
+ * Single source of truth for all default values
6
+ */
7
+ export const DEFAULT_WIDGET_CONFIG: Partial<AgentWidgetConfig> = {
8
+ apiUrl: "http://localhost:43111/api/chat/dispatch",
9
+ theme: {
10
+ primary: "#111827",
11
+ accent: "#1d4ed8",
12
+ surface: "#ffffff",
13
+ muted: "#6b7280",
14
+ container: "#f8fafc",
15
+ border: "#f1f5f9",
16
+ divider: "#f1f5f9",
17
+ messageBorder: "#f1f5f9",
18
+ inputBackground: "#ffffff",
19
+ callToAction: "#000000",
20
+ callToActionBackground: "#ffffff",
21
+ sendButtonBackgroundColor: "#111827",
22
+ sendButtonTextColor: "#ffffff",
23
+ sendButtonBorderColor: "#60a5fa",
24
+ closeButtonColor: "#6b7280",
25
+ closeButtonBackgroundColor: "transparent",
26
+ closeButtonBorderColor: "",
27
+ clearChatIconColor: "#6b7280",
28
+ clearChatBackgroundColor: "transparent",
29
+ clearChatBorderColor: "transparent",
30
+ micIconColor: "#111827",
31
+ micBackgroundColor: "transparent",
32
+ micBorderColor: "transparent",
33
+ recordingIconColor: "#ffffff",
34
+ recordingBackgroundColor: "#ef4444",
35
+ recordingBorderColor: "transparent",
36
+ inputFontFamily: "sans-serif",
37
+ inputFontWeight: "400",
38
+ radiusSm: "0.75rem",
39
+ radiusMd: "1rem",
40
+ radiusLg: "1.5rem",
41
+ launcherRadius: "9999px",
42
+ buttonRadius: "9999px",
43
+ },
44
+ launcher: {
45
+ enabled: true,
46
+ title: "Chat Assistant",
47
+ subtitle: "Here to help you get answers fast",
48
+ agentIconText: "💬",
49
+ position: "bottom-right",
50
+ width: "min(400px, calc(100vw - 24px))",
51
+ autoExpand: false,
52
+ callToActionIconHidden: false,
53
+ agentIconSize: "40px",
54
+ headerIconSize: "40px",
55
+ closeButtonSize: "32px",
56
+ callToActionIconName: "arrow-up-right",
57
+ callToActionIconText: "",
58
+ callToActionIconSize: "32px",
59
+ callToActionIconPadding: "5px",
60
+ callToActionIconColor: "#000000",
61
+ callToActionIconBackgroundColor: "#ffffff",
62
+ closeButtonColor: "#6b7280",
63
+ closeButtonBackgroundColor: "transparent",
64
+ clearChat: {
65
+ iconColor: "#6b7280",
66
+ backgroundColor: "transparent",
67
+ borderColor: "transparent",
68
+ enabled: true,
69
+ iconName: "refresh-cw",
70
+ size: "29px",
71
+ showTooltip: true,
72
+ tooltipText: "Clear chat",
73
+ paddingX: "0px",
74
+ paddingY: "0px",
75
+ },
76
+ headerIconHidden: false,
77
+ },
78
+ copy: {
79
+ welcomeTitle: "Hello 👋",
80
+ welcomeSubtitle: "Ask anything about your account or products.",
81
+ inputPlaceholder: "How can I help...",
82
+ sendButtonLabel: "Send",
83
+ },
84
+ sendButton: {
85
+ borderWidth: "0px",
86
+ paddingX: "12px",
87
+ paddingY: "10px",
88
+ backgroundColor: "#111827",
89
+ textColor: "#ffffff",
90
+ borderColor: "#60a5fa",
91
+ useIcon: true,
92
+ iconText: "↑",
93
+ size: "40px",
94
+ showTooltip: true,
95
+ tooltipText: "Send message",
96
+ iconName: "send",
97
+ },
98
+ statusIndicator: {
99
+ visible: true,
100
+ idleText: "Online",
101
+ connectingText: "Connecting…",
102
+ connectedText: "Streaming…",
103
+ errorText: "Offline",
104
+ },
105
+ voiceRecognition: {
106
+ enabled: true,
107
+ pauseDuration: 2000,
108
+ iconName: "mic",
109
+ iconSize: "39px",
110
+ borderWidth: "0px",
111
+ paddingX: "9px",
112
+ paddingY: "14px",
113
+ iconColor: "#111827",
114
+ backgroundColor: "transparent",
115
+ borderColor: "transparent",
116
+ recordingIconColor: "#ffffff",
117
+ recordingBackgroundColor: "#ef4444",
118
+ recordingBorderColor: "transparent",
119
+ showTooltip: true,
120
+ tooltipText: "Start voice recognition",
121
+ },
122
+ features: {
123
+ showReasoning: true,
124
+ showToolCalls: true,
125
+ },
126
+ suggestionChips: [
127
+ "What can you help me with?",
128
+ "Tell me about your features",
129
+ "How does this work?",
130
+ ],
131
+ debug: false,
132
+ };
133
+
134
+ /**
135
+ * Helper to deep merge user config with defaults
136
+ * This ensures all default values are present while allowing selective overrides
137
+ */
138
+ export function mergeWithDefaults(
139
+ config?: Partial<AgentWidgetConfig>
140
+ ): Partial<AgentWidgetConfig> {
141
+ if (!config) return DEFAULT_WIDGET_CONFIG;
142
+
143
+ return {
144
+ ...DEFAULT_WIDGET_CONFIG,
145
+ ...config,
146
+ theme: {
147
+ ...DEFAULT_WIDGET_CONFIG.theme,
148
+ ...config.theme,
149
+ },
150
+ launcher: {
151
+ ...DEFAULT_WIDGET_CONFIG.launcher,
152
+ ...config.launcher,
153
+ clearChat: {
154
+ ...DEFAULT_WIDGET_CONFIG.launcher?.clearChat,
155
+ ...config.launcher?.clearChat,
156
+ },
157
+ },
158
+ copy: {
159
+ ...DEFAULT_WIDGET_CONFIG.copy,
160
+ ...config.copy,
161
+ },
162
+ sendButton: {
163
+ ...DEFAULT_WIDGET_CONFIG.sendButton,
164
+ ...config.sendButton,
165
+ },
166
+ statusIndicator: {
167
+ ...DEFAULT_WIDGET_CONFIG.statusIndicator,
168
+ ...config.statusIndicator,
169
+ },
170
+ voiceRecognition: {
171
+ ...DEFAULT_WIDGET_CONFIG.voiceRecognition,
172
+ ...config.voiceRecognition,
173
+ },
174
+ features: {
175
+ ...DEFAULT_WIDGET_CONFIG.features,
176
+ ...config.features,
177
+ },
178
+ suggestionChips: config.suggestionChips ?? DEFAULT_WIDGET_CONFIG.suggestionChips,
179
+ };
180
+ }
package/src/index.ts CHANGED
@@ -1,37 +1,40 @@
1
1
  import {
2
- initChatWidget as initChatWidgetFn,
3
- type ChatWidgetInitHandle
2
+ initAgentWidget as initAgentWidgetFn,
3
+ type AgentWidgetInitHandle
4
4
  } from "./runtime/init";
5
5
 
6
6
  export type {
7
- ChatWidgetConfig,
8
- ChatWidgetTheme,
9
- ChatWidgetFeatureFlags,
10
- ChatWidgetInitOptions,
11
- ChatWidgetMessage,
12
- ChatWidgetLauncherConfig,
13
- ChatWidgetEvent
7
+ AgentWidgetConfig,
8
+ AgentWidgetTheme,
9
+ AgentWidgetFeatureFlags,
10
+ AgentWidgetInitOptions,
11
+ AgentWidgetMessage,
12
+ AgentWidgetLauncherConfig,
13
+ AgentWidgetEvent
14
14
  } from "./types";
15
15
 
16
- export { initChatWidgetFn as initChatWidget };
16
+ export { initAgentWidgetFn as initAgentWidget };
17
17
  export {
18
- createChatExperience,
19
- type ChatWidgetController
18
+ createAgentExperience,
19
+ type AgentWidgetController
20
20
  } from "./ui";
21
21
  export {
22
- ChatWidgetSession,
23
- type ChatWidgetSessionStatus
22
+ AgentWidgetSession,
23
+ type AgentWidgetSessionStatus
24
24
  } from "./session";
25
- export { ChatWidgetClient } from "./client";
25
+ export { AgentWidgetClient } from "./client";
26
26
  export {
27
27
  markdownPostprocessor,
28
28
  escapeHtml,
29
29
  directivePostprocessor
30
30
  } from "./postprocessors";
31
- export type { ChatWidgetInitHandle };
31
+ export type { AgentWidgetInitHandle };
32
32
 
33
33
  // Plugin system exports
34
- export type { ChatWidgetPlugin } from "./plugins/types";
34
+ export type { AgentWidgetPlugin } from "./plugins/types";
35
35
  export { pluginRegistry } from "./plugins/registry";
36
36
 
37
- export default initChatWidgetFn;
37
+ // Default configuration exports
38
+ export { DEFAULT_WIDGET_CONFIG, mergeWithDefaults } from "./defaults";
39
+
40
+ export default initAgentWidgetFn;
package/src/install.ts CHANGED
@@ -17,7 +17,7 @@ interface SiteAgentInstallConfig {
17
17
  declare global {
18
18
  interface Window {
19
19
  siteAgentConfig?: SiteAgentInstallConfig;
20
- ChatWidget?: any;
20
+ AgentWidget?: any;
21
21
  }
22
22
  }
23
23
 
@@ -67,7 +67,7 @@ declare global {
67
67
 
68
68
  // Check if JS is already loaded
69
69
  const isJsLoaded = () => {
70
- return !!(window as any).ChatWidget;
70
+ return !!(window as any).AgentWidget;
71
71
  };
72
72
 
73
73
  // Load CSS
@@ -107,8 +107,8 @@ declare global {
107
107
 
108
108
  // Initialize widget
109
109
  const initWidget = () => {
110
- if (!window.ChatWidget || !window.ChatWidget.initChatWidget) {
111
- console.warn("ChatWidget not available. Make sure the script loaded successfully.");
110
+ if (!window.AgentWidget || !window.AgentWidget.initAgentWidget) {
111
+ console.warn("AgentWidget not available. Make sure the script loaded successfully.");
112
112
  return;
113
113
  }
114
114
 
@@ -125,12 +125,12 @@ declare global {
125
125
  }
126
126
 
127
127
  try {
128
- window.ChatWidget.initChatWidget({
128
+ window.AgentWidget.initAgentWidget({
129
129
  target,
130
130
  config: widgetConfig
131
131
  });
132
132
  } catch (error) {
133
- console.error("Failed to initialize ChatWidget:", error);
133
+ console.error("Failed to initialize AgentWidget:", error);
134
134
  }
135
135
  };
136
136
 
@@ -141,11 +141,11 @@ declare global {
141
141
  await loadJS();
142
142
 
143
143
  if (autoInit && (config.config || (config as any).apiUrl)) {
144
- // Wait a tick to ensure ChatWidget is fully initialized
144
+ // Wait a tick to ensure AgentWidget is fully initialized
145
145
  setTimeout(initWidget, 0);
146
146
  }
147
147
  } catch (error) {
148
- console.error("Failed to install ChatWidget:", error);
148
+ console.error("Failed to install AgentWidget:", error);
149
149
  }
150
150
  };
151
151
 
@@ -1,12 +1,12 @@
1
- import { ChatWidgetPlugin } from "./types";
1
+ import { AgentWidgetPlugin } from "./types";
2
2
 
3
3
  class PluginRegistry {
4
- private plugins: Map<string, ChatWidgetPlugin> = new Map();
4
+ private plugins: Map<string, AgentWidgetPlugin> = new Map();
5
5
 
6
6
  /**
7
7
  * Register a plugin
8
8
  */
9
- register(plugin: ChatWidgetPlugin): void {
9
+ register(plugin: AgentWidgetPlugin): void {
10
10
  if (this.plugins.has(plugin.id)) {
11
11
  console.warn(`Plugin "${plugin.id}" is already registered. Overwriting.`);
12
12
  }
@@ -29,7 +29,7 @@ class PluginRegistry {
29
29
  /**
30
30
  * Get all plugins sorted by priority
31
31
  */
32
- getAll(): ChatWidgetPlugin[] {
32
+ getAll(): AgentWidgetPlugin[] {
33
33
  return Array.from(this.plugins.values()).sort(
34
34
  (a, b) => (b.priority ?? 0) - (a.priority ?? 0)
35
35
  );
@@ -39,7 +39,7 @@ class PluginRegistry {
39
39
  * Get plugins for a specific instance (from config)
40
40
  * Merges instance plugins with globally registered plugins
41
41
  */
42
- getForInstance(instancePlugins?: ChatWidgetPlugin[]): ChatWidgetPlugin[] {
42
+ getForInstance(instancePlugins?: AgentWidgetPlugin[]): AgentWidgetPlugin[] {
43
43
  const allPlugins = this.getAll();
44
44
 
45
45
  if (!instancePlugins || instancePlugins.length === 0) {
@@ -70,3 +70,5 @@ export const pluginRegistry = new PluginRegistry();
70
70
 
71
71
 
72
72
 
73
+
74
+
@@ -1,9 +1,9 @@
1
- import { ChatWidgetMessage, ChatWidgetConfig } from "../types";
1
+ import { AgentWidgetMessage, AgentWidgetConfig } from "../types";
2
2
 
3
3
  /**
4
4
  * Plugin interface for customizing widget components
5
5
  */
6
- export interface ChatWidgetPlugin {
6
+ export interface AgentWidgetPlugin {
7
7
  /**
8
8
  * Unique identifier for the plugin
9
9
  */
@@ -19,9 +19,9 @@ export interface ChatWidgetPlugin {
19
19
  * Return null to use default renderer
20
20
  */
21
21
  renderMessage?: (context: {
22
- message: ChatWidgetMessage;
22
+ message: AgentWidgetMessage;
23
23
  defaultRenderer: () => HTMLElement;
24
- config: ChatWidgetConfig;
24
+ config: AgentWidgetConfig;
25
25
  }) => HTMLElement | null;
26
26
 
27
27
  /**
@@ -29,7 +29,7 @@ export interface ChatWidgetPlugin {
29
29
  * Return null to use default renderer
30
30
  */
31
31
  renderLauncher?: (context: {
32
- config: ChatWidgetConfig;
32
+ config: AgentWidgetConfig;
33
33
  defaultRenderer: () => HTMLElement;
34
34
  onToggle: () => void;
35
35
  }) => HTMLElement | null;
@@ -39,7 +39,7 @@ export interface ChatWidgetPlugin {
39
39
  * Return null to use default renderer
40
40
  */
41
41
  renderHeader?: (context: {
42
- config: ChatWidgetConfig;
42
+ config: AgentWidgetConfig;
43
43
  defaultRenderer: () => HTMLElement;
44
44
  onClose?: () => void;
45
45
  }) => HTMLElement | null;
@@ -49,7 +49,7 @@ export interface ChatWidgetPlugin {
49
49
  * Return null to use default renderer
50
50
  */
51
51
  renderComposer?: (context: {
52
- config: ChatWidgetConfig;
52
+ config: AgentWidgetConfig;
53
53
  defaultRenderer: () => HTMLElement;
54
54
  onSubmit: (text: string) => void;
55
55
  disabled: boolean;
@@ -60,9 +60,9 @@ export interface ChatWidgetPlugin {
60
60
  * Return null to use default renderer
61
61
  */
62
62
  renderReasoning?: (context: {
63
- message: ChatWidgetMessage;
63
+ message: AgentWidgetMessage;
64
64
  defaultRenderer: () => HTMLElement;
65
- config: ChatWidgetConfig;
65
+ config: AgentWidgetConfig;
66
66
  }) => HTMLElement | null;
67
67
 
68
68
  /**
@@ -70,9 +70,9 @@ export interface ChatWidgetPlugin {
70
70
  * Return null to use default renderer
71
71
  */
72
72
  renderToolCall?: (context: {
73
- message: ChatWidgetMessage;
73
+ message: AgentWidgetMessage;
74
74
  defaultRenderer: () => HTMLElement;
75
- config: ChatWidgetConfig;
75
+ config: AgentWidgetConfig;
76
76
  }) => HTMLElement | null;
77
77
 
78
78
  /**
@@ -88,3 +88,5 @@ export interface ChatWidgetPlugin {
88
88
 
89
89
 
90
90
 
91
+
92
+
@@ -1,5 +1,5 @@
1
- import { createChatExperience, ChatWidgetController } from "../ui";
2
- import { ChatWidgetConfig, ChatWidgetInitOptions } from "../types";
1
+ import { createAgentExperience, AgentWidgetController } from "../ui";
2
+ import { AgentWidgetConfig, AgentWidgetInitOptions } from "../types";
3
3
 
4
4
  const ensureTarget = (target: string | HTMLElement): HTMLElement => {
5
5
  if (typeof window === "undefined" || typeof document === "undefined") {
@@ -84,11 +84,11 @@ const mountStyles = (root: ShadowRoot | HTMLElement) => {
84
84
  }
85
85
  };
86
86
 
87
- export type ChatWidgetInitHandle = ChatWidgetController & { host: HTMLElement };
87
+ export type AgentWidgetInitHandle = AgentWidgetController & { host: HTMLElement };
88
88
 
89
- export const initChatWidget = (
90
- options: ChatWidgetInitOptions
91
- ): ChatWidgetInitHandle => {
89
+ export const initAgentWidget = (
90
+ options: AgentWidgetInitOptions
91
+ ): AgentWidgetInitHandle => {
92
92
  const target = ensureTarget(options.target);
93
93
  const host = document.createElement("div");
94
94
  host.className = "vanilla-agent-host";
@@ -113,12 +113,12 @@ export const initChatWidget = (
113
113
  mountStyles(host);
114
114
  }
115
115
 
116
- let controller = createChatExperience(mount, options.config);
116
+ let controller = createAgentExperience(mount, options.config);
117
117
  options.onReady?.();
118
118
 
119
119
  return {
120
120
  host,
121
- update(nextConfig: ChatWidgetConfig) {
121
+ update(nextConfig: AgentWidgetConfig) {
122
122
  controller.update(nextConfig);
123
123
  },
124
124
  open() {
@@ -130,6 +130,9 @@ export const initChatWidget = (
130
130
  toggle() {
131
131
  controller.toggle();
132
132
  },
133
+ clearChat() {
134
+ controller.clearChat();
135
+ },
133
136
  destroy() {
134
137
  controller.destroy();
135
138
  host.remove();
package/src/session.ts CHANGED
@@ -1,33 +1,33 @@
1
- import { ChatWidgetClient } from "./client";
1
+ import { AgentWidgetClient } from "./client";
2
2
  import {
3
- ChatWidgetConfig,
4
- ChatWidgetEvent,
5
- ChatWidgetMessage
3
+ AgentWidgetConfig,
4
+ AgentWidgetEvent,
5
+ AgentWidgetMessage
6
6
  } from "./types";
7
7
 
8
- export type ChatWidgetSessionStatus =
8
+ export type AgentWidgetSessionStatus =
9
9
  | "idle"
10
10
  | "connecting"
11
11
  | "connected"
12
12
  | "error";
13
13
 
14
14
  type SessionCallbacks = {
15
- onMessagesChanged: (messages: ChatWidgetMessage[]) => void;
16
- onStatusChanged: (status: ChatWidgetSessionStatus) => void;
15
+ onMessagesChanged: (messages: AgentWidgetMessage[]) => void;
16
+ onStatusChanged: (status: AgentWidgetSessionStatus) => void;
17
17
  onStreamingChanged: (streaming: boolean) => void;
18
18
  onError?: (error: Error) => void;
19
19
  };
20
20
 
21
- export class ChatWidgetSession {
22
- private client: ChatWidgetClient;
23
- private messages: ChatWidgetMessage[];
24
- private status: ChatWidgetSessionStatus = "idle";
21
+ export class AgentWidgetSession {
22
+ private client: AgentWidgetClient;
23
+ private messages: AgentWidgetMessage[];
24
+ private status: AgentWidgetSessionStatus = "idle";
25
25
  private streaming = false;
26
26
  private abortController: AbortController | null = null;
27
27
  private sequenceCounter = Date.now();
28
28
 
29
29
  constructor(
30
- private config: ChatWidgetConfig = {},
30
+ private config: AgentWidgetConfig = {},
31
31
  private callbacks: SessionCallbacks
32
32
  ) {
33
33
  this.messages = [...(config.initialMessages ?? [])].map((message) => ({
@@ -35,7 +35,7 @@ export class ChatWidgetSession {
35
35
  sequence: message.sequence ?? this.nextSequence()
36
36
  }));
37
37
  this.messages = this.sortMessages(this.messages);
38
- this.client = new ChatWidgetClient(config);
38
+ this.client = new AgentWidgetClient(config);
39
39
 
40
40
  if (this.messages.length) {
41
41
  this.callbacks.onMessagesChanged([...this.messages]);
@@ -43,9 +43,9 @@ export class ChatWidgetSession {
43
43
  this.callbacks.onStatusChanged(this.status);
44
44
  }
45
45
 
46
- public updateConfig(next: ChatWidgetConfig) {
46
+ public updateConfig(next: AgentWidgetConfig) {
47
47
  this.config = { ...this.config, ...next };
48
- this.client = new ChatWidgetClient(this.config);
48
+ this.client = new AgentWidgetClient(this.config);
49
49
  }
50
50
 
51
51
  public getMessages() {
@@ -66,7 +66,7 @@ export class ChatWidgetSession {
66
66
 
67
67
  this.abortController?.abort();
68
68
 
69
- const userMessage: ChatWidgetMessage = {
69
+ const userMessage: AgentWidgetMessage = {
70
70
  id: `user-${Date.now()}`,
71
71
  role: "user",
72
72
  content: input,
@@ -91,7 +91,7 @@ export class ChatWidgetSession {
91
91
  this.handleEvent
92
92
  );
93
93
  } catch (error) {
94
- const fallback: ChatWidgetMessage = {
94
+ const fallback: AgentWidgetMessage = {
95
95
  id: `assistant-${Date.now()}`,
96
96
  role: "assistant",
97
97
  createdAt: new Date().toISOString(),
@@ -119,7 +119,16 @@ export class ChatWidgetSession {
119
119
  this.setStatus("idle");
120
120
  }
121
121
 
122
- private handleEvent = (event: ChatWidgetEvent) => {
122
+ public clearMessages() {
123
+ this.abortController?.abort();
124
+ this.abortController = null;
125
+ this.messages = [];
126
+ this.setStreaming(false);
127
+ this.setStatus("idle");
128
+ this.callbacks.onMessagesChanged([...this.messages]);
129
+ }
130
+
131
+ private handleEvent = (event: AgentWidgetEvent) => {
123
132
  if (event.type === "message") {
124
133
  this.upsertMessage(event.message);
125
134
  } else if (event.type === "status") {
@@ -138,7 +147,7 @@ export class ChatWidgetSession {
138
147
  }
139
148
  };
140
149
 
141
- private setStatus(status: ChatWidgetSessionStatus) {
150
+ private setStatus(status: AgentWidgetSessionStatus) {
142
151
  if (this.status === status) return;
143
152
  this.status = status;
144
153
  this.callbacks.onStatusChanged(status);
@@ -150,13 +159,13 @@ export class ChatWidgetSession {
150
159
  this.callbacks.onStreamingChanged(streaming);
151
160
  }
152
161
 
153
- private appendMessage(message: ChatWidgetMessage) {
162
+ private appendMessage(message: AgentWidgetMessage) {
154
163
  const withSequence = this.ensureSequence(message);
155
164
  this.messages = this.sortMessages([...this.messages, withSequence]);
156
165
  this.callbacks.onMessagesChanged([...this.messages]);
157
166
  }
158
167
 
159
- private upsertMessage(message: ChatWidgetMessage) {
168
+ private upsertMessage(message: AgentWidgetMessage) {
160
169
  const withSequence = this.ensureSequence(message);
161
170
  const index = this.messages.findIndex((m) => m.id === withSequence.id);
162
171
  if (index === -1) {
@@ -171,7 +180,7 @@ export class ChatWidgetSession {
171
180
  this.callbacks.onMessagesChanged([...this.messages]);
172
181
  }
173
182
 
174
- private ensureSequence(message: ChatWidgetMessage): ChatWidgetMessage {
183
+ private ensureSequence(message: AgentWidgetMessage): AgentWidgetMessage {
175
184
  if (message.sequence !== undefined) {
176
185
  return { ...message };
177
186
  }
@@ -185,7 +194,7 @@ export class ChatWidgetSession {
185
194
  return this.sequenceCounter++;
186
195
  }
187
196
 
188
- private sortMessages(messages: ChatWidgetMessage[]) {
197
+ private sortMessages(messages: AgentWidgetMessage[]) {
189
198
  return [...messages].sort((a, b) => {
190
199
  // Sort by createdAt timestamp first (chronological order)
191
200
  const timeA = new Date(a.createdAt).getTime();
@@ -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% {