vanilla-agent 1.7.2 → 1.8.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/dist/index.cjs +7 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.global.js +45 -45
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/suggestions.ts +48 -3
- package/src/defaults.ts +10 -0
- package/src/types.ts +25 -0
- package/src/ui.ts +70 -19
- package/src/utils/actions.ts +6 -7
package/package.json
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
import { createElement } from "../utils/dom";
|
|
2
2
|
import { AgentWidgetSession } from "../session";
|
|
3
|
-
import { AgentWidgetMessage } from "../types";
|
|
3
|
+
import { AgentWidgetMessage, AgentWidgetSuggestionChipsConfig } from "../types";
|
|
4
4
|
|
|
5
5
|
export interface SuggestionButtons {
|
|
6
6
|
buttons: HTMLButtonElement[];
|
|
7
|
-
render: (
|
|
7
|
+
render: (
|
|
8
|
+
chips: string[] | undefined,
|
|
9
|
+
session: AgentWidgetSession,
|
|
10
|
+
textarea: HTMLTextAreaElement,
|
|
11
|
+
messages?: AgentWidgetMessage[],
|
|
12
|
+
config?: AgentWidgetSuggestionChipsConfig
|
|
13
|
+
) => void;
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
export const createSuggestions = (container: HTMLElement): SuggestionButtons => {
|
|
11
17
|
const suggestionButtons: HTMLButtonElement[] = [];
|
|
12
18
|
|
|
13
|
-
const render = (
|
|
19
|
+
const render = (
|
|
20
|
+
chips: string[] | undefined,
|
|
21
|
+
session: AgentWidgetSession,
|
|
22
|
+
textarea: HTMLTextAreaElement,
|
|
23
|
+
messages?: AgentWidgetMessage[],
|
|
24
|
+
chipsConfig?: AgentWidgetSuggestionChipsConfig
|
|
25
|
+
) => {
|
|
14
26
|
container.innerHTML = "";
|
|
15
27
|
suggestionButtons.length = 0;
|
|
16
28
|
if (!chips || !chips.length) return;
|
|
@@ -23,6 +35,20 @@ export const createSuggestions = (container: HTMLElement): SuggestionButtons =>
|
|
|
23
35
|
|
|
24
36
|
const fragment = document.createDocumentFragment();
|
|
25
37
|
const streaming = session ? session.isStreaming() : false;
|
|
38
|
+
|
|
39
|
+
// Get font family mapping function
|
|
40
|
+
const getFontFamilyValue = (family: "sans-serif" | "serif" | "mono"): string => {
|
|
41
|
+
switch (family) {
|
|
42
|
+
case "serif":
|
|
43
|
+
return 'Georgia, "Times New Roman", Times, serif';
|
|
44
|
+
case "mono":
|
|
45
|
+
return '"Courier New", Courier, "Lucida Console", Monaco, monospace';
|
|
46
|
+
case "sans-serif":
|
|
47
|
+
default:
|
|
48
|
+
return '-apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif';
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
26
52
|
chips.forEach((chip) => {
|
|
27
53
|
const btn = createElement(
|
|
28
54
|
"button",
|
|
@@ -31,6 +57,25 @@ export const createSuggestions = (container: HTMLElement): SuggestionButtons =>
|
|
|
31
57
|
btn.type = "button";
|
|
32
58
|
btn.textContent = chip;
|
|
33
59
|
btn.disabled = streaming;
|
|
60
|
+
|
|
61
|
+
// Apply typography settings
|
|
62
|
+
if (chipsConfig?.fontFamily) {
|
|
63
|
+
btn.style.fontFamily = getFontFamilyValue(chipsConfig.fontFamily);
|
|
64
|
+
}
|
|
65
|
+
if (chipsConfig?.fontWeight) {
|
|
66
|
+
btn.style.fontWeight = chipsConfig.fontWeight;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Apply padding settings
|
|
70
|
+
if (chipsConfig?.paddingX) {
|
|
71
|
+
btn.style.paddingLeft = chipsConfig.paddingX;
|
|
72
|
+
btn.style.paddingRight = chipsConfig.paddingX;
|
|
73
|
+
}
|
|
74
|
+
if (chipsConfig?.paddingY) {
|
|
75
|
+
btn.style.paddingTop = chipsConfig.paddingY;
|
|
76
|
+
btn.style.paddingBottom = chipsConfig.paddingY;
|
|
77
|
+
}
|
|
78
|
+
|
|
34
79
|
btn.addEventListener("click", () => {
|
|
35
80
|
if (!session || session.isStreaming()) return;
|
|
36
81
|
textarea.value = "";
|
package/src/defaults.ts
CHANGED
|
@@ -128,6 +128,12 @@ export const DEFAULT_WIDGET_CONFIG: Partial<AgentWidgetConfig> = {
|
|
|
128
128
|
"Tell me about your features",
|
|
129
129
|
"How does this work?",
|
|
130
130
|
],
|
|
131
|
+
suggestionChipsConfig: {
|
|
132
|
+
fontFamily: "sans-serif",
|
|
133
|
+
fontWeight: "500",
|
|
134
|
+
paddingX: "12px",
|
|
135
|
+
paddingY: "6px",
|
|
136
|
+
},
|
|
131
137
|
debug: false,
|
|
132
138
|
};
|
|
133
139
|
|
|
@@ -176,5 +182,9 @@ export function mergeWithDefaults(
|
|
|
176
182
|
...config.features,
|
|
177
183
|
},
|
|
178
184
|
suggestionChips: config.suggestionChips ?? DEFAULT_WIDGET_CONFIG.suggestionChips,
|
|
185
|
+
suggestionChipsConfig: {
|
|
186
|
+
...DEFAULT_WIDGET_CONFIG.suggestionChipsConfig,
|
|
187
|
+
...config.suggestionChipsConfig,
|
|
188
|
+
},
|
|
179
189
|
};
|
|
180
190
|
}
|
package/src/types.ts
CHANGED
|
@@ -51,6 +51,7 @@ export type AgentWidgetActionParser = (
|
|
|
51
51
|
export type AgentWidgetActionHandlerResult = {
|
|
52
52
|
handled?: boolean;
|
|
53
53
|
displayText?: string;
|
|
54
|
+
persistMessage?: boolean; // If false, prevents message from being saved to history
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
export type AgentWidgetActionContext = {
|
|
@@ -92,11 +93,27 @@ export type AgentWidgetActionEventPayload = {
|
|
|
92
93
|
message: AgentWidgetMessage;
|
|
93
94
|
};
|
|
94
95
|
|
|
96
|
+
export type AgentWidgetStateEvent = {
|
|
97
|
+
open: boolean;
|
|
98
|
+
source: "user" | "auto" | "api" | "system";
|
|
99
|
+
timestamp: number;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export type AgentWidgetStateSnapshot = {
|
|
103
|
+
open: boolean;
|
|
104
|
+
launcherEnabled: boolean;
|
|
105
|
+
voiceActive: boolean;
|
|
106
|
+
streaming: boolean;
|
|
107
|
+
};
|
|
108
|
+
|
|
95
109
|
export type AgentWidgetControllerEventMap = {
|
|
96
110
|
"assistant:message": AgentWidgetMessage;
|
|
97
111
|
"assistant:complete": AgentWidgetMessage;
|
|
98
112
|
"voice:state": AgentWidgetVoiceStateEvent;
|
|
99
113
|
"action:detected": AgentWidgetActionEventPayload;
|
|
114
|
+
"widget:opened": AgentWidgetStateEvent;
|
|
115
|
+
"widget:closed": AgentWidgetStateEvent;
|
|
116
|
+
"widget:state": AgentWidgetStateSnapshot;
|
|
100
117
|
};
|
|
101
118
|
|
|
102
119
|
export type AgentWidgetFeatureFlags = {
|
|
@@ -258,6 +275,13 @@ export type AgentWidgetToolCallConfig = {
|
|
|
258
275
|
labelTextColor?: string;
|
|
259
276
|
};
|
|
260
277
|
|
|
278
|
+
export type AgentWidgetSuggestionChipsConfig = {
|
|
279
|
+
fontFamily?: "sans-serif" | "serif" | "mono";
|
|
280
|
+
fontWeight?: string;
|
|
281
|
+
paddingX?: string;
|
|
282
|
+
paddingY?: string;
|
|
283
|
+
};
|
|
284
|
+
|
|
261
285
|
/**
|
|
262
286
|
* Interface for pluggable stream parsers that extract text from streaming responses.
|
|
263
287
|
* Parsers handle incremental parsing to extract text values from structured formats (JSON, XML, etc.).
|
|
@@ -330,6 +354,7 @@ export type AgentWidgetConfig = {
|
|
|
330
354
|
launcher?: AgentWidgetLauncherConfig;
|
|
331
355
|
initialMessages?: AgentWidgetMessage[];
|
|
332
356
|
suggestionChips?: string[];
|
|
357
|
+
suggestionChipsConfig?: AgentWidgetSuggestionChipsConfig;
|
|
333
358
|
debug?: boolean;
|
|
334
359
|
formEndpoint?: string;
|
|
335
360
|
launcherWidth?: string;
|
package/src/ui.ts
CHANGED
|
@@ -7,7 +7,9 @@ import {
|
|
|
7
7
|
AgentWidgetStorageAdapter,
|
|
8
8
|
AgentWidgetStoredState,
|
|
9
9
|
AgentWidgetControllerEventMap,
|
|
10
|
-
AgentWidgetVoiceStateEvent
|
|
10
|
+
AgentWidgetVoiceStateEvent,
|
|
11
|
+
AgentWidgetStateEvent,
|
|
12
|
+
AgentWidgetStateSnapshot
|
|
11
13
|
} from "./types";
|
|
12
14
|
import { applyThemeVariables } from "./utils/theme";
|
|
13
15
|
import { renderLucideIcon } from "./utils/icons";
|
|
@@ -73,6 +75,10 @@ type Controller = {
|
|
|
73
75
|
event: K,
|
|
74
76
|
handler: (payload: AgentWidgetControllerEventMap[K]) => void
|
|
75
77
|
) => void;
|
|
78
|
+
// State query methods
|
|
79
|
+
isOpen: () => boolean;
|
|
80
|
+
isVoiceActive: () => boolean;
|
|
81
|
+
getState: () => AgentWidgetStateSnapshot;
|
|
76
82
|
};
|
|
77
83
|
|
|
78
84
|
const buildPostprocessor = (
|
|
@@ -84,14 +90,18 @@ const buildPostprocessor = (
|
|
|
84
90
|
const rawPayload = context.message.rawContent ?? null;
|
|
85
91
|
|
|
86
92
|
if (actionManager) {
|
|
87
|
-
const
|
|
93
|
+
const actionResult = actionManager.process({
|
|
88
94
|
text: nextText,
|
|
89
95
|
raw: rawPayload ?? nextText,
|
|
90
96
|
message: context.message,
|
|
91
97
|
streaming: context.streaming
|
|
92
98
|
});
|
|
93
|
-
if (
|
|
94
|
-
nextText =
|
|
99
|
+
if (actionResult !== null) {
|
|
100
|
+
nextText = actionResult.text;
|
|
101
|
+
// Mark message as non-persistable if persist is false
|
|
102
|
+
if (!actionResult.persist) {
|
|
103
|
+
(context.message as any).__skipPersist = true;
|
|
104
|
+
}
|
|
95
105
|
}
|
|
96
106
|
}
|
|
97
107
|
|
|
@@ -285,7 +295,9 @@ export const createAgentExperience = (
|
|
|
285
295
|
};
|
|
286
296
|
|
|
287
297
|
const getMessagesForPersistence = () =>
|
|
288
|
-
session
|
|
298
|
+
session
|
|
299
|
+
? stripStreamingFromMessages(session.getMessages()).filter(msg => !(msg as any).__skipPersist)
|
|
300
|
+
: [];
|
|
289
301
|
|
|
290
302
|
function persistState(messagesOverride?: AgentWidgetMessage[]) {
|
|
291
303
|
if (!storageAdapter?.save || !session) return;
|
|
@@ -597,15 +609,39 @@ export const createAgentExperience = (
|
|
|
597
609
|
}
|
|
598
610
|
};
|
|
599
611
|
|
|
600
|
-
const setOpenState = (nextOpen: boolean) => {
|
|
612
|
+
const setOpenState = (nextOpen: boolean, source: "user" | "auto" | "api" | "system" = "user") => {
|
|
601
613
|
if (!launcherEnabled) return;
|
|
602
614
|
if (open === nextOpen) return;
|
|
615
|
+
|
|
616
|
+
const prevOpen = open;
|
|
603
617
|
open = nextOpen;
|
|
604
618
|
updateOpenState();
|
|
619
|
+
|
|
605
620
|
if (open) {
|
|
606
621
|
recalcPanelHeight();
|
|
607
622
|
scheduleAutoScroll(true);
|
|
608
623
|
}
|
|
624
|
+
|
|
625
|
+
// Emit widget state events
|
|
626
|
+
const stateEvent: AgentWidgetStateEvent = {
|
|
627
|
+
open,
|
|
628
|
+
source,
|
|
629
|
+
timestamp: Date.now()
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
if (open && !prevOpen) {
|
|
633
|
+
eventBus.emit("widget:opened", stateEvent);
|
|
634
|
+
} else if (!open && prevOpen) {
|
|
635
|
+
eventBus.emit("widget:closed", stateEvent);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Emit general state snapshot
|
|
639
|
+
eventBus.emit("widget:state", {
|
|
640
|
+
open,
|
|
641
|
+
launcherEnabled,
|
|
642
|
+
voiceActive: voiceState.active,
|
|
643
|
+
streaming: session.isStreaming()
|
|
644
|
+
});
|
|
609
645
|
};
|
|
610
646
|
|
|
611
647
|
const setComposerDisabled = (disabled: boolean) => {
|
|
@@ -664,7 +700,7 @@ export const createAgentExperience = (
|
|
|
664
700
|
suggestionsManager.render([], session, textarea, messages);
|
|
665
701
|
} else {
|
|
666
702
|
// Show suggestions if no user message yet
|
|
667
|
-
suggestionsManager.render(config.suggestionChips, session, textarea, messages);
|
|
703
|
+
suggestionsManager.render(config.suggestionChips, session, textarea, messages, config.suggestionChipsConfig);
|
|
668
704
|
}
|
|
669
705
|
}
|
|
670
706
|
scheduleAutoScroll(!isStreaming);
|
|
@@ -1059,7 +1095,7 @@ export const createAgentExperience = (
|
|
|
1059
1095
|
destroyCallbacks.push(autoResumeUnsub);
|
|
1060
1096
|
|
|
1061
1097
|
const toggleOpen = () => {
|
|
1062
|
-
setOpenState(!open);
|
|
1098
|
+
setOpenState(!open, "user");
|
|
1063
1099
|
};
|
|
1064
1100
|
|
|
1065
1101
|
let launcherButtonInstance = launcherEnabled
|
|
@@ -1070,7 +1106,7 @@ export const createAgentExperience = (
|
|
|
1070
1106
|
mount.appendChild(launcherButtonInstance.element);
|
|
1071
1107
|
}
|
|
1072
1108
|
updateOpenState();
|
|
1073
|
-
suggestionsManager.render(config.suggestionChips, session, textarea);
|
|
1109
|
+
suggestionsManager.render(config.suggestionChips, session, textarea, undefined, config.suggestionChipsConfig);
|
|
1074
1110
|
updateCopy();
|
|
1075
1111
|
setComposerDisabled(session.isStreaming());
|
|
1076
1112
|
scheduleAutoScroll(true);
|
|
@@ -1271,11 +1307,11 @@ export const createAgentExperience = (
|
|
|
1271
1307
|
updateOpenState();
|
|
1272
1308
|
} else {
|
|
1273
1309
|
// Launcher was just enabled - respect autoExpand setting
|
|
1274
|
-
setOpenState(autoExpand);
|
|
1310
|
+
setOpenState(autoExpand, "auto");
|
|
1275
1311
|
}
|
|
1276
1312
|
} else if (autoExpandChanged) {
|
|
1277
1313
|
// autoExpand value changed - update state to match
|
|
1278
|
-
setOpenState(autoExpand);
|
|
1314
|
+
setOpenState(autoExpand, "auto");
|
|
1279
1315
|
}
|
|
1280
1316
|
// Otherwise, preserve current open state (user may have manually opened/closed)
|
|
1281
1317
|
|
|
@@ -1716,7 +1752,7 @@ export const createAgentExperience = (
|
|
|
1716
1752
|
session.getMessages(),
|
|
1717
1753
|
postprocess
|
|
1718
1754
|
);
|
|
1719
|
-
suggestionsManager.render(config.suggestionChips, session, textarea);
|
|
1755
|
+
suggestionsManager.render(config.suggestionChips, session, textarea, undefined, config.suggestionChipsConfig);
|
|
1720
1756
|
updateCopy();
|
|
1721
1757
|
setComposerDisabled(session.isStreaming());
|
|
1722
1758
|
|
|
@@ -2013,15 +2049,15 @@ export const createAgentExperience = (
|
|
|
2013
2049
|
},
|
|
2014
2050
|
open() {
|
|
2015
2051
|
if (!launcherEnabled) return;
|
|
2016
|
-
setOpenState(true);
|
|
2052
|
+
setOpenState(true, "api");
|
|
2017
2053
|
},
|
|
2018
2054
|
close() {
|
|
2019
2055
|
if (!launcherEnabled) return;
|
|
2020
|
-
setOpenState(false);
|
|
2056
|
+
setOpenState(false, "api");
|
|
2021
2057
|
},
|
|
2022
2058
|
toggle() {
|
|
2023
2059
|
if (!launcherEnabled) return;
|
|
2024
|
-
setOpenState(!open);
|
|
2060
|
+
setOpenState(!open, "api");
|
|
2025
2061
|
},
|
|
2026
2062
|
clearChat() {
|
|
2027
2063
|
// Clear messages in session (this will trigger onMessagesChanged which re-renders)
|
|
@@ -2082,7 +2118,7 @@ export const createAgentExperience = (
|
|
|
2082
2118
|
|
|
2083
2119
|
// Auto-open widget if closed and launcher is enabled
|
|
2084
2120
|
if (!open && launcherEnabled) {
|
|
2085
|
-
setOpenState(true);
|
|
2121
|
+
setOpenState(true, "system");
|
|
2086
2122
|
}
|
|
2087
2123
|
|
|
2088
2124
|
textarea.value = message;
|
|
@@ -2098,7 +2134,7 @@ export const createAgentExperience = (
|
|
|
2098
2134
|
|
|
2099
2135
|
// Auto-open widget if closed and launcher is enabled
|
|
2100
2136
|
if (!open && launcherEnabled) {
|
|
2101
|
-
setOpenState(true);
|
|
2137
|
+
setOpenState(true, "system");
|
|
2102
2138
|
}
|
|
2103
2139
|
|
|
2104
2140
|
textarea.value = "";
|
|
@@ -2113,7 +2149,7 @@ export const createAgentExperience = (
|
|
|
2113
2149
|
|
|
2114
2150
|
// Auto-open widget if closed and launcher is enabled
|
|
2115
2151
|
if (!open && launcherEnabled) {
|
|
2116
|
-
setOpenState(true);
|
|
2152
|
+
setOpenState(true, "system");
|
|
2117
2153
|
}
|
|
2118
2154
|
|
|
2119
2155
|
voiceState.manuallyDeactivated = false;
|
|
@@ -2132,7 +2168,7 @@ export const createAgentExperience = (
|
|
|
2132
2168
|
injectTestMessage(event: AgentWidgetEvent) {
|
|
2133
2169
|
// Auto-open widget if closed and launcher is enabled
|
|
2134
2170
|
if (!open && launcherEnabled) {
|
|
2135
|
-
setOpenState(true);
|
|
2171
|
+
setOpenState(true, "system");
|
|
2136
2172
|
}
|
|
2137
2173
|
session.injectTestEvent(event);
|
|
2138
2174
|
},
|
|
@@ -2156,6 +2192,21 @@ export const createAgentExperience = (
|
|
|
2156
2192
|
off(event, handler) {
|
|
2157
2193
|
eventBus.off(event, handler);
|
|
2158
2194
|
},
|
|
2195
|
+
// State query methods
|
|
2196
|
+
isOpen(): boolean {
|
|
2197
|
+
return launcherEnabled && open;
|
|
2198
|
+
},
|
|
2199
|
+
isVoiceActive(): boolean {
|
|
2200
|
+
return voiceState.active;
|
|
2201
|
+
},
|
|
2202
|
+
getState(): AgentWidgetStateSnapshot {
|
|
2203
|
+
return {
|
|
2204
|
+
open: launcherEnabled && open,
|
|
2205
|
+
launcherEnabled,
|
|
2206
|
+
voiceActive: voiceState.active,
|
|
2207
|
+
streaming: session.isStreaming()
|
|
2208
|
+
};
|
|
2209
|
+
},
|
|
2159
2210
|
destroy() {
|
|
2160
2211
|
destroyCallbacks.forEach((cb) => cb());
|
|
2161
2212
|
wrapper.remove();
|
package/src/utils/actions.ts
CHANGED
|
@@ -140,7 +140,7 @@ export const createActionManager = (options: ActionManagerOptions) => {
|
|
|
140
140
|
}));
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
-
const process = (context: ActionManagerProcessContext): string | null => {
|
|
143
|
+
const process = (context: ActionManagerProcessContext): { text: string; persist: boolean } | null => {
|
|
144
144
|
if (
|
|
145
145
|
context.streaming ||
|
|
146
146
|
context.message.role !== "assistant" ||
|
|
@@ -202,12 +202,11 @@ export const createActionManager = (options: ActionManagerOptions) => {
|
|
|
202
202
|
|
|
203
203
|
if (!handlerResult) continue;
|
|
204
204
|
|
|
205
|
-
if (handlerResult.displayText !== undefined && handlerResult.handled) {
|
|
206
|
-
return handlerResult.displayText;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
205
|
if (handlerResult.handled) {
|
|
210
|
-
|
|
206
|
+
// persistMessage defaults to true if not specified
|
|
207
|
+
const persist = handlerResult.persistMessage !== false;
|
|
208
|
+
const displayText = handlerResult.displayText !== undefined ? handlerResult.displayText : "";
|
|
209
|
+
return { text: displayText, persist };
|
|
211
210
|
}
|
|
212
211
|
} catch (error) {
|
|
213
212
|
if (typeof console !== "undefined") {
|
|
@@ -217,7 +216,7 @@ export const createActionManager = (options: ActionManagerOptions) => {
|
|
|
217
216
|
}
|
|
218
217
|
}
|
|
219
218
|
|
|
220
|
-
return "";
|
|
219
|
+
return { text: "", persist: true };
|
|
221
220
|
};
|
|
222
221
|
|
|
223
222
|
return {
|