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/src/components/panel.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createElement } from "../utils/dom";
|
|
2
2
|
import { renderLucideIcon } from "../utils/icons";
|
|
3
|
-
import {
|
|
3
|
+
import { AgentWidgetConfig } from "../types";
|
|
4
4
|
import { positionMap } from "../utils/positioning";
|
|
5
5
|
|
|
6
6
|
export interface PanelWrapper {
|
|
@@ -8,7 +8,7 @@ export interface PanelWrapper {
|
|
|
8
8
|
panel: HTMLElement;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export const createWrapper = (config?:
|
|
11
|
+
export const createWrapper = (config?: AgentWidgetConfig): PanelWrapper => {
|
|
12
12
|
const launcherEnabled = config?.launcher?.enabled ?? true;
|
|
13
13
|
|
|
14
14
|
if (!launcherEnabled) {
|
|
@@ -40,7 +40,7 @@ export const createWrapper = (config?: ChatWidgetConfig): PanelWrapper => {
|
|
|
40
40
|
"tvw-relative tvw-min-h-[320px]"
|
|
41
41
|
);
|
|
42
42
|
const launcherWidth = config?.launcher?.width ?? config?.launcherWidth;
|
|
43
|
-
const width = launcherWidth ?? "min(
|
|
43
|
+
const width = launcherWidth ?? "min(400px, calc(100vw - 24px))";
|
|
44
44
|
panel.style.width = width;
|
|
45
45
|
panel.style.maxWidth = width;
|
|
46
46
|
|
|
@@ -63,10 +63,13 @@ export interface PanelElements {
|
|
|
63
63
|
introTitle: HTMLElement;
|
|
64
64
|
introSubtitle: HTMLElement;
|
|
65
65
|
closeButton: HTMLButtonElement;
|
|
66
|
+
closeButtonWrapper: HTMLElement;
|
|
67
|
+
clearChatButton: HTMLButtonElement | null;
|
|
68
|
+
clearChatButtonWrapper: HTMLElement | null;
|
|
66
69
|
iconHolder: HTMLElement;
|
|
67
70
|
}
|
|
68
71
|
|
|
69
|
-
export const buildPanel = (config?:
|
|
72
|
+
export const buildPanel = (config?: AgentWidgetConfig, showClose = true): PanelElements => {
|
|
70
73
|
const container = createElement(
|
|
71
74
|
"div",
|
|
72
75
|
"tvw-flex tvw-h-full tvw-w-full tvw-flex-col tvw-bg-cw-surface tvw-text-cw-primary tvw-rounded-2xl tvw-overflow-hidden tvw-shadow-2xl tvw-border tvw-border-cw-border"
|
|
@@ -141,19 +144,184 @@ export const buildPanel = (config?: ChatWidgetConfig, showClose = true): PanelEl
|
|
|
141
144
|
header.append(headerCopy);
|
|
142
145
|
}
|
|
143
146
|
|
|
147
|
+
// Create clear chat button if enabled
|
|
148
|
+
const clearChatConfig = launcher.clearChat ?? {};
|
|
149
|
+
const clearChatEnabled = clearChatConfig.enabled ?? true;
|
|
150
|
+
let clearChatButton: HTMLButtonElement | null = null;
|
|
151
|
+
let clearChatButtonWrapper: HTMLElement | null = null;
|
|
152
|
+
|
|
153
|
+
if (clearChatEnabled) {
|
|
154
|
+
const clearChatSize = clearChatConfig.size ?? "32px";
|
|
155
|
+
const clearChatIconName = clearChatConfig.iconName ?? "refresh-cw";
|
|
156
|
+
const clearChatIconColor = clearChatConfig.iconColor ?? "";
|
|
157
|
+
const clearChatBgColor = clearChatConfig.backgroundColor ?? "";
|
|
158
|
+
const clearChatBorderWidth = clearChatConfig.borderWidth ?? "";
|
|
159
|
+
const clearChatBorderColor = clearChatConfig.borderColor ?? "";
|
|
160
|
+
const clearChatBorderRadius = clearChatConfig.borderRadius ?? "";
|
|
161
|
+
const clearChatPaddingX = clearChatConfig.paddingX ?? "";
|
|
162
|
+
const clearChatPaddingY = clearChatConfig.paddingY ?? "";
|
|
163
|
+
const clearChatTooltipText = clearChatConfig.tooltipText ?? "Clear chat";
|
|
164
|
+
const clearChatShowTooltip = clearChatConfig.showTooltip ?? true;
|
|
165
|
+
|
|
166
|
+
// Create button wrapper for tooltip
|
|
167
|
+
clearChatButtonWrapper = createElement(
|
|
168
|
+
"div",
|
|
169
|
+
"tvw-relative tvw-ml-auto tvw-clear-chat-button-wrapper"
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
clearChatButton = createElement(
|
|
173
|
+
"button",
|
|
174
|
+
"tvw-inline-flex tvw-items-center tvw-justify-center tvw-rounded-full tvw-text-cw-muted hover:tvw-bg-gray-100 tvw-cursor-pointer tvw-border-none"
|
|
175
|
+
) as HTMLButtonElement;
|
|
176
|
+
|
|
177
|
+
clearChatButton.style.height = clearChatSize;
|
|
178
|
+
clearChatButton.style.width = clearChatSize;
|
|
179
|
+
clearChatButton.type = "button";
|
|
180
|
+
clearChatButton.setAttribute("aria-label", clearChatTooltipText);
|
|
181
|
+
|
|
182
|
+
// Add icon
|
|
183
|
+
const iconSvg = renderLucideIcon(clearChatIconName, "20px", clearChatIconColor || "", 2);
|
|
184
|
+
if (iconSvg) {
|
|
185
|
+
clearChatButton.appendChild(iconSvg);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Apply styling from config
|
|
189
|
+
if (clearChatIconColor) {
|
|
190
|
+
clearChatButton.style.color = clearChatIconColor;
|
|
191
|
+
clearChatButton.classList.remove("tvw-text-cw-muted");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (clearChatBgColor) {
|
|
195
|
+
clearChatButton.style.backgroundColor = clearChatBgColor;
|
|
196
|
+
clearChatButton.classList.remove("hover:tvw-bg-gray-100");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (clearChatBorderWidth || clearChatBorderColor) {
|
|
200
|
+
const borderWidth = clearChatBorderWidth || "0px";
|
|
201
|
+
const borderColor = clearChatBorderColor || "transparent";
|
|
202
|
+
clearChatButton.style.border = `${borderWidth} solid ${borderColor}`;
|
|
203
|
+
clearChatButton.classList.remove("tvw-border-none");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (clearChatBorderRadius) {
|
|
207
|
+
clearChatButton.style.borderRadius = clearChatBorderRadius;
|
|
208
|
+
clearChatButton.classList.remove("tvw-rounded-full");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Apply padding styling
|
|
212
|
+
if (clearChatPaddingX) {
|
|
213
|
+
clearChatButton.style.paddingLeft = clearChatPaddingX;
|
|
214
|
+
clearChatButton.style.paddingRight = clearChatPaddingX;
|
|
215
|
+
} else {
|
|
216
|
+
clearChatButton.style.paddingLeft = "";
|
|
217
|
+
clearChatButton.style.paddingRight = "";
|
|
218
|
+
}
|
|
219
|
+
if (clearChatPaddingY) {
|
|
220
|
+
clearChatButton.style.paddingTop = clearChatPaddingY;
|
|
221
|
+
clearChatButton.style.paddingBottom = clearChatPaddingY;
|
|
222
|
+
} else {
|
|
223
|
+
clearChatButton.style.paddingTop = "";
|
|
224
|
+
clearChatButton.style.paddingBottom = "";
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
clearChatButtonWrapper.appendChild(clearChatButton);
|
|
228
|
+
|
|
229
|
+
// Add tooltip with portaling to document.body to escape overflow clipping
|
|
230
|
+
if (clearChatShowTooltip && clearChatTooltipText && clearChatButton && clearChatButtonWrapper) {
|
|
231
|
+
let portaledTooltip: HTMLElement | null = null;
|
|
232
|
+
|
|
233
|
+
const showTooltip = () => {
|
|
234
|
+
if (portaledTooltip || !clearChatButton) return; // Already showing or button doesn't exist
|
|
235
|
+
|
|
236
|
+
// Create tooltip element
|
|
237
|
+
portaledTooltip = createElement("div", "tvw-clear-chat-tooltip");
|
|
238
|
+
portaledTooltip.textContent = clearChatTooltipText;
|
|
239
|
+
|
|
240
|
+
// Add arrow
|
|
241
|
+
const arrow = createElement("div");
|
|
242
|
+
arrow.className = "tvw-clear-chat-tooltip-arrow";
|
|
243
|
+
portaledTooltip.appendChild(arrow);
|
|
244
|
+
|
|
245
|
+
// Get button position
|
|
246
|
+
const buttonRect = clearChatButton.getBoundingClientRect();
|
|
247
|
+
|
|
248
|
+
// Position tooltip above button
|
|
249
|
+
portaledTooltip.style.position = "fixed";
|
|
250
|
+
portaledTooltip.style.left = `${buttonRect.left + buttonRect.width / 2}px`;
|
|
251
|
+
portaledTooltip.style.top = `${buttonRect.top - 8}px`;
|
|
252
|
+
portaledTooltip.style.transform = "translate(-50%, -100%)";
|
|
253
|
+
|
|
254
|
+
// Append to body
|
|
255
|
+
document.body.appendChild(portaledTooltip);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const hideTooltip = () => {
|
|
259
|
+
if (portaledTooltip && portaledTooltip.parentNode) {
|
|
260
|
+
portaledTooltip.parentNode.removeChild(portaledTooltip);
|
|
261
|
+
portaledTooltip = null;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// Add event listeners
|
|
266
|
+
clearChatButtonWrapper.addEventListener("mouseenter", showTooltip);
|
|
267
|
+
clearChatButtonWrapper.addEventListener("mouseleave", hideTooltip);
|
|
268
|
+
clearChatButton.addEventListener("focus", showTooltip);
|
|
269
|
+
clearChatButton.addEventListener("blur", hideTooltip);
|
|
270
|
+
|
|
271
|
+
// Store cleanup function on the button for later use
|
|
272
|
+
(clearChatButtonWrapper as any)._cleanupTooltip = () => {
|
|
273
|
+
hideTooltip();
|
|
274
|
+
if (clearChatButtonWrapper) {
|
|
275
|
+
clearChatButtonWrapper.removeEventListener("mouseenter", showTooltip);
|
|
276
|
+
clearChatButtonWrapper.removeEventListener("mouseleave", hideTooltip);
|
|
277
|
+
}
|
|
278
|
+
if (clearChatButton) {
|
|
279
|
+
clearChatButton.removeEventListener("focus", showTooltip);
|
|
280
|
+
clearChatButton.removeEventListener("blur", hideTooltip);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
header.appendChild(clearChatButtonWrapper);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Create close button wrapper for tooltip positioning
|
|
289
|
+
const closeButtonWrapper = createElement(
|
|
290
|
+
"div",
|
|
291
|
+
closeButtonPlacement === "top-right"
|
|
292
|
+
? "tvw-absolute tvw-top-4 tvw-right-4 tvw-z-50"
|
|
293
|
+
: (clearChatEnabled
|
|
294
|
+
? ""
|
|
295
|
+
: "tvw-ml-auto")
|
|
296
|
+
);
|
|
297
|
+
|
|
144
298
|
// Create close button with base classes
|
|
145
299
|
const closeButton = createElement(
|
|
146
300
|
"button",
|
|
147
|
-
|
|
148
|
-
? "tvw-absolute tvw-top-4 tvw-right-4 tvw-z-50 tvw-inline-flex tvw-items-center tvw-justify-center tvw-rounded-full tvw-text-cw-muted hover:tvw-bg-gray-100 tvw-cursor-pointer tvw-border-none"
|
|
149
|
-
: "tvw-ml-auto tvw-inline-flex tvw-items-center tvw-justify-center tvw-rounded-full tvw-text-cw-muted hover:tvw-bg-gray-100 tvw-cursor-pointer tvw-border-none"
|
|
301
|
+
"tvw-inline-flex tvw-items-center tvw-justify-center tvw-rounded-full tvw-text-cw-muted hover:tvw-bg-gray-100 tvw-cursor-pointer tvw-border-none"
|
|
150
302
|
) as HTMLButtonElement;
|
|
151
303
|
closeButton.style.height = closeButtonSize;
|
|
152
304
|
closeButton.style.width = closeButtonSize;
|
|
153
305
|
closeButton.type = "button";
|
|
154
|
-
|
|
155
|
-
|
|
306
|
+
|
|
307
|
+
// Get tooltip config
|
|
308
|
+
const closeButtonTooltipText = launcher.closeButtonTooltipText ?? "Close chat";
|
|
309
|
+
const closeButtonShowTooltip = launcher.closeButtonShowTooltip ?? true;
|
|
310
|
+
|
|
311
|
+
closeButton.setAttribute("aria-label", closeButtonTooltipText);
|
|
156
312
|
closeButton.style.display = showClose ? "" : "none";
|
|
313
|
+
|
|
314
|
+
// Add icon or fallback text
|
|
315
|
+
const closeButtonIconName = launcher.closeButtonIconName ?? "x";
|
|
316
|
+
const closeButtonIconText = launcher.closeButtonIconText ?? "×";
|
|
317
|
+
|
|
318
|
+
// Try to render Lucide icon, fallback to text if not provided or fails
|
|
319
|
+
const iconSvg = renderLucideIcon(closeButtonIconName, "20px", launcher.closeButtonColor || "", 2);
|
|
320
|
+
if (iconSvg) {
|
|
321
|
+
closeButton.appendChild(iconSvg);
|
|
322
|
+
} else {
|
|
323
|
+
closeButton.textContent = closeButtonIconText;
|
|
324
|
+
}
|
|
157
325
|
|
|
158
326
|
// Apply close button styling from config
|
|
159
327
|
if (launcher.closeButtonColor) {
|
|
@@ -190,15 +358,85 @@ export const buildPanel = (config?: ChatWidgetConfig, showClose = true): PanelEl
|
|
|
190
358
|
closeButton.style.borderRadius = "";
|
|
191
359
|
closeButton.classList.add("tvw-rounded-full");
|
|
192
360
|
}
|
|
193
|
-
|
|
194
|
-
//
|
|
361
|
+
|
|
362
|
+
// Apply padding styling
|
|
363
|
+
if (launcher.closeButtonPaddingX) {
|
|
364
|
+
closeButton.style.paddingLeft = launcher.closeButtonPaddingX;
|
|
365
|
+
closeButton.style.paddingRight = launcher.closeButtonPaddingX;
|
|
366
|
+
} else {
|
|
367
|
+
closeButton.style.paddingLeft = "";
|
|
368
|
+
closeButton.style.paddingRight = "";
|
|
369
|
+
}
|
|
370
|
+
if (launcher.closeButtonPaddingY) {
|
|
371
|
+
closeButton.style.paddingTop = launcher.closeButtonPaddingY;
|
|
372
|
+
closeButton.style.paddingBottom = launcher.closeButtonPaddingY;
|
|
373
|
+
} else {
|
|
374
|
+
closeButton.style.paddingTop = "";
|
|
375
|
+
closeButton.style.paddingBottom = "";
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
closeButtonWrapper.appendChild(closeButton);
|
|
379
|
+
|
|
380
|
+
// Add tooltip with portaling to document.body to escape overflow clipping
|
|
381
|
+
if (closeButtonShowTooltip && closeButtonTooltipText) {
|
|
382
|
+
let portaledTooltip: HTMLElement | null = null;
|
|
383
|
+
|
|
384
|
+
const showTooltip = () => {
|
|
385
|
+
if (portaledTooltip) return; // Already showing
|
|
386
|
+
|
|
387
|
+
// Create tooltip element
|
|
388
|
+
portaledTooltip = createElement("div", "tvw-clear-chat-tooltip");
|
|
389
|
+
portaledTooltip.textContent = closeButtonTooltipText;
|
|
390
|
+
|
|
391
|
+
// Add arrow
|
|
392
|
+
const arrow = createElement("div");
|
|
393
|
+
arrow.className = "tvw-clear-chat-tooltip-arrow";
|
|
394
|
+
portaledTooltip.appendChild(arrow);
|
|
395
|
+
|
|
396
|
+
// Get button position
|
|
397
|
+
const buttonRect = closeButton.getBoundingClientRect();
|
|
398
|
+
|
|
399
|
+
// Position tooltip above button
|
|
400
|
+
portaledTooltip.style.position = "fixed";
|
|
401
|
+
portaledTooltip.style.left = `${buttonRect.left + buttonRect.width / 2}px`;
|
|
402
|
+
portaledTooltip.style.top = `${buttonRect.top - 8}px`;
|
|
403
|
+
portaledTooltip.style.transform = "translate(-50%, -100%)";
|
|
404
|
+
|
|
405
|
+
// Append to body
|
|
406
|
+
document.body.appendChild(portaledTooltip);
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const hideTooltip = () => {
|
|
410
|
+
if (portaledTooltip && portaledTooltip.parentNode) {
|
|
411
|
+
portaledTooltip.parentNode.removeChild(portaledTooltip);
|
|
412
|
+
portaledTooltip = null;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
// Add event listeners
|
|
417
|
+
closeButtonWrapper.addEventListener("mouseenter", showTooltip);
|
|
418
|
+
closeButtonWrapper.addEventListener("mouseleave", hideTooltip);
|
|
419
|
+
closeButton.addEventListener("focus", showTooltip);
|
|
420
|
+
closeButton.addEventListener("blur", hideTooltip);
|
|
421
|
+
|
|
422
|
+
// Store cleanup function on the wrapper for later use
|
|
423
|
+
(closeButtonWrapper as any)._cleanupTooltip = () => {
|
|
424
|
+
hideTooltip();
|
|
425
|
+
closeButtonWrapper.removeEventListener("mouseenter", showTooltip);
|
|
426
|
+
closeButtonWrapper.removeEventListener("mouseleave", hideTooltip);
|
|
427
|
+
closeButton.removeEventListener("focus", showTooltip);
|
|
428
|
+
closeButton.removeEventListener("blur", hideTooltip);
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Position close button wrapper based on placement
|
|
195
433
|
if (closeButtonPlacement === "top-right") {
|
|
196
434
|
// Make container position relative for absolute positioning
|
|
197
435
|
container.style.position = "relative";
|
|
198
|
-
container.appendChild(
|
|
436
|
+
container.appendChild(closeButtonWrapper);
|
|
199
437
|
} else {
|
|
200
438
|
// Inline placement: append to header
|
|
201
|
-
header.appendChild(
|
|
439
|
+
header.appendChild(closeButtonWrapper);
|
|
202
440
|
}
|
|
203
441
|
|
|
204
442
|
const body = createElement(
|
|
@@ -547,6 +785,9 @@ export const buildPanel = (config?: ChatWidgetConfig, showClose = true): PanelEl
|
|
|
547
785
|
introTitle,
|
|
548
786
|
introSubtitle,
|
|
549
787
|
closeButton,
|
|
788
|
+
closeButtonWrapper,
|
|
789
|
+
clearChatButton,
|
|
790
|
+
clearChatButtonWrapper,
|
|
550
791
|
iconHolder
|
|
551
792
|
};
|
|
552
793
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createElement } from "../utils/dom";
|
|
2
|
-
import {
|
|
2
|
+
import { AgentWidgetMessage } from "../types";
|
|
3
3
|
import { describeReasonStatus } from "../utils/formatting";
|
|
4
4
|
|
|
5
5
|
// Expansion state per widget instance
|
|
6
6
|
const reasoningExpansionState = new Set<string>();
|
|
7
7
|
|
|
8
|
-
export const createReasoningBubble = (message:
|
|
8
|
+
export const createReasoningBubble = (message: AgentWidgetMessage): HTMLElement => {
|
|
9
9
|
const reasoning = message.reasoning;
|
|
10
10
|
const bubble = createElement(
|
|
11
11
|
"div",
|
|
@@ -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
|
|