@smooai/chat-widget 0.9.0 → 0.10.1
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 +1 -0
- package/dist/chat-widget.global.js +887 -845
- package/dist/chat-widget.global.js.map +1 -1
- package/dist/index.d.ts +17 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1314 -1272
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +15 -1
- package/src/element.ts +36 -5
- package/src/logo.ts +9 -4
- package/src/styles.ts +29 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smooai/chat-widget",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Embeddable AI chat as a framework-light web component — the Aurora Glass design, streaming replies, grounded sources, and per-brand theming. Speaks the smooth-operator WebSocket protocol.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "SmooAI",
|
package/src/config.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* `<smooth-agent-chat>` element) or programmatically (passing this object to
|
|
6
6
|
* {@link mountChatWidget} / `element.configure(...)`).
|
|
7
7
|
*/
|
|
8
|
+
import { safeHttpUrl } from './markdown.js';
|
|
9
|
+
|
|
8
10
|
export interface ChatWidgetTheme {
|
|
9
11
|
/** Foreground text color for the widget chrome. */
|
|
10
12
|
text?: string;
|
|
@@ -65,6 +67,13 @@ export interface ChatWidgetConfig {
|
|
|
65
67
|
agentId: string;
|
|
66
68
|
/** Display name for the agent (header label). Defaults to "Assistant". */
|
|
67
69
|
agentName?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Brand logo shown in the full-page header avatar tile; falls back to the
|
|
72
|
+
* Smooth icon. SECURITY: only absolute `http(s)` URLs are honored — any other
|
|
73
|
+
* scheme (`javascript:`/`data:`/…) is ignored, so a hostile config can't
|
|
74
|
+
* inject script.
|
|
75
|
+
*/
|
|
76
|
+
logoUrl?: string;
|
|
68
77
|
/** Optional display name for the user participant. */
|
|
69
78
|
userName?: string;
|
|
70
79
|
/** Optional email address for the user participant. */
|
|
@@ -127,12 +136,14 @@ export interface ChatWidgetConfig {
|
|
|
127
136
|
/** The fully-resolved theme (canonical keys only — aliases are folded in). */
|
|
128
137
|
export type ResolvedTheme = Required<Omit<ChatWidgetTheme, 'chatBubbleInbound' | 'chatBubbleInboundText' | 'chatBubbleOutbound' | 'chatBubbleOutboundText'>>;
|
|
129
138
|
|
|
130
|
-
export type ResolvedConfig = Required<Omit<ChatWidgetConfig, 'theme' | 'userName' | 'userEmail' | 'userPhone' | 'authContext'>> & {
|
|
139
|
+
export type ResolvedConfig = Required<Omit<ChatWidgetConfig, 'theme' | 'userName' | 'userEmail' | 'userPhone' | 'authContext' | 'logoUrl'>> & {
|
|
131
140
|
theme: ResolvedTheme;
|
|
132
141
|
userName?: string;
|
|
133
142
|
userEmail?: string;
|
|
134
143
|
userPhone?: string;
|
|
135
144
|
authContext?: { userId: string; signature: string; timestamp: number };
|
|
145
|
+
/** Sanitized brand logo URL (`http(s)` only) or `undefined` — see {@link ChatWidgetConfig.logoUrl}. */
|
|
146
|
+
logoUrl?: string;
|
|
136
147
|
};
|
|
137
148
|
|
|
138
149
|
/** Resolve a partial config against the built-in defaults. */
|
|
@@ -150,6 +161,9 @@ export function resolveConfig(config: ChatWidgetConfig): ResolvedConfig {
|
|
|
150
161
|
mode: config.mode ?? 'popover',
|
|
151
162
|
agentId: config.agentId,
|
|
152
163
|
agentName: config.agentName ?? 'Assistant',
|
|
164
|
+
// Only absolute http(s) URLs survive — anything else (javascript:/data:/
|
|
165
|
+
// relative) is dropped so the header can never render a hostile logo src.
|
|
166
|
+
logoUrl: safeHttpUrl(config.logoUrl) ?? undefined,
|
|
153
167
|
userName: config.userName,
|
|
154
168
|
userEmail: config.userEmail,
|
|
155
169
|
userPhone: config.userPhone,
|
package/src/element.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { AsYouType, isValidPhoneNumber, parsePhoneNumber } from 'libphonenumber-
|
|
|
18
18
|
import type { ChatWidgetConfig, ChatWidgetMode, ChatWidgetTheme } from './config.js';
|
|
19
19
|
import { needsUserInfo, resolveConfig } from './config.js';
|
|
20
20
|
import { type ChatMessage, type Citation, type ConnectionStatus, ConversationController, type IdentityRestore, type Interrupt } from './conversation.js';
|
|
21
|
-
import {
|
|
21
|
+
import { SMOOTH_ICON_SVG } from './logo.js';
|
|
22
22
|
import { cleanCitationSnippet, escapeHtml, renderMarkdown, safeHttpUrl } from './markdown.js';
|
|
23
23
|
import { buildStyles } from './styles.js';
|
|
24
24
|
|
|
@@ -49,7 +49,7 @@ function phoneToE164(value: string): string | null {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const OBSERVED = ['endpoint', 'agent-id', 'agent-name', 'placeholder', 'greeting', 'start-open', 'mode'] as const;
|
|
52
|
+
const OBSERVED = ['endpoint', 'agent-id', 'agent-name', 'logo-url', 'placeholder', 'greeting', 'start-open', 'mode'] as const;
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Inline SVG icons (static, trusted strings — never interpolated with user data).
|
|
@@ -195,6 +195,7 @@ export class SmoothAgentChatElement extends HTMLElement {
|
|
|
195
195
|
mode,
|
|
196
196
|
agentId,
|
|
197
197
|
agentName: this.overrides.agentName ?? this.getAttribute('agent-name') ?? undefined,
|
|
198
|
+
logoUrl: this.overrides.logoUrl ?? this.getAttribute('logo-url') ?? undefined,
|
|
198
199
|
userName: this.overrides.userName,
|
|
199
200
|
userEmail: this.overrides.userEmail,
|
|
200
201
|
userPhone: this.overrides.userPhone,
|
|
@@ -264,13 +265,19 @@ export class SmoothAgentChatElement extends HTMLElement {
|
|
|
264
265
|
const style = document.createElement('style');
|
|
265
266
|
style.textContent = buildStyles(resolved.theme, resolved.mode);
|
|
266
267
|
|
|
267
|
-
// Header: in full-page mode lead with the
|
|
268
|
+
// Header: in full-page mode lead with the brand logo in the avatar tile
|
|
268
269
|
// and a subtle "powered by" tag; in popover mode show a brand-colored
|
|
269
|
-
// monogram avatar + a compact close (collapse) button.
|
|
270
|
+
// monogram avatar + a compact close (collapse) button. The logo defaults
|
|
271
|
+
// to the square Smooth icon, but a host page can override it with
|
|
272
|
+
// `logoUrl` (already sanitized to http(s)-only by resolveConfig; escaped
|
|
273
|
+
// here so it can't break out of the src attribute).
|
|
270
274
|
const monogram = escapeHtml((resolved.agentName.trim().charAt(0) || 'A').toUpperCase());
|
|
275
|
+
const headerLogo = resolved.logoUrl
|
|
276
|
+
? `<img src="${escapeHtml(resolved.logoUrl)}" alt="" class="logo-img" />`
|
|
277
|
+
: SMOOTH_ICON_SVG;
|
|
271
278
|
const header = fullpage
|
|
272
279
|
? `<div class="header">
|
|
273
|
-
<div class="avatar"><span class="logo-wrap">${
|
|
280
|
+
<div class="avatar"><span class="logo-wrap">${headerLogo}</span></div>
|
|
274
281
|
<div class="meta">
|
|
275
282
|
<span class="title">${escapeHtml(resolved.agentName)}</span>
|
|
276
283
|
<span class="status"><span class="dot off"></span><span class="status-text"></span></span>
|
|
@@ -335,6 +342,7 @@ export class SmoothAgentChatElement extends HTMLElement {
|
|
|
335
342
|
</div>`;
|
|
336
343
|
|
|
337
344
|
const container = document.createElement('div');
|
|
345
|
+
container.className = 'wrap';
|
|
338
346
|
container.innerHTML = `
|
|
339
347
|
${fullpage ? '' : `<button class="launcher" part="launcher" aria-label="Open chat">${ICON.spark}</button>`}
|
|
340
348
|
<div class="panel${fullpage ? ' fullpage' : ' hidden'}" part="panel" role="${fullpage ? 'region' : 'dialog'}" aria-label="${escapeHtml(resolved.agentName)} chat">
|
|
@@ -400,6 +408,10 @@ export class SmoothAgentChatElement extends HTMLElement {
|
|
|
400
408
|
})();
|
|
401
409
|
});
|
|
402
410
|
|
|
411
|
+
// Full-page mode sizes to the host's box; fall back to the viewport only
|
|
412
|
+
// when the container gives the host no height.
|
|
413
|
+
if (fullpage) this.syncViewportFallback();
|
|
414
|
+
|
|
403
415
|
// Full-page mode connects eagerly (there's no launcher click to trigger it) —
|
|
404
416
|
// but only once any identity gate is cleared.
|
|
405
417
|
if (fullpage && !gating) void this.controller?.connect().catch(() => {});
|
|
@@ -991,6 +1003,25 @@ export class SmoothAgentChatElement extends HTMLElement {
|
|
|
991
1003
|
}
|
|
992
1004
|
}
|
|
993
1005
|
|
|
1006
|
+
/**
|
|
1007
|
+
* Full-page sizing probe: decide whether the host's container gives it a
|
|
1008
|
+
* real box. With the `.wrap` flex chain hidden, `height: 100%` of a SIZED
|
|
1009
|
+
* container still resolves (clientHeight > 0), while an auto-height parent
|
|
1010
|
+
* (e.g. mounted straight into `<body>`) collapses to ~0. Only the latter
|
|
1011
|
+
* gets `data-viewport-fallback`, whose CSS applies `min-height: 100dvh` —
|
|
1012
|
+
* so an embed inside a fixed-height box never overflows it (the composer
|
|
1013
|
+
* stays visible), and a bare full-page route still fills the viewport.
|
|
1014
|
+
*/
|
|
1015
|
+
private syncViewportFallback(): void {
|
|
1016
|
+
const wrap = this.shadowRoot?.querySelector<HTMLElement>('.wrap');
|
|
1017
|
+
if (!wrap) return;
|
|
1018
|
+
const prev = wrap.style.display;
|
|
1019
|
+
wrap.style.display = 'none';
|
|
1020
|
+
const heightless = this.clientHeight < 8;
|
|
1021
|
+
wrap.style.display = prev;
|
|
1022
|
+
this.toggleAttribute('data-viewport-fallback', heightless);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
994
1025
|
/** Cancel any in-flight reveal loop and clear its state (called on full rebuild). */
|
|
995
1026
|
private resetReveal(): void {
|
|
996
1027
|
if (this.rafId && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(this.rafId);
|
package/src/logo.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The Smooth logo, inlined as
|
|
3
|
-
*
|
|
2
|
+
* The Smooth logo + icon, inlined as SVG strings so the full-page header can
|
|
3
|
+
* render them without a separate network fetch (the IIFE bundle is
|
|
4
|
+
* self-contained).
|
|
4
5
|
*
|
|
5
|
-
* GENERATED from `assets/smooth-logo.svg` — do not
|
|
6
|
-
*
|
|
6
|
+
* GENERATED from `assets/smooth-logo.svg` / `assets/smooth-icon.svg` — do not
|
|
7
|
+
* edit by hand. Regenerate with:
|
|
8
|
+
* node -e 'const fs=require("fs");process.stdout.write(JSON.stringify(fs.readFileSync("assets/smooth-icon.svg","utf8")))'
|
|
7
9
|
*/
|
|
8
10
|
/* eslint-disable */
|
|
9
11
|
export const SMOOTH_LOGO_SVG = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg id=\"Layer_1\" data-name=\"Layer 1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 550 135\">\n <defs>\n <style>\n .cls-1 {\n fill: url(#linear-gradient-3);\n }\n\n .cls-2 {\n fill: url(#linear-gradient-2);\n }\n\n .cls-3 {\n fill: url(#linear-gradient);\n fill-rule: evenodd;\n }\n </style>\n <linearGradient id=\"linear-gradient\" x1=\"115.59\" y1=\"112.81\" x2=\"25.08\" y2=\"22.3\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\".3\" stop-color=\"#f49f0a\"/>\n <stop offset=\".79\" stop-color=\"#fb7a4d\"/>\n <stop offset=\"1\" stop-color=\"#ff6b6c\"/>\n </linearGradient>\n <linearGradient id=\"linear-gradient-2\" x1=\"360.91\" y1=\"152.01\" x2=\"202.32\" y2=\"-6.59\" xlink:href=\"#linear-gradient\"/>\n <linearGradient id=\"linear-gradient-3\" x1=\"443.91\" y1=\"30.15\" x2=\"531.36\" y2=\"117.59\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\".43\" stop-color=\"#00a6a6\"/>\n <stop offset=\"1\" stop-color=\"#1238dd\"/>\n </linearGradient>\n </defs>\n <path class=\"cls-3\" d=\"M48.28,14.96c-12.39,5.21-22.54,14.64-28.65,26.61-6.12,11.97-7.8,25.72-4.77,38.81,3.04,13.09,10.6,24.69,21.36,32.75,10.76,8.06,24.02,12.05,37.44,11.28,13.42-.77,26.13-6.26,35.9-15.5,9.76-9.24,15.95-21.63,17.46-34.99,1.51-13.36-1.74-26.82-9.19-38.01-1.07-1.61-.64-3.78.97-4.85,1.61-1.07,3.78-.64,4.85.97,8.36,12.56,12.02,27.68,10.32,42.67-1.7,15-8.64,28.91-19.61,39.28-10.96,10.37-25.24,16.54-40.31,17.4-15.07.87-29.96-3.62-42.04-12.66-12.08-9.05-20.58-22.07-23.99-36.77-3.41-14.7-1.51-30.14,5.35-43.58,6.87-13.44,18.26-24.02,32.17-29.87,13.91-5.85,29.44-6.6,43.85-2.11,1.85.57,2.88,2.54,2.3,4.38-.57,1.85-2.54,2.88-4.38,2.3-12.83-4-26.67-3.33-39.06,1.88ZM111.39,19.75c0,2.07-1.68,3.75-3.75,3.75s-3.75-1.68-3.75-3.75,1.68-3.75,3.75-3.75,3.75,1.68,3.75,3.75ZM64.64,59.93c0,1.91,2.39,2.56,7.69,3.88,3.89.97,6.6,2.18,8.15,3.63,1.53,1.45,2.29,3.53,2.29,6.25,0,3.57-1.03,6.26-3.11,8.08-2.07,1.82-5.09,2.73-9.09,2.73h-9.6c-1.97,0-3.57-1.6-3.59-3.57-.01-1.99,1.6-3.61,3.59-3.61h9.41c3.15-.12,4.79-.95,4.91-2.47,0-1.3-1.03-2.21-3.07-2.73-6.91-1.72-11.11-3.44-12.6-5.15-1.48-1.71-2.23-3.77-2.23-6.19,0-6.59,3.2-9.85,9.59-9.8h10.77c1.99,0,3.6,1.61,3.6,3.59s-1.61,3.59-3.6,3.59h-9.69c-1.83,0-3.43.06-3.43,1.77Z\"/>\n <path class=\"cls-2\" d=\"M205.52,48.44h-8.86c-.44-3.75-2.23-6.65-5.38-8.72-3.16-2.07-7.03-3.1-11.6-3.1h0c-3.35,0-6.27.54-8.78,1.62-2.49,1.09-4.44,2.59-5.84,4.48-1.39,1.89-2.08,4.05-2.08,6.46h0c0,2.01.49,3.75,1.46,5.2.97,1.44,2.22,2.63,3.74,3.58,1.53.95,3.13,1.72,4.8,2.32,1.68.6,3.22,1.09,4.62,1.46h0l7.68,2.06c1.97.52,4.17,1.23,6.6,2.14,2.43.92,4.75,2.16,6.98,3.72,2.23,1.56,4.07,3.56,5.52,6,1.45,2.44,2.18,5.43,2.18,8.98h0c0,4.08-1.07,7.77-3.2,11.08-2.12,3.29-5.22,5.91-9.3,7.86-4.08,1.95-9.02,2.92-14.82,2.92h0c-5.43,0-10.11-.87-14.06-2.62-3.95-1.75-7.05-4.19-9.3-7.32-2.25-3.12-3.53-6.75-3.84-10.88h9.46c.25,2.85,1.22,5.21,2.9,7.06,1.69,1.87,3.83,3.25,6.42,4.14,2.6.89,5.41,1.34,8.42,1.34h0c3.49,0,6.63-.57,9.4-1.72,2.79-1.13,4.99-2.73,6.62-4.8,1.63-2.05,2.44-4.46,2.44-7.22h0c0-2.51-.7-4.55-2.1-6.12-1.41-1.57-3.26-2.85-5.54-3.84-2.29-.99-4.77-1.85-7.44-2.58h0l-9.3-2.66c-5.91-1.71-10.59-4.13-14.04-7.28-3.44-3.16-5.16-7.29-5.16-12.38h0c0-4.23,1.15-7.93,3.46-11.1,2.29-3.16,5.39-5.62,9.3-7.38,3.91-1.76,8.27-2.64,13.08-2.64h0c4.88,0,9.21.87,13,2.6,3.8,1.73,6.81,4.11,9.04,7.12,2.23,3,3.4,6.41,3.52,10.22h0ZM229.16,105.18h-8.72v-56.74h8.42v8.86h.74c1.19-3.03,3.1-5.38,5.74-7.06,2.63-1.69,5.79-2.54,9.48-2.54h0c3.75,0,6.87.85,9.36,2.54,2.51,1.68,4.46,4.03,5.86,7.06h.58c1.45-2.92,3.63-5.25,6.54-7,2.91-1.73,6.39-2.6,10.46-2.6h0c5.07,0,9.21,1.58,12.44,4.74,3.23,3.17,4.84,8.09,4.84,14.76h0v37.98h-8.72v-37.98c0-4.19-1.14-7.18-3.42-8.98-2.29-1.79-4.99-2.68-8.1-2.68h0c-3.99,0-7.07,1.2-9.26,3.6-2.2,2.4-3.3,5.43-3.3,9.1h0v36.94h-8.86v-38.86c0-3.23-1.05-5.83-3.14-7.82-2.09-1.97-4.79-2.96-8.08-2.96h0c-2.27,0-4.38.6-6.34,1.8-1.96,1.21-3.53,2.88-4.72,5-1.2,2.13-1.8,4.59-1.8,7.38h0v35.46ZM333.9,106.36h0c-5.12,0-9.61-1.22-13.46-3.66-3.85-2.44-6.86-5.85-9.02-10.24-2.15-4.37-3.22-9.49-3.22-15.36h0c0-5.91,1.07-11.07,3.22-15.48,2.16-4.4,5.17-7.82,9.02-10.26,3.85-2.44,8.34-3.66,13.46-3.66h0c5.12,0,9.61,1.22,13.46,3.66,3.85,2.44,6.86,5.86,9.02,10.26,2.15,4.41,3.22,9.57,3.22,15.48h0c0,5.87-1.07,10.99-3.22,15.36-2.16,4.39-5.17,7.8-9.02,10.24-3.85,2.44-8.34,3.66-13.46,3.66ZM333.9,98.52h0c3.89,0,7.09-.99,9.6-2.98,2.52-2,4.38-4.63,5.58-7.88,1.21-3.25,1.82-6.77,1.82-10.56h0c0-3.79-.61-7.32-1.82-10.6-1.2-3.27-3.06-5.91-5.58-7.94-2.51-2.01-5.71-3.02-9.6-3.02h0c-3.89,0-7.09,1.01-9.6,3.02-2.51,2.03-4.37,4.67-5.58,7.94-1.2,3.28-1.8,6.81-1.8,10.6h0c0,3.79.6,7.31,1.8,10.56,1.21,3.25,3.07,5.88,5.58,7.88,2.51,1.99,5.71,2.98,9.6,2.98ZM395.94,106.36h0c-5.12,0-9.61-1.22-13.46-3.66-3.85-2.44-6.85-5.85-9-10.24-2.16-4.37-3.24-9.49-3.24-15.36h0c0-5.91,1.08-11.07,3.24-15.48,2.15-4.4,5.15-7.82,9-10.26,3.85-2.44,8.34-3.66,13.46-3.66h0c5.12,0,9.61,1.22,13.46,3.66,3.85,2.44,6.86,5.86,9.02,10.26,2.16,4.41,3.24,9.57,3.24,15.48h0c0,5.87-1.08,10.99-3.24,15.36-2.16,4.39-5.17,7.8-9.02,10.24-3.85,2.44-8.34,3.66-13.46,3.66ZM395.94,98.52h0c3.89,0,7.09-.99,9.6-2.98,2.52-2,4.38-4.63,5.58-7.88,1.21-3.25,1.82-6.77,1.82-10.56h0c0-3.79-.61-7.32-1.82-10.6-1.2-3.27-3.06-5.91-5.58-7.94-2.51-2.01-5.71-3.02-9.6-3.02h0c-3.88,0-7.08,1.01-9.6,3.02-2.51,2.03-4.37,4.67-5.58,7.94-1.2,3.28-1.8,6.81-1.8,10.6h0c0,3.79.6,7.31,1.8,10.56,1.21,3.25,3.07,5.88,5.58,7.88,2.52,1.99,5.72,2.98,9.6,2.98Z\"/>\n <path class=\"cls-1\" d=\"M467.88,48.02v13.28h-35.79v-13.28h35.79ZM439.68,34.38h17.89v53.42c0,1.5.36,2.62,1.08,3.36.72.74,1.88,1.1,3.49,1.1.62,0,1.48-.07,2.59-.21,1.11-.14,1.91-.27,2.38-.41l2.31,13.02c-2.02.58-3.97.97-5.84,1.18-1.88.21-3.66.31-5.33.31-6.08,0-10.7-1.43-13.84-4.28-3.15-2.85-4.72-7.01-4.72-12.48v-55.01ZM506.59,72.63v32.71h-17.89V28.95h17.53v33.53h-1.13c1.4-4.55,3.6-8.21,6.59-11,2.99-2.79,7.01-4.18,12.07-4.18,4,0,7.48.89,10.46,2.67,2.97,1.78,5.28,4.29,6.92,7.54,1.64,3.25,2.46,7.02,2.46,11.33v36.5h-17.89v-33.02c0-3.21-.82-5.73-2.46-7.56-1.64-1.83-3.93-2.74-6.87-2.74-1.92,0-3.62.42-5.1,1.26-1.49.84-2.64,2.04-3.46,3.61-.82,1.57-1.23,3.49-1.23,5.74Z\"/>\n</svg>";
|
|
12
|
+
|
|
13
|
+
/** The square Smooth icon (the stylized `th` glyph) — used as the default full-page header avatar. */
|
|
14
|
+
export const SMOOTH_ICON_SVG = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg id=\"Layer_1\" data-name=\"Layer 1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 150 150\">\n <defs>\n <style>\n .cls-1 {\n fill: url(#linear-gradient);\n }\n </style>\n <linearGradient id=\"linear-gradient\" x1=\"31.06\" y1=\"37.6\" x2=\"118.5\" y2=\"125.04\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\".43\" stop-color=\"#00a6a6\"/>\n <stop offset=\"1\" stop-color=\"#1238dd\"/>\n </linearGradient>\n </defs>\n <path class=\"cls-1\" d=\"M55.03,55.47v13.28H19.24v-13.28h35.79ZM26.83,41.83h17.89v53.42c0,1.5.36,2.62,1.08,3.36.72.74,1.88,1.1,3.49,1.1.62,0,1.48-.07,2.59-.21,1.11-.14,1.91-.27,2.38-.41l2.31,13.02c-2.02.58-3.97.97-5.84,1.18-1.88.21-3.66.31-5.33.31-6.08,0-10.7-1.43-13.84-4.28-3.15-2.85-4.72-7.01-4.72-12.48v-55.01ZM93.74,80.08v32.71h-17.89V36.39h17.53v33.53h-1.13c1.4-4.55,3.6-8.21,6.59-11,2.99-2.79,7.01-4.18,12.07-4.18,4,0,7.48.89,10.46,2.67,2.97,1.78,5.28,4.29,6.92,7.54,1.64,3.25,2.46,7.02,2.46,11.33v36.5h-17.89v-33.02c0-3.21-.82-5.73-2.46-7.56-1.64-1.83-3.93-2.74-6.87-2.74-1.92,0-3.62.42-5.1,1.26-1.49.84-2.64,2.04-3.46,3.61-.82,1.57-1.23,3.49-1.23,5.74Z\"/>\n</svg>";
|
package/src/styles.ts
CHANGED
|
@@ -44,11 +44,11 @@ export function buildStyles(theme: ResolvedTheme, mode: ChatWidgetMode = 'popove
|
|
|
44
44
|
${
|
|
45
45
|
mode === 'fullpage'
|
|
46
46
|
? `/* Full-page: fill the host's box (sized by its container, else the viewport). */
|
|
47
|
-
display:
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-direction: column;
|
|
48
49
|
position: relative;
|
|
49
50
|
width: 100%;
|
|
50
|
-
height: 100
|
|
51
|
-
min-height: 100vh;`
|
|
51
|
+
height: 100%;`
|
|
52
52
|
: `/* Popover: float in the bottom-right corner. */
|
|
53
53
|
position: fixed;
|
|
54
54
|
bottom: 24px;
|
|
@@ -58,7 +58,24 @@ export function buildStyles(theme: ResolvedTheme, mode: ChatWidgetMode = 'popove
|
|
|
58
58
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
59
59
|
-webkit-font-smoothing: antialiased;
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
${
|
|
62
|
+
mode === 'fullpage'
|
|
63
|
+
? `
|
|
64
|
+
/* Viewport fallback — the element sets this attribute only when the host's
|
|
65
|
+
container gives it no resolved height (e.g. mounted straight into an
|
|
66
|
+
auto-height <body>). A sized container always wins, so an embed inside a
|
|
67
|
+
fixed-height box never overflows it (composer stays visible). */
|
|
68
|
+
:host([data-viewport-fallback]) { min-height: 100dvh; }
|
|
69
|
+
/* The render wrapper passes the host's box down to the panel via flex. */
|
|
70
|
+
.wrap {
|
|
71
|
+
flex: 1;
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
min-height: 0;
|
|
75
|
+
}
|
|
76
|
+
`
|
|
77
|
+
: ''
|
|
78
|
+
}
|
|
62
79
|
* { box-sizing: border-box; }
|
|
63
80
|
|
|
64
81
|
/* ───────────────────────────── Launcher ───────────────────────────── */
|
|
@@ -140,11 +157,13 @@ export function buildStyles(theme: ResolvedTheme, mode: ChatWidgetMode = 'popove
|
|
|
140
157
|
pointer-events: none;
|
|
141
158
|
background: radial-gradient(120% 100% at 50% 0%, color-mix(in srgb, var(--sac-primary) 22%, transparent), transparent 70%);
|
|
142
159
|
}
|
|
143
|
-
/* Full-page: the panel becomes the whole surface
|
|
160
|
+
/* Full-page: the panel becomes the whole surface — it follows the host's box
|
|
161
|
+
(via the .wrap flex chain), never a hardcoded viewport unit. */
|
|
144
162
|
.panel.fullpage {
|
|
145
163
|
width: 100%;
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
flex: 1;
|
|
165
|
+
height: auto;
|
|
166
|
+
min-height: 0;
|
|
148
167
|
max-width: none;
|
|
149
168
|
max-height: none;
|
|
150
169
|
border: none;
|
|
@@ -176,8 +195,9 @@ export function buildStyles(theme: ResolvedTheme, mode: ChatWidgetMode = 'popove
|
|
|
176
195
|
0 1px 0 rgba(255, 255, 255, .25) inset;
|
|
177
196
|
}
|
|
178
197
|
.avatar svg { width: 22px; height: 22px; }
|
|
179
|
-
.avatar .logo-wrap { display: flex; }
|
|
198
|
+
.avatar .logo-wrap { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; }
|
|
180
199
|
.avatar .logo { height: 22px; width: auto; display: block; }
|
|
200
|
+
.avatar .logo-img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; display: block; border-radius: 9px; }
|
|
181
201
|
.meta { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
|
|
182
202
|
.title { font-weight: 650; font-size: 15.5px; letter-spacing: -.01em; line-height: 1.1; }
|
|
183
203
|
.status {
|
|
@@ -226,6 +246,7 @@ export function buildStyles(theme: ResolvedTheme, mode: ChatWidgetMode = 'popove
|
|
|
226
246
|
.panel.fullpage .header { padding: 18px 22px; }
|
|
227
247
|
.panel.fullpage .avatar { width: 44px; height: 44px; }
|
|
228
248
|
.panel.fullpage .avatar .logo { height: 26px; }
|
|
249
|
+
.panel.fullpage .avatar svg { width: 28px; height: 28px; }
|
|
229
250
|
|
|
230
251
|
/* ────────────────────────────── Messages ──────────────────────────── */
|
|
231
252
|
.messages {
|