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.
- 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 +33 -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 +3 -3
- 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 +33 -0
- package/src/types.ts +56 -31
- package/src/ui.ts +330 -22
- 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
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { createElement } from "../utils/dom";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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:
|
|
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:
|
|
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 {
|
|
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:
|
|
8
|
+
export const createToolBubble = (message: AgentWidgetMessage): HTMLElement => {
|
|
9
9
|
const tool = message.toolCall;
|
|
10
10
|
const bubble = createElement(
|
|
11
11
|
"div",
|
package/src/defaults.ts
ADDED
|
@@ -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
|
-
|
|
3
|
-
type
|
|
2
|
+
initAgentWidget as initAgentWidgetFn,
|
|
3
|
+
type AgentWidgetInitHandle
|
|
4
4
|
} from "./runtime/init";
|
|
5
5
|
|
|
6
6
|
export type {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
AgentWidgetConfig,
|
|
8
|
+
AgentWidgetTheme,
|
|
9
|
+
AgentWidgetFeatureFlags,
|
|
10
|
+
AgentWidgetInitOptions,
|
|
11
|
+
AgentWidgetMessage,
|
|
12
|
+
AgentWidgetLauncherConfig,
|
|
13
|
+
AgentWidgetEvent
|
|
14
14
|
} from "./types";
|
|
15
15
|
|
|
16
|
-
export {
|
|
16
|
+
export { initAgentWidgetFn as initAgentWidget };
|
|
17
17
|
export {
|
|
18
|
-
|
|
19
|
-
type
|
|
18
|
+
createAgentExperience,
|
|
19
|
+
type AgentWidgetController
|
|
20
20
|
} from "./ui";
|
|
21
21
|
export {
|
|
22
|
-
|
|
23
|
-
type
|
|
22
|
+
AgentWidgetSession,
|
|
23
|
+
type AgentWidgetSessionStatus
|
|
24
24
|
} from "./session";
|
|
25
|
-
export {
|
|
25
|
+
export { AgentWidgetClient } from "./client";
|
|
26
26
|
export {
|
|
27
27
|
markdownPostprocessor,
|
|
28
28
|
escapeHtml,
|
|
29
29
|
directivePostprocessor
|
|
30
30
|
} from "./postprocessors";
|
|
31
|
-
export type {
|
|
31
|
+
export type { AgentWidgetInitHandle };
|
|
32
32
|
|
|
33
33
|
// Plugin system exports
|
|
34
|
-
export type {
|
|
34
|
+
export type { AgentWidgetPlugin } from "./plugins/types";
|
|
35
35
|
export { pluginRegistry } from "./plugins/registry";
|
|
36
36
|
|
|
37
|
-
|
|
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
|
-
|
|
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).
|
|
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.
|
|
111
|
-
console.warn("
|
|
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.
|
|
128
|
+
window.AgentWidget.initAgentWidget({
|
|
129
129
|
target,
|
|
130
130
|
config: widgetConfig
|
|
131
131
|
});
|
|
132
132
|
} catch (error) {
|
|
133
|
-
console.error("Failed to initialize
|
|
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
|
|
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
|
|
148
|
+
console.error("Failed to install AgentWidget:", error);
|
|
149
149
|
}
|
|
150
150
|
};
|
|
151
151
|
|
package/src/plugins/registry.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AgentWidgetPlugin } from "./types";
|
|
2
2
|
|
|
3
3
|
class PluginRegistry {
|
|
4
|
-
private plugins: Map<string,
|
|
4
|
+
private plugins: Map<string, AgentWidgetPlugin> = new Map();
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Register a plugin
|
|
8
8
|
*/
|
|
9
|
-
register(plugin:
|
|
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():
|
|
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?:
|
|
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
|
+
|
package/src/plugins/types.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AgentWidgetMessage, AgentWidgetConfig } from "../types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Plugin interface for customizing widget components
|
|
5
5
|
*/
|
|
6
|
-
export interface
|
|
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:
|
|
22
|
+
message: AgentWidgetMessage;
|
|
23
23
|
defaultRenderer: () => HTMLElement;
|
|
24
|
-
config:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
63
|
+
message: AgentWidgetMessage;
|
|
64
64
|
defaultRenderer: () => HTMLElement;
|
|
65
|
-
config:
|
|
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:
|
|
73
|
+
message: AgentWidgetMessage;
|
|
74
74
|
defaultRenderer: () => HTMLElement;
|
|
75
|
-
config:
|
|
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
|
+
|
package/src/runtime/init.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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
|
|
87
|
+
export type AgentWidgetInitHandle = AgentWidgetController & { host: HTMLElement };
|
|
88
88
|
|
|
89
|
-
export const
|
|
90
|
-
options:
|
|
91
|
-
):
|
|
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 =
|
|
116
|
+
let controller = createAgentExperience(mount, options.config);
|
|
117
117
|
options.onReady?.();
|
|
118
118
|
|
|
119
119
|
return {
|
|
120
120
|
host,
|
|
121
|
-
update(nextConfig:
|
|
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 {
|
|
1
|
+
import { AgentWidgetClient } from "./client";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
AgentWidgetConfig,
|
|
4
|
+
AgentWidgetEvent,
|
|
5
|
+
AgentWidgetMessage
|
|
6
6
|
} from "./types";
|
|
7
7
|
|
|
8
|
-
export type
|
|
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:
|
|
16
|
-
onStatusChanged: (status:
|
|
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
|
|
22
|
-
private client:
|
|
23
|
-
private messages:
|
|
24
|
-
private status:
|
|
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:
|
|
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
|
|
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:
|
|
46
|
+
public updateConfig(next: AgentWidgetConfig) {
|
|
47
47
|
this.config = { ...this.config, ...next };
|
|
48
|
-
this.client = new
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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();
|
package/src/styles/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% {
|