vanilla-agent 1.4.1 → 1.6.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 +43 -5
- package/dist/index.cjs +7 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +163 -23
- package/dist/index.d.ts +163 -23
- package/dist/index.global.js +52 -52
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/widget.css +25 -8
- package/package.json +1 -1
- package/src/client.ts +115 -21
- package/src/components/launcher.ts +1 -1
- package/src/components/message-bubble.ts +6 -1
- package/src/components/reasoning-bubble.ts +2 -0
- package/src/components/tool-bubble.ts +2 -0
- package/src/index.ts +6 -0
- package/src/runtime/init.ts +5 -33
- package/src/session.ts +15 -0
- package/src/styles/widget.css +25 -8
- package/src/types.ts +140 -1
- package/src/ui.ts +414 -20
- package/src/utils/actions.ts +228 -0
- package/src/utils/events.ts +41 -0
- package/src/utils/formatting.ts +22 -10
- package/src/utils/storage.ts +72 -0
package/dist/widget.css
CHANGED
|
@@ -797,7 +797,7 @@ form:focus-within textarea {
|
|
|
797
797
|
}
|
|
798
798
|
|
|
799
799
|
/* Markdown content overflow handling */
|
|
800
|
-
|
|
800
|
+
.vanilla-message-bubble pre {
|
|
801
801
|
overflow-x: auto;
|
|
802
802
|
max-width: 100%;
|
|
803
803
|
word-wrap: break-word;
|
|
@@ -812,7 +812,7 @@ form:focus-within textarea {
|
|
|
812
812
|
border: 1px solid #e5e7eb;
|
|
813
813
|
}
|
|
814
814
|
|
|
815
|
-
|
|
815
|
+
.vanilla-message-bubble code {
|
|
816
816
|
word-break: break-word;
|
|
817
817
|
word-wrap: break-word;
|
|
818
818
|
white-space: pre-wrap;
|
|
@@ -821,14 +821,14 @@ form:focus-within textarea {
|
|
|
821
821
|
font-size: 0.875em;
|
|
822
822
|
}
|
|
823
823
|
|
|
824
|
-
|
|
824
|
+
.vanilla-message-bubble pre code {
|
|
825
825
|
font-size: inherit;
|
|
826
826
|
background-color: transparent;
|
|
827
827
|
padding: 0;
|
|
828
828
|
border-radius: 0;
|
|
829
829
|
}
|
|
830
830
|
|
|
831
|
-
|
|
831
|
+
.vanilla-message-bubble img {
|
|
832
832
|
max-width: 100%;
|
|
833
833
|
height: auto;
|
|
834
834
|
display: block;
|
|
@@ -836,11 +836,28 @@ form:focus-within textarea {
|
|
|
836
836
|
border-radius: 0.375rem;
|
|
837
837
|
}
|
|
838
838
|
|
|
839
|
+
/* Ensure all links in chat bubbles have underlines */
|
|
840
|
+
.vanilla-message-bubble a {
|
|
841
|
+
text-decoration: underline;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.vanilla-message-bubble a:visited {
|
|
845
|
+
text-decoration: underline;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
.vanilla-message-bubble a:hover {
|
|
849
|
+
text-decoration: underline;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
.vanilla-message-bubble a:active {
|
|
853
|
+
text-decoration: underline;
|
|
854
|
+
}
|
|
855
|
+
|
|
839
856
|
/* Ensure links in user messages match the text color */
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
857
|
+
.vanilla-message-user-bubble a,
|
|
858
|
+
.vanilla-message-user-bubble a:visited,
|
|
859
|
+
.vanilla-message-user-bubble a:hover,
|
|
860
|
+
.vanilla-message-user-bubble a:active {
|
|
844
861
|
color: inherit;
|
|
845
862
|
text-decoration: underline;
|
|
846
863
|
}
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
AgentWidgetConfig,
|
|
3
|
+
AgentWidgetMessage,
|
|
4
|
+
AgentWidgetEvent,
|
|
5
|
+
AgentWidgetStreamParser,
|
|
6
|
+
AgentWidgetContextProvider,
|
|
7
|
+
AgentWidgetRequestMiddleware,
|
|
8
|
+
AgentWidgetRequestPayload
|
|
9
|
+
} from "./types";
|
|
10
|
+
import {
|
|
11
|
+
extractTextFromJson,
|
|
12
|
+
createPlainTextParser,
|
|
13
|
+
createJsonStreamParser,
|
|
14
|
+
createRegexJsonParser,
|
|
15
|
+
createXmlParser
|
|
16
|
+
} from "./utils/formatting";
|
|
3
17
|
|
|
4
18
|
type DispatchOptions = {
|
|
5
19
|
messages: AgentWidgetMessage[];
|
|
@@ -10,11 +24,30 @@ type SSEHandler = (event: AgentWidgetEvent) => void;
|
|
|
10
24
|
|
|
11
25
|
const DEFAULT_ENDPOINT = "https://api.travrse.ai/v1/dispatch";
|
|
12
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Maps parserType string to the corresponding parser factory function
|
|
29
|
+
*/
|
|
30
|
+
function getParserFromType(parserType?: "plain" | "json" | "regex-json" | "xml"): () => AgentWidgetStreamParser {
|
|
31
|
+
switch (parserType) {
|
|
32
|
+
case "json":
|
|
33
|
+
return createJsonStreamParser;
|
|
34
|
+
case "regex-json":
|
|
35
|
+
return createRegexJsonParser;
|
|
36
|
+
case "xml":
|
|
37
|
+
return createXmlParser;
|
|
38
|
+
case "plain":
|
|
39
|
+
default:
|
|
40
|
+
return createPlainTextParser;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
13
44
|
export class AgentWidgetClient {
|
|
14
45
|
private readonly apiUrl: string;
|
|
15
46
|
private readonly headers: Record<string, string>;
|
|
16
47
|
private readonly debug: boolean;
|
|
17
48
|
private readonly createStreamParser: () => AgentWidgetStreamParser;
|
|
49
|
+
private readonly contextProviders: AgentWidgetContextProvider[];
|
|
50
|
+
private readonly requestMiddleware?: AgentWidgetRequestMiddleware;
|
|
18
51
|
|
|
19
52
|
constructor(private config: AgentWidgetConfig = {}) {
|
|
20
53
|
this.apiUrl = config.apiUrl ?? DEFAULT_ENDPOINT;
|
|
@@ -23,8 +56,10 @@ export class AgentWidgetClient {
|
|
|
23
56
|
...config.headers
|
|
24
57
|
};
|
|
25
58
|
this.debug = Boolean(config.debug);
|
|
26
|
-
// Use custom stream parser
|
|
27
|
-
this.createStreamParser = config.streamParser ??
|
|
59
|
+
// Use custom stream parser if provided, otherwise use parserType, or fall back to plain text parser
|
|
60
|
+
this.createStreamParser = config.streamParser ?? getParserFromType(config.parserType);
|
|
61
|
+
this.contextProviders = config.contextProviders ?? [];
|
|
62
|
+
this.requestMiddleware = config.requestMiddleware;
|
|
28
63
|
}
|
|
29
64
|
|
|
30
65
|
public async dispatch(options: DispatchOptions, onEvent: SSEHandler) {
|
|
@@ -35,23 +70,7 @@ export class AgentWidgetClient {
|
|
|
35
70
|
|
|
36
71
|
onEvent({ type: "status", status: "connecting" });
|
|
37
72
|
|
|
38
|
-
|
|
39
|
-
// Sort by createdAt to ensure chronological order (not local sequence)
|
|
40
|
-
const body = {
|
|
41
|
-
messages: options.messages
|
|
42
|
-
.slice()
|
|
43
|
-
.sort((a, b) => {
|
|
44
|
-
const timeA = new Date(a.createdAt).getTime();
|
|
45
|
-
const timeB = new Date(b.createdAt).getTime();
|
|
46
|
-
return timeA - timeB;
|
|
47
|
-
})
|
|
48
|
-
.map((message) => ({
|
|
49
|
-
role: message.role,
|
|
50
|
-
content: message.content,
|
|
51
|
-
createdAt: message.createdAt
|
|
52
|
-
})),
|
|
53
|
-
...(this.config.flowId && { flowId: this.config.flowId })
|
|
54
|
-
};
|
|
73
|
+
const body = await this.buildPayload(options.messages);
|
|
55
74
|
|
|
56
75
|
if (this.debug) {
|
|
57
76
|
// eslint-disable-next-line no-console
|
|
@@ -81,6 +100,73 @@ export class AgentWidgetClient {
|
|
|
81
100
|
}
|
|
82
101
|
}
|
|
83
102
|
|
|
103
|
+
private async buildPayload(
|
|
104
|
+
messages: AgentWidgetMessage[]
|
|
105
|
+
): Promise<AgentWidgetRequestPayload> {
|
|
106
|
+
const normalizedMessages = messages
|
|
107
|
+
.slice()
|
|
108
|
+
.sort((a, b) => {
|
|
109
|
+
const timeA = new Date(a.createdAt).getTime();
|
|
110
|
+
const timeB = new Date(b.createdAt).getTime();
|
|
111
|
+
return timeA - timeB;
|
|
112
|
+
})
|
|
113
|
+
.map((message) => ({
|
|
114
|
+
role: message.role,
|
|
115
|
+
content: message.content,
|
|
116
|
+
createdAt: message.createdAt
|
|
117
|
+
}));
|
|
118
|
+
|
|
119
|
+
const payload: AgentWidgetRequestPayload = {
|
|
120
|
+
messages: normalizedMessages,
|
|
121
|
+
...(this.config.flowId && { flowId: this.config.flowId })
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (this.contextProviders.length) {
|
|
125
|
+
const contextAggregate: Record<string, unknown> = {};
|
|
126
|
+
await Promise.all(
|
|
127
|
+
this.contextProviders.map(async (provider) => {
|
|
128
|
+
try {
|
|
129
|
+
const result = await provider({
|
|
130
|
+
messages,
|
|
131
|
+
config: this.config
|
|
132
|
+
});
|
|
133
|
+
if (result && typeof result === "object") {
|
|
134
|
+
Object.assign(contextAggregate, result);
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (typeof console !== "undefined") {
|
|
138
|
+
// eslint-disable-next-line no-console
|
|
139
|
+
console.warn("[AgentWidget] Context provider failed:", error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
if (Object.keys(contextAggregate).length) {
|
|
146
|
+
payload.context = contextAggregate;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (this.requestMiddleware) {
|
|
151
|
+
try {
|
|
152
|
+
const result = await this.requestMiddleware({
|
|
153
|
+
payload: { ...payload },
|
|
154
|
+
config: this.config
|
|
155
|
+
});
|
|
156
|
+
if (result && typeof result === "object") {
|
|
157
|
+
return result as AgentWidgetRequestPayload;
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
if (typeof console !== "undefined") {
|
|
161
|
+
// eslint-disable-next-line no-console
|
|
162
|
+
console.error("[AgentWidget] Request middleware error:", error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return payload;
|
|
168
|
+
}
|
|
169
|
+
|
|
84
170
|
private async streamResponse(
|
|
85
171
|
body: ReadableStream<Uint8Array>,
|
|
86
172
|
onEvent: SSEHandler
|
|
@@ -554,6 +640,7 @@ export class AgentWidgetClient {
|
|
|
554
640
|
// Accumulate raw content for structured format parsing
|
|
555
641
|
const rawBuffer = rawContentBuffers.get(assistant.id) ?? "";
|
|
556
642
|
const accumulatedRaw = rawBuffer + chunk;
|
|
643
|
+
assistant.rawContent = accumulatedRaw;
|
|
557
644
|
|
|
558
645
|
// Use stream parser to parse
|
|
559
646
|
if (!streamParsers.has(assistant.id)) {
|
|
@@ -578,6 +665,7 @@ export class AgentWidgetClient {
|
|
|
578
665
|
// Clear any raw buffer/parser since we're in plain text mode
|
|
579
666
|
rawContentBuffers.delete(assistant.id);
|
|
580
667
|
streamParsers.delete(assistant.id);
|
|
668
|
+
assistant.rawContent = undefined;
|
|
581
669
|
emitMessage(assistant);
|
|
582
670
|
continue;
|
|
583
671
|
}
|
|
@@ -606,6 +694,7 @@ export class AgentWidgetClient {
|
|
|
606
694
|
currentAssistant.content += chunk;
|
|
607
695
|
rawContentBuffers.delete(currentAssistant.id);
|
|
608
696
|
streamParsers.delete(currentAssistant.id);
|
|
697
|
+
currentAssistant.rawContent = undefined;
|
|
609
698
|
emitMessage(currentAssistant);
|
|
610
699
|
}
|
|
611
700
|
}
|
|
@@ -615,6 +704,7 @@ export class AgentWidgetClient {
|
|
|
615
704
|
assistant.content += chunk;
|
|
616
705
|
rawContentBuffers.delete(assistant.id);
|
|
617
706
|
streamParsers.delete(assistant.id);
|
|
707
|
+
assistant.rawContent = undefined;
|
|
618
708
|
emitMessage(assistant);
|
|
619
709
|
});
|
|
620
710
|
} else {
|
|
@@ -633,6 +723,7 @@ export class AgentWidgetClient {
|
|
|
633
723
|
// Clear any raw buffer/parser if we were in structured format mode
|
|
634
724
|
rawContentBuffers.delete(assistant.id);
|
|
635
725
|
streamParsers.delete(assistant.id);
|
|
726
|
+
assistant.rawContent = undefined;
|
|
636
727
|
emitMessage(assistant);
|
|
637
728
|
}
|
|
638
729
|
// Otherwise wait for more chunks (incomplete structured format)
|
|
@@ -651,6 +742,7 @@ export class AgentWidgetClient {
|
|
|
651
742
|
// Check if we have raw content buffer that needs final processing
|
|
652
743
|
const rawBuffer = rawContentBuffers.get(assistant.id);
|
|
653
744
|
const contentToProcess = rawBuffer ?? ensureStringContent(finalContent);
|
|
745
|
+
assistant.rawContent = contentToProcess;
|
|
654
746
|
|
|
655
747
|
// Try to extract text from final structured content
|
|
656
748
|
const parser = streamParsers.get(assistant.id);
|
|
@@ -736,6 +828,7 @@ export class AgentWidgetClient {
|
|
|
736
828
|
// No extracted text yet - try to extract from final content
|
|
737
829
|
const rawBuffer = rawContentBuffers.get(assistant.id);
|
|
738
830
|
const contentToProcess = rawBuffer ?? ensureStringContent(finalContent);
|
|
831
|
+
assistant.rawContent = contentToProcess;
|
|
739
832
|
|
|
740
833
|
// Try fast path first
|
|
741
834
|
const extractedText = extractTextFromJson(contentToProcess);
|
|
@@ -825,6 +918,7 @@ export class AgentWidgetClient {
|
|
|
825
918
|
// Check if we have raw content buffer that needs final processing
|
|
826
919
|
const rawBuffer = rawContentBuffers.get(assistant.id);
|
|
827
920
|
const stringContent = rawBuffer ?? ensureStringContent(finalContent);
|
|
921
|
+
assistant.rawContent = stringContent;
|
|
828
922
|
// Try to extract text from structured content
|
|
829
923
|
let displayContent = ensureStringContent(finalContent);
|
|
830
924
|
const parser = streamParsers.get(assistant.id);
|
|
@@ -159,7 +159,7 @@ export const createLauncherButton = (
|
|
|
159
159
|
: positionMap["bottom-right"];
|
|
160
160
|
|
|
161
161
|
const base =
|
|
162
|
-
"tvw-fixed tvw-flex tvw-items-center tvw-gap-3 tvw-rounded-launcher tvw-bg-cw-surface tvw-py-2.5 tvw-pl-3 tvw-pr-3 tvw-shadow-lg tvw-border tvw-border-gray-200 tvw-transition hover:tvw-translate-y-[-2px] tvw-cursor-pointer";
|
|
162
|
+
"tvw-fixed tvw-flex tvw-items-center tvw-gap-3 tvw-rounded-launcher tvw-bg-cw-surface tvw-py-2.5 tvw-pl-3 tvw-pr-3 tvw-shadow-lg tvw-border tvw-border-gray-200 tvw-transition hover:tvw-translate-y-[-2px] tvw-cursor-pointer tvw-z-50";
|
|
163
163
|
|
|
164
164
|
button.className = `${base} ${positionClass}`;
|
|
165
165
|
};
|
|
@@ -5,6 +5,7 @@ export type MessageTransform = (context: {
|
|
|
5
5
|
text: string;
|
|
6
6
|
message: AgentWidgetMessage;
|
|
7
7
|
streaming: boolean;
|
|
8
|
+
raw?: string;
|
|
8
9
|
}) => string;
|
|
9
10
|
|
|
10
11
|
// Create typing indicator element
|
|
@@ -41,6 +42,7 @@ export const createStandardBubble = (
|
|
|
41
42
|
transform: MessageTransform
|
|
42
43
|
): HTMLElement => {
|
|
43
44
|
const classes = [
|
|
45
|
+
"vanilla-message-bubble",
|
|
44
46
|
"tvw-max-w-[85%]",
|
|
45
47
|
"tvw-rounded-2xl",
|
|
46
48
|
"tvw-text-sm",
|
|
@@ -50,6 +52,7 @@ export const createStandardBubble = (
|
|
|
50
52
|
|
|
51
53
|
if (message.role === "user") {
|
|
52
54
|
classes.push(
|
|
55
|
+
"vanilla-message-user-bubble",
|
|
53
56
|
"tvw-ml-auto",
|
|
54
57
|
"tvw-bg-cw-accent",
|
|
55
58
|
"tvw-text-white",
|
|
@@ -58,6 +61,7 @@ export const createStandardBubble = (
|
|
|
58
61
|
);
|
|
59
62
|
} else {
|
|
60
63
|
classes.push(
|
|
64
|
+
"vanilla-message-assistant-bubble",
|
|
61
65
|
"tvw-bg-cw-surface",
|
|
62
66
|
"tvw-border",
|
|
63
67
|
"tvw-border-cw-message-border",
|
|
@@ -74,7 +78,8 @@ export const createStandardBubble = (
|
|
|
74
78
|
contentDiv.innerHTML = transform({
|
|
75
79
|
text: message.content,
|
|
76
80
|
message,
|
|
77
|
-
streaming: Boolean(message.streaming)
|
|
81
|
+
streaming: Boolean(message.streaming),
|
|
82
|
+
raw: message.rawContent
|
|
78
83
|
});
|
|
79
84
|
bubble.appendChild(contentDiv);
|
|
80
85
|
|
package/src/index.ts
CHANGED
|
@@ -25,6 +25,12 @@ export {
|
|
|
25
25
|
type AgentWidgetSessionStatus
|
|
26
26
|
} from "./session";
|
|
27
27
|
export { AgentWidgetClient } from "./client";
|
|
28
|
+
export { createLocalStorageAdapter } from "./utils/storage";
|
|
29
|
+
export {
|
|
30
|
+
createActionManager,
|
|
31
|
+
defaultActionHandlers,
|
|
32
|
+
defaultJsonActionParser
|
|
33
|
+
} from "./utils/actions";
|
|
28
34
|
export {
|
|
29
35
|
markdownPostprocessor,
|
|
30
36
|
escapeHtml,
|
package/src/runtime/init.ts
CHANGED
|
@@ -113,46 +113,18 @@ export const initAgentWidget = (
|
|
|
113
113
|
mountStyles(host);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
let controller = createAgentExperience(mount, options.config
|
|
116
|
+
let controller = createAgentExperience(mount, options.config, {
|
|
117
|
+
debugTools: options.debugTools
|
|
118
|
+
});
|
|
117
119
|
options.onReady?.();
|
|
118
120
|
|
|
119
121
|
const handle: AgentWidgetInitHandle = {
|
|
122
|
+
...controller,
|
|
120
123
|
host,
|
|
121
|
-
update(nextConfig: AgentWidgetConfig) {
|
|
122
|
-
controller.update(nextConfig);
|
|
123
|
-
},
|
|
124
|
-
open() {
|
|
125
|
-
controller.open();
|
|
126
|
-
},
|
|
127
|
-
close() {
|
|
128
|
-
controller.close();
|
|
129
|
-
},
|
|
130
|
-
toggle() {
|
|
131
|
-
controller.toggle();
|
|
132
|
-
},
|
|
133
|
-
clearChat() {
|
|
134
|
-
controller.clearChat();
|
|
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
|
-
},
|
|
148
|
-
injectTestMessage(event: AgentWidgetEvent) {
|
|
149
|
-
controller.injectTestMessage(event);
|
|
150
|
-
},
|
|
151
124
|
destroy() {
|
|
152
125
|
controller.destroy();
|
|
153
126
|
host.remove();
|
|
154
|
-
|
|
155
|
-
if (options.windowKey && typeof window !== 'undefined') {
|
|
127
|
+
if (options.windowKey && typeof window !== "undefined") {
|
|
156
128
|
delete (window as any)[options.windowKey];
|
|
157
129
|
}
|
|
158
130
|
}
|
package/src/session.ts
CHANGED
|
@@ -133,6 +133,21 @@ export class AgentWidgetSession {
|
|
|
133
133
|
this.callbacks.onMessagesChanged([...this.messages]);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
public hydrateMessages(messages: AgentWidgetMessage[]) {
|
|
137
|
+
this.abortController?.abort();
|
|
138
|
+
this.abortController = null;
|
|
139
|
+
this.messages = this.sortMessages(
|
|
140
|
+
messages.map((message) => ({
|
|
141
|
+
...message,
|
|
142
|
+
streaming: false,
|
|
143
|
+
sequence: message.sequence ?? this.nextSequence()
|
|
144
|
+
}))
|
|
145
|
+
);
|
|
146
|
+
this.setStreaming(false);
|
|
147
|
+
this.setStatus("idle");
|
|
148
|
+
this.callbacks.onMessagesChanged([...this.messages]);
|
|
149
|
+
}
|
|
150
|
+
|
|
136
151
|
private handleEvent = (event: AgentWidgetEvent) => {
|
|
137
152
|
if (event.type === "message") {
|
|
138
153
|
this.upsertMessage(event.message);
|
package/src/styles/widget.css
CHANGED
|
@@ -797,7 +797,7 @@ form:focus-within textarea {
|
|
|
797
797
|
}
|
|
798
798
|
|
|
799
799
|
/* Markdown content overflow handling */
|
|
800
|
-
|
|
800
|
+
.vanilla-message-bubble pre {
|
|
801
801
|
overflow-x: auto;
|
|
802
802
|
max-width: 100%;
|
|
803
803
|
word-wrap: break-word;
|
|
@@ -812,7 +812,7 @@ form:focus-within textarea {
|
|
|
812
812
|
border: 1px solid #e5e7eb;
|
|
813
813
|
}
|
|
814
814
|
|
|
815
|
-
|
|
815
|
+
.vanilla-message-bubble code {
|
|
816
816
|
word-break: break-word;
|
|
817
817
|
word-wrap: break-word;
|
|
818
818
|
white-space: pre-wrap;
|
|
@@ -821,14 +821,14 @@ form:focus-within textarea {
|
|
|
821
821
|
font-size: 0.875em;
|
|
822
822
|
}
|
|
823
823
|
|
|
824
|
-
|
|
824
|
+
.vanilla-message-bubble pre code {
|
|
825
825
|
font-size: inherit;
|
|
826
826
|
background-color: transparent;
|
|
827
827
|
padding: 0;
|
|
828
828
|
border-radius: 0;
|
|
829
829
|
}
|
|
830
830
|
|
|
831
|
-
|
|
831
|
+
.vanilla-message-bubble img {
|
|
832
832
|
max-width: 100%;
|
|
833
833
|
height: auto;
|
|
834
834
|
display: block;
|
|
@@ -836,11 +836,28 @@ form:focus-within textarea {
|
|
|
836
836
|
border-radius: 0.375rem;
|
|
837
837
|
}
|
|
838
838
|
|
|
839
|
+
/* Ensure all links in chat bubbles have underlines */
|
|
840
|
+
.vanilla-message-bubble a {
|
|
841
|
+
text-decoration: underline;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.vanilla-message-bubble a:visited {
|
|
845
|
+
text-decoration: underline;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
.vanilla-message-bubble a:hover {
|
|
849
|
+
text-decoration: underline;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
.vanilla-message-bubble a:active {
|
|
853
|
+
text-decoration: underline;
|
|
854
|
+
}
|
|
855
|
+
|
|
839
856
|
/* Ensure links in user messages match the text color */
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
857
|
+
.vanilla-message-user-bubble a,
|
|
858
|
+
.vanilla-message-user-bubble a:visited,
|
|
859
|
+
.vanilla-message-user-bubble a:hover,
|
|
860
|
+
.vanilla-message-user-bubble a:active {
|
|
844
861
|
color: inherit;
|
|
845
862
|
text-decoration: underline;
|
|
846
863
|
}
|