vanilla-agent 1.0.0 → 1.2.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 +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.global.js +28 -28
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/widget.css +40 -0
- package/package.json +1 -1
- package/src/components/message-bubble.ts +40 -1
- package/src/runtime/init.ts +24 -1
- package/src/styles/widget.css +40 -0
- package/src/types.ts +1 -0
- package/src/ui.ts +64 -34
package/dist/widget.css
CHANGED
|
@@ -783,3 +783,43 @@ form:focus-within textarea {
|
|
|
783
783
|
.tvw-voice-recording svg {
|
|
784
784
|
animation: voice-recording-pulse 1.5s ease-in-out infinite;
|
|
785
785
|
}
|
|
786
|
+
|
|
787
|
+
/* Markdown content overflow handling */
|
|
788
|
+
#vanilla-agent-root pre {
|
|
789
|
+
overflow-x: auto;
|
|
790
|
+
max-width: 100%;
|
|
791
|
+
word-wrap: break-word;
|
|
792
|
+
word-break: break-word;
|
|
793
|
+
white-space: pre-wrap;
|
|
794
|
+
background-color: #f3f4f6;
|
|
795
|
+
padding: 0.75rem;
|
|
796
|
+
border-radius: 0.375rem;
|
|
797
|
+
margin: 0.5rem 0;
|
|
798
|
+
font-size: 0.875rem;
|
|
799
|
+
line-height: 1.5;
|
|
800
|
+
border: 1px solid #e5e7eb;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
#vanilla-agent-root code {
|
|
804
|
+
word-break: break-word;
|
|
805
|
+
word-wrap: break-word;
|
|
806
|
+
white-space: pre-wrap;
|
|
807
|
+
overflow-wrap: break-word;
|
|
808
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
|
|
809
|
+
font-size: 0.875em;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
#vanilla-agent-root pre code {
|
|
813
|
+
font-size: inherit;
|
|
814
|
+
background-color: transparent;
|
|
815
|
+
padding: 0;
|
|
816
|
+
border-radius: 0;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
#vanilla-agent-root img {
|
|
820
|
+
max-width: 100%;
|
|
821
|
+
height: auto;
|
|
822
|
+
display: block;
|
|
823
|
+
margin: 0.5rem 0;
|
|
824
|
+
border-radius: 0.375rem;
|
|
825
|
+
}
|
package/package.json
CHANGED
|
@@ -7,6 +7,35 @@ export type MessageTransform = (context: {
|
|
|
7
7
|
streaming: boolean;
|
|
8
8
|
}) => string;
|
|
9
9
|
|
|
10
|
+
// Create typing indicator element
|
|
11
|
+
export const createTypingIndicator = (): HTMLElement => {
|
|
12
|
+
const container = document.createElement("div");
|
|
13
|
+
container.className = "tvw-flex tvw-items-center tvw-space-x-1 tvw-h-5 tvw-mt-2";
|
|
14
|
+
|
|
15
|
+
const dot1 = document.createElement("div");
|
|
16
|
+
dot1.className = "tvw-bg-cw-primary tvw-animate-typing tvw-rounded-full tvw-h-1.5 tvw-w-1.5";
|
|
17
|
+
dot1.style.animationDelay = "0ms";
|
|
18
|
+
|
|
19
|
+
const dot2 = document.createElement("div");
|
|
20
|
+
dot2.className = "tvw-bg-cw-primary tvw-animate-typing tvw-rounded-full tvw-h-1.5 tvw-w-1.5";
|
|
21
|
+
dot2.style.animationDelay = "250ms";
|
|
22
|
+
|
|
23
|
+
const dot3 = document.createElement("div");
|
|
24
|
+
dot3.className = "tvw-bg-cw-primary tvw-animate-typing tvw-rounded-full tvw-h-1.5 tvw-w-1.5";
|
|
25
|
+
dot3.style.animationDelay = "500ms";
|
|
26
|
+
|
|
27
|
+
const srOnly = document.createElement("span");
|
|
28
|
+
srOnly.className = "tvw-sr-only";
|
|
29
|
+
srOnly.textContent = "Loading";
|
|
30
|
+
|
|
31
|
+
container.appendChild(dot1);
|
|
32
|
+
container.appendChild(dot2);
|
|
33
|
+
container.appendChild(dot3);
|
|
34
|
+
container.appendChild(srOnly);
|
|
35
|
+
|
|
36
|
+
return container;
|
|
37
|
+
};
|
|
38
|
+
|
|
10
39
|
export const createStandardBubble = (
|
|
11
40
|
message: AgentWidgetMessage,
|
|
12
41
|
transform: MessageTransform
|
|
@@ -39,11 +68,21 @@ export const createStandardBubble = (
|
|
|
39
68
|
}
|
|
40
69
|
|
|
41
70
|
const bubble = createElement("div", classes.join(" "));
|
|
42
|
-
|
|
71
|
+
|
|
72
|
+
// Add message content
|
|
73
|
+
const contentDiv = document.createElement("div");
|
|
74
|
+
contentDiv.innerHTML = transform({
|
|
43
75
|
text: message.content,
|
|
44
76
|
message,
|
|
45
77
|
streaming: Boolean(message.streaming)
|
|
46
78
|
});
|
|
79
|
+
bubble.appendChild(contentDiv);
|
|
80
|
+
|
|
81
|
+
// Add typing indicator if this is a streaming assistant message with content
|
|
82
|
+
if (message.streaming && message.role === "assistant" && message.content && message.content.trim()) {
|
|
83
|
+
const typingIndicator = createTypingIndicator();
|
|
84
|
+
bubble.appendChild(typingIndicator);
|
|
85
|
+
}
|
|
47
86
|
|
|
48
87
|
return bubble;
|
|
49
88
|
};
|
package/src/runtime/init.ts
CHANGED
|
@@ -116,7 +116,7 @@ export const initAgentWidget = (
|
|
|
116
116
|
let controller = createAgentExperience(mount, options.config);
|
|
117
117
|
options.onReady?.();
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
const handle: AgentWidgetInitHandle = {
|
|
120
120
|
host,
|
|
121
121
|
update(nextConfig: AgentWidgetConfig) {
|
|
122
122
|
controller.update(nextConfig);
|
|
@@ -133,9 +133,32 @@ export const initAgentWidget = (
|
|
|
133
133
|
clearChat() {
|
|
134
134
|
controller.clearChat();
|
|
135
135
|
},
|
|
136
|
+
setMessage(message: string) {
|
|
137
|
+
return controller.setMessage(message);
|
|
138
|
+
},
|
|
139
|
+
submitMessage(message?: string) {
|
|
140
|
+
return controller.submitMessage(message);
|
|
141
|
+
},
|
|
142
|
+
startVoiceRecognition() {
|
|
143
|
+
return controller.startVoiceRecognition();
|
|
144
|
+
},
|
|
145
|
+
stopVoiceRecognition() {
|
|
146
|
+
return controller.stopVoiceRecognition();
|
|
147
|
+
},
|
|
136
148
|
destroy() {
|
|
137
149
|
controller.destroy();
|
|
138
150
|
host.remove();
|
|
151
|
+
// Clean up window reference if it was set
|
|
152
|
+
if (options.windowKey && typeof window !== 'undefined') {
|
|
153
|
+
delete (window as any)[options.windowKey];
|
|
154
|
+
}
|
|
139
155
|
}
|
|
140
156
|
};
|
|
157
|
+
|
|
158
|
+
// Store on window if windowKey is provided
|
|
159
|
+
if (options.windowKey && typeof window !== 'undefined') {
|
|
160
|
+
(window as any)[options.windowKey] = handle;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return handle;
|
|
141
164
|
};
|
package/src/styles/widget.css
CHANGED
|
@@ -783,3 +783,43 @@ form:focus-within textarea {
|
|
|
783
783
|
.tvw-voice-recording svg {
|
|
784
784
|
animation: voice-recording-pulse 1.5s ease-in-out infinite;
|
|
785
785
|
}
|
|
786
|
+
|
|
787
|
+
/* Markdown content overflow handling */
|
|
788
|
+
#vanilla-agent-root pre {
|
|
789
|
+
overflow-x: auto;
|
|
790
|
+
max-width: 100%;
|
|
791
|
+
word-wrap: break-word;
|
|
792
|
+
word-break: break-word;
|
|
793
|
+
white-space: pre-wrap;
|
|
794
|
+
background-color: #f3f4f6;
|
|
795
|
+
padding: 0.75rem;
|
|
796
|
+
border-radius: 0.375rem;
|
|
797
|
+
margin: 0.5rem 0;
|
|
798
|
+
font-size: 0.875rem;
|
|
799
|
+
line-height: 1.5;
|
|
800
|
+
border: 1px solid #e5e7eb;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
#vanilla-agent-root code {
|
|
804
|
+
word-break: break-word;
|
|
805
|
+
word-wrap: break-word;
|
|
806
|
+
white-space: pre-wrap;
|
|
807
|
+
overflow-wrap: break-word;
|
|
808
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
|
|
809
|
+
font-size: 0.875em;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
#vanilla-agent-root pre code {
|
|
813
|
+
font-size: inherit;
|
|
814
|
+
background-color: transparent;
|
|
815
|
+
padding: 0;
|
|
816
|
+
border-radius: 0;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
#vanilla-agent-root img {
|
|
820
|
+
max-width: 100%;
|
|
821
|
+
height: auto;
|
|
822
|
+
display: block;
|
|
823
|
+
margin: 0.5rem 0;
|
|
824
|
+
border-radius: 0.375rem;
|
|
825
|
+
}
|
package/src/types.ts
CHANGED
package/src/ui.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { statusCopy } from "./utils/constants";
|
|
|
8
8
|
import { createLauncherButton } from "./components/launcher";
|
|
9
9
|
import { createWrapper, buildPanel } from "./components/panel";
|
|
10
10
|
import { MessageTransform } from "./components/message-bubble";
|
|
11
|
-
import { createStandardBubble } from "./components/message-bubble";
|
|
11
|
+
import { createStandardBubble, createTypingIndicator } from "./components/message-bubble";
|
|
12
12
|
import { createReasoningBubble } from "./components/reasoning-bubble";
|
|
13
13
|
import { createToolBubble } from "./components/tool-bubble";
|
|
14
14
|
import { createSuggestions } from "./components/suggestions";
|
|
@@ -23,6 +23,10 @@ type Controller = {
|
|
|
23
23
|
close: () => void;
|
|
24
24
|
toggle: () => void;
|
|
25
25
|
clearChat: () => void;
|
|
26
|
+
setMessage: (message: string) => boolean;
|
|
27
|
+
submitMessage: (message?: string) => boolean;
|
|
28
|
+
startVoiceRecognition: () => boolean;
|
|
29
|
+
stopVoiceRecognition: () => boolean;
|
|
26
30
|
};
|
|
27
31
|
|
|
28
32
|
const buildPostprocessor = (cfg?: AgentWidgetConfig): MessageTransform => {
|
|
@@ -148,34 +152,6 @@ export const createAgentExperience = (
|
|
|
148
152
|
});
|
|
149
153
|
};
|
|
150
154
|
|
|
151
|
-
// Create typing indicator element
|
|
152
|
-
const createTypingIndicator = (): HTMLElement => {
|
|
153
|
-
const container = document.createElement("div");
|
|
154
|
-
container.className = "tvw-flex tvw-items-center tvw-space-x-1 tvw-h-5";
|
|
155
|
-
|
|
156
|
-
const dot1 = document.createElement("div");
|
|
157
|
-
dot1.className = "tvw-bg-cw-primary tvw-animate-typing tvw-rounded-full tvw-h-1.5 tvw-w-1.5";
|
|
158
|
-
dot1.style.animationDelay = "0ms";
|
|
159
|
-
|
|
160
|
-
const dot2 = document.createElement("div");
|
|
161
|
-
dot2.className = "tvw-bg-cw-primary tvw-animate-typing tvw-rounded-full tvw-h-1.5 tvw-w-1.5";
|
|
162
|
-
dot2.style.animationDelay = "250ms";
|
|
163
|
-
|
|
164
|
-
const dot3 = document.createElement("div");
|
|
165
|
-
dot3.className = "tvw-bg-cw-primary tvw-animate-typing tvw-rounded-full tvw-h-1.5 tvw-w-1.5";
|
|
166
|
-
dot3.style.animationDelay = "500ms";
|
|
167
|
-
|
|
168
|
-
const srOnly = document.createElement("span");
|
|
169
|
-
srOnly.className = "tvw-sr-only";
|
|
170
|
-
srOnly.textContent = "Loading";
|
|
171
|
-
|
|
172
|
-
container.appendChild(dot1);
|
|
173
|
-
container.appendChild(dot2);
|
|
174
|
-
container.appendChild(dot3);
|
|
175
|
-
container.appendChild(srOnly);
|
|
176
|
-
|
|
177
|
-
return container;
|
|
178
|
-
};
|
|
179
155
|
|
|
180
156
|
// Message rendering with plugin support
|
|
181
157
|
const renderMessagesWithPlugins = (
|
|
@@ -258,10 +234,15 @@ export const createAgentExperience = (
|
|
|
258
234
|
fragment.appendChild(wrapper);
|
|
259
235
|
});
|
|
260
236
|
|
|
261
|
-
// Add typing indicator if streaming
|
|
262
|
-
|
|
237
|
+
// Add standalone typing indicator only if streaming but no assistant message is streaming yet
|
|
238
|
+
// (This shows while waiting for the stream to start)
|
|
239
|
+
const hasStreamingAssistantMessage = messages.some(
|
|
240
|
+
(msg) => msg.role === "assistant" && msg.streaming && msg.content && msg.content.trim()
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
if (isStreaming && messages.some((msg) => msg.role === "user") && !hasStreamingAssistantMessage) {
|
|
263
244
|
const typingIndicator = createTypingIndicator();
|
|
264
|
-
|
|
245
|
+
|
|
265
246
|
// Create a bubble wrapper for the typing indicator (similar to assistant messages)
|
|
266
247
|
const typingBubble = document.createElement("div");
|
|
267
248
|
typingBubble.className = [
|
|
@@ -277,9 +258,9 @@ export const createAgentExperience = (
|
|
|
277
258
|
"tvw-px-5",
|
|
278
259
|
"tvw-py-3"
|
|
279
260
|
].join(" ");
|
|
280
|
-
|
|
261
|
+
|
|
281
262
|
typingBubble.appendChild(typingIndicator);
|
|
282
|
-
|
|
263
|
+
|
|
283
264
|
const typingWrapper = document.createElement("div");
|
|
284
265
|
typingWrapper.className = "tvw-flex";
|
|
285
266
|
typingWrapper.appendChild(typingBubble);
|
|
@@ -1619,6 +1600,55 @@ export const createAgentExperience = (
|
|
|
1619
1600
|
});
|
|
1620
1601
|
window.dispatchEvent(clearEvent);
|
|
1621
1602
|
},
|
|
1603
|
+
setMessage(message: string): boolean {
|
|
1604
|
+
if (!textarea) return false;
|
|
1605
|
+
if (session.isStreaming()) return false;
|
|
1606
|
+
|
|
1607
|
+
// Auto-open widget if closed and launcher is enabled
|
|
1608
|
+
if (!open && launcherEnabled) {
|
|
1609
|
+
setOpenState(true);
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
textarea.value = message;
|
|
1613
|
+
// Trigger input event for any listeners
|
|
1614
|
+
textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
1615
|
+
return true;
|
|
1616
|
+
},
|
|
1617
|
+
submitMessage(message?: string): boolean {
|
|
1618
|
+
if (session.isStreaming()) return false;
|
|
1619
|
+
|
|
1620
|
+
const valueToSubmit = message?.trim() || textarea.value.trim();
|
|
1621
|
+
if (!valueToSubmit) return false;
|
|
1622
|
+
|
|
1623
|
+
// Auto-open widget if closed and launcher is enabled
|
|
1624
|
+
if (!open && launcherEnabled) {
|
|
1625
|
+
setOpenState(true);
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
textarea.value = "";
|
|
1629
|
+
session.sendMessage(valueToSubmit);
|
|
1630
|
+
return true;
|
|
1631
|
+
},
|
|
1632
|
+
startVoiceRecognition(): boolean {
|
|
1633
|
+
if (isRecording || session.isStreaming()) return false;
|
|
1634
|
+
|
|
1635
|
+
const SpeechRecognitionClass = getSpeechRecognitionClass();
|
|
1636
|
+
if (!SpeechRecognitionClass) return false;
|
|
1637
|
+
|
|
1638
|
+
// Auto-open widget if closed and launcher is enabled
|
|
1639
|
+
if (!open && launcherEnabled) {
|
|
1640
|
+
setOpenState(true);
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
startVoiceRecognition();
|
|
1644
|
+
return true;
|
|
1645
|
+
},
|
|
1646
|
+
stopVoiceRecognition(): boolean {
|
|
1647
|
+
if (!isRecording) return false;
|
|
1648
|
+
|
|
1649
|
+
stopVoiceRecognition();
|
|
1650
|
+
return true;
|
|
1651
|
+
},
|
|
1622
1652
|
destroy() {
|
|
1623
1653
|
destroyCallbacks.forEach((cb) => cb());
|
|
1624
1654
|
wrapper.remove();
|