@convai/web-sdk 1.8.0-beta.7 → 1.8.0-beta.8
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 +19 -1
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -1
- package/dist/embed/ConvaiChatElement.d.ts +77 -1
- package/dist/embed/ConvaiChatElement.d.ts.map +1 -1
- package/dist/embed/ConvaiChatElement.js +433 -54
- package/dist/embed/ConvaiChatElement.js.map +1 -1
- package/dist/embed/chat-embed-v1.js +135 -35
- package/dist/embed/chat-embed-v1.js.map +3 -3
- package/dist/react/components/rtc-widget/components/MarkdownRenderer.d.ts +7 -0
- package/dist/react/components/rtc-widget/components/MarkdownRenderer.d.ts.map +1 -1
- package/dist/react/components/rtc-widget/components/MarkdownRenderer.js +111 -17
- package/dist/react/components/rtc-widget/components/MarkdownRenderer.js.map +1 -1
- package/dist/react/components/rtc-widget/components/UserMessage.js +1 -1
- package/dist/react/components/rtc-widget/components/UserMessage.js.map +1 -1
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +7 -0
- package/dist/react/index.js.map +1 -1
- package/dist/utils/inlineMarkdown.d.ts +22 -0
- package/dist/utils/inlineMarkdown.d.ts.map +1 -1
- package/dist/utils/inlineMarkdown.js +84 -7
- package/dist/utils/inlineMarkdown.js.map +1 -1
- package/dist/vanilla/ConvaiWidget.d.ts.map +1 -1
- package/dist/vanilla/ConvaiWidget.js +137 -11
- package/dist/vanilla/ConvaiWidget.js.map +1 -1
- package/dist/vanilla/index.d.ts +2 -0
- package/dist/vanilla/index.d.ts.map +1 -1
- package/dist/vanilla/index.js +2 -0
- package/dist/vanilla/index.js.map +1 -1
- package/dist/vanilla/styles.d.ts.map +1 -1
- package/dist/vanilla/styles.js +12 -0
- package/dist/vanilla/styles.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +32 -10
|
@@ -7,7 +7,7 @@ import { aeroTheme, injectGlobalStyles, injectKeyframes, } from "./styles.js";
|
|
|
7
7
|
import { Icons } from "./icons.js";
|
|
8
8
|
import { WidgetEventSubscriptions } from "./WidgetEventSubscriptions.js";
|
|
9
9
|
import { ConvaiClient } from "../core/ConvaiClient.js";
|
|
10
|
-
import { parseInlineMarkdown } from "../utils/inlineMarkdown.js";
|
|
10
|
+
import { parseInlineMarkdown, compactUrl } from "../utils/inlineMarkdown.js";
|
|
11
11
|
import { freshPublicationGrantOptions } from "../core/publishedChat.js";
|
|
12
12
|
/**
|
|
13
13
|
* Positioning declarations for the widget root, keyed by `placement`. The
|
|
@@ -182,6 +182,10 @@ export function createConvaiWidget(container, options) {
|
|
|
182
182
|
: "bottom-right";
|
|
183
183
|
// Inject global styles
|
|
184
184
|
injectGlobalStyles(styleTarget);
|
|
185
|
+
// Keyframes were injected lazily by the connecting overlay and the settings
|
|
186
|
+
// tray, but a link-preview skeleton can render before either exists — a
|
|
187
|
+
// replayed transcript paints message bubbles immediately. Idempotent.
|
|
188
|
+
injectKeyframes(styleTarget);
|
|
185
189
|
// State
|
|
186
190
|
let isOpen = false;
|
|
187
191
|
let isSettingsOpen = false;
|
|
@@ -2071,7 +2075,11 @@ export function createConvaiWidget(container, options) {
|
|
|
2071
2075
|
};
|
|
2072
2076
|
// Microphone button removed - only controlled via voice mode now
|
|
2073
2077
|
// Helper for Markdown - matches React MarkdownRenderer.tsx
|
|
2074
|
-
|
|
2078
|
+
// `media` renders inline image/video previews under a media link. Bot messages
|
|
2079
|
+
// want them; the user's own bubble does not — you already know what you typed,
|
|
2080
|
+
// and a preview there would let a URL a user pasted embed remote media into
|
|
2081
|
+
// the transcript. Links stay clickable either way.
|
|
2082
|
+
const renderMarkdown = (text, container, { media = true } = {}) => {
|
|
2075
2083
|
if (!text)
|
|
2076
2084
|
return;
|
|
2077
2085
|
// Handle both actual newlines and \n escape sequences
|
|
@@ -2080,19 +2088,106 @@ export function createConvaiWidget(container, options) {
|
|
|
2080
2088
|
lines.forEach((line, lineIndex) => {
|
|
2081
2089
|
// Process markdown within each line
|
|
2082
2090
|
// Bold, italic, links, images and bare URLs — see utils/inlineMarkdown.
|
|
2091
|
+
// `cdn.example.com/a/b/c.png` -> `cdn.example.com · c.png`
|
|
2092
|
+
const describeUrl = (url) => {
|
|
2093
|
+
try {
|
|
2094
|
+
const { host, pathname } = new URL(url);
|
|
2095
|
+
const file = pathname.split("/").filter(Boolean).pop();
|
|
2096
|
+
return file ? `${host} · ${file}` : host;
|
|
2097
|
+
}
|
|
2098
|
+
catch {
|
|
2099
|
+
return url;
|
|
2100
|
+
}
|
|
2101
|
+
};
|
|
2102
|
+
/**
|
|
2103
|
+
* A preview card that reserves its space before the media arrives, so the
|
|
2104
|
+
* bubble does not jump when an image decodes, and collapses to one muted
|
|
2105
|
+
* line if the URL does not resolve — a character can easily invent one,
|
|
2106
|
+
* and an empty player in the transcript reads as a broken widget.
|
|
2107
|
+
*
|
|
2108
|
+
* Chrome is derived from the inherited text colour: the widget is embedded
|
|
2109
|
+
* in someone else's page, on a background this code cannot know.
|
|
2110
|
+
*/
|
|
2083
2111
|
const mediaPreview = (src, alt, media) => {
|
|
2112
|
+
const card = document.createElement("span");
|
|
2113
|
+
card.setAttribute("part", "message-media-card");
|
|
2114
|
+
card.style.cssText = `
|
|
2115
|
+
display: block;
|
|
2116
|
+
position: relative;
|
|
2117
|
+
margin-top: 6px;
|
|
2118
|
+
max-width: 320px;
|
|
2119
|
+
aspect-ratio: 16 / 10;
|
|
2120
|
+
border-radius: 8px;
|
|
2121
|
+
overflow: hidden;
|
|
2122
|
+
background: color-mix(in srgb, currentColor 6%, transparent);
|
|
2123
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 10%, transparent);
|
|
2124
|
+
`;
|
|
2084
2125
|
const el = media === "image"
|
|
2085
2126
|
? Object.assign(document.createElement("img"), { src, alt, loading: "lazy" })
|
|
2086
2127
|
: Object.assign(document.createElement("video"), { src, controls: true, preload: "metadata" });
|
|
2087
2128
|
el.setAttribute("part", "message-media");
|
|
2088
2129
|
el.style.cssText = `
|
|
2089
2130
|
display: block;
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2131
|
+
width: 100%;
|
|
2132
|
+
height: 100%;
|
|
2133
|
+
object-fit: ${media === "image" ? "cover" : "contain"};
|
|
2093
2134
|
border-radius: 8px;
|
|
2135
|
+
opacity: 0;
|
|
2136
|
+
transition: opacity .18s ease;
|
|
2137
|
+
`;
|
|
2138
|
+
const skeleton = document.createElement("span");
|
|
2139
|
+
skeleton.setAttribute("part", "message-media-skeleton");
|
|
2140
|
+
skeleton.setAttribute("aria-hidden", "true");
|
|
2141
|
+
skeleton.dataset.convaiSkeleton = "";
|
|
2142
|
+
skeleton.style.cssText = `
|
|
2143
|
+
position: absolute;
|
|
2144
|
+
inset: 0;
|
|
2145
|
+
border-radius: 8px;
|
|
2146
|
+
background: color-mix(in srgb, currentColor 8%, transparent);
|
|
2147
|
+
background-image: linear-gradient(90deg, transparent, color-mix(in srgb, currentColor 12%, transparent), transparent);
|
|
2148
|
+
background-size: 200% 100%;
|
|
2149
|
+
animation: convai-preview-shimmer 1.4s ease-in-out infinite;
|
|
2150
|
+
`;
|
|
2151
|
+
const caption = document.createElement("span");
|
|
2152
|
+
caption.setAttribute("part", "message-media-caption");
|
|
2153
|
+
caption.textContent = describeUrl(src);
|
|
2154
|
+
caption.style.cssText = `
|
|
2155
|
+
display: block;
|
|
2156
|
+
margin-top: 4px;
|
|
2157
|
+
font-size: 11px;
|
|
2158
|
+
opacity: .7;
|
|
2159
|
+
white-space: nowrap;
|
|
2160
|
+
overflow: hidden;
|
|
2161
|
+
text-overflow: ellipsis;
|
|
2094
2162
|
`;
|
|
2095
|
-
|
|
2163
|
+
const settled = () => {
|
|
2164
|
+
el.style.opacity = "1";
|
|
2165
|
+
skeleton.remove();
|
|
2166
|
+
};
|
|
2167
|
+
el.addEventListener(media === "image" ? "load" : "loadedmetadata", settled);
|
|
2168
|
+
el.addEventListener("error", () => {
|
|
2169
|
+
const dead = document.createElement("span");
|
|
2170
|
+
dead.setAttribute("part", "message-media-unavailable");
|
|
2171
|
+
dead.textContent = `Preview unavailable — ${describeUrl(src)}`;
|
|
2172
|
+
dead.style.cssText = `
|
|
2173
|
+
display: block;
|
|
2174
|
+
margin-top: 6px;
|
|
2175
|
+
font-size: 11px;
|
|
2176
|
+
opacity: .55;
|
|
2177
|
+
font-style: italic;
|
|
2178
|
+
white-space: nowrap;
|
|
2179
|
+
overflow: hidden;
|
|
2180
|
+
text-overflow: ellipsis;
|
|
2181
|
+
`;
|
|
2182
|
+
card.replaceWith(dead);
|
|
2183
|
+
caption.remove();
|
|
2184
|
+
});
|
|
2185
|
+
card.appendChild(el);
|
|
2186
|
+
card.appendChild(skeleton);
|
|
2187
|
+
const frag = document.createDocumentFragment();
|
|
2188
|
+
frag.appendChild(card);
|
|
2189
|
+
frag.appendChild(caption);
|
|
2190
|
+
return frag;
|
|
2096
2191
|
};
|
|
2097
2192
|
const processLine = (text) => {
|
|
2098
2193
|
const parts = [];
|
|
@@ -2119,7 +2214,7 @@ export function createConvaiWidget(container, options) {
|
|
|
2119
2214
|
case "link": {
|
|
2120
2215
|
const a = document.createElement("a");
|
|
2121
2216
|
a.href = node.href;
|
|
2122
|
-
a.textContent = node.label;
|
|
2217
|
+
a.textContent = node.label === node.href ? compactUrl(node.label) : node.label;
|
|
2123
2218
|
a.target = "_blank";
|
|
2124
2219
|
a.rel = "noopener noreferrer";
|
|
2125
2220
|
a.setAttribute("part", "message-link");
|
|
@@ -2129,13 +2224,44 @@ export function createConvaiWidget(container, options) {
|
|
|
2129
2224
|
word-break: break-all;
|
|
2130
2225
|
`;
|
|
2131
2226
|
parts.push(a);
|
|
2132
|
-
if (node.media)
|
|
2227
|
+
if (media && node.media)
|
|
2133
2228
|
parts.push(mediaPreview(node.href, node.label, node.media));
|
|
2134
2229
|
break;
|
|
2135
2230
|
}
|
|
2136
|
-
case "
|
|
2137
|
-
|
|
2231
|
+
case "blocked": {
|
|
2232
|
+
const blocked = document.createElement("span");
|
|
2233
|
+
blocked.setAttribute("part", "message-link-blocked");
|
|
2234
|
+
blocked.textContent = node.label;
|
|
2235
|
+
blocked.title = `Link blocked: ${node.href}`;
|
|
2236
|
+
blocked.style.cssText = `
|
|
2237
|
+
opacity: .75;
|
|
2238
|
+
text-decoration: underline dotted;
|
|
2239
|
+
cursor: help;
|
|
2240
|
+
`;
|
|
2241
|
+
parts.push(blocked);
|
|
2242
|
+
break;
|
|
2243
|
+
}
|
|
2244
|
+
case "image": {
|
|
2245
|
+
if (media) {
|
|
2246
|
+
parts.push(mediaPreview(node.src, node.alt, "image"));
|
|
2247
|
+
break;
|
|
2248
|
+
}
|
|
2249
|
+
// With previews off, an image still has to say what it pointed at,
|
|
2250
|
+
// so it degrades to the same anchor a plain link would render.
|
|
2251
|
+
const imgLink = document.createElement("a");
|
|
2252
|
+
imgLink.href = node.src;
|
|
2253
|
+
imgLink.textContent = node.alt || node.src;
|
|
2254
|
+
imgLink.target = "_blank";
|
|
2255
|
+
imgLink.rel = "noopener noreferrer";
|
|
2256
|
+
imgLink.setAttribute("part", "message-link");
|
|
2257
|
+
imgLink.style.cssText = `
|
|
2258
|
+
color: #10b981;
|
|
2259
|
+
text-decoration: underline;
|
|
2260
|
+
word-break: break-all;
|
|
2261
|
+
`;
|
|
2262
|
+
parts.push(imgLink);
|
|
2138
2263
|
break;
|
|
2264
|
+
}
|
|
2139
2265
|
default:
|
|
2140
2266
|
parts.push(document.createTextNode(node.text));
|
|
2141
2267
|
}
|
|
@@ -2296,7 +2422,7 @@ export function createConvaiWidget(container, options) {
|
|
|
2296
2422
|
white-space: pre-wrap;
|
|
2297
2423
|
word-wrap: break-word;
|
|
2298
2424
|
`;
|
|
2299
|
-
renderMarkdown(msg.text, contentDiv);
|
|
2425
|
+
renderMarkdown(msg.text, contentDiv, { media: !msg.isUser });
|
|
2300
2426
|
bubble.appendChild(contentDiv);
|
|
2301
2427
|
messageWrapper.appendChild(bubble);
|
|
2302
2428
|
// Timestamp - matches React exactly
|