@smooai/chat-widget 0.10.1 → 0.10.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smooai/chat-widget",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
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
@@ -95,6 +95,12 @@ export interface ChatWidgetConfig {
95
95
  connectionErrorMessage?: string;
96
96
  /** Start the panel open instead of collapsed to the launcher. */
97
97
  startOpen?: boolean;
98
+ /**
99
+ * Hide the "powered by smooth-operator" branding in the header tag and the
100
+ * composer footer. Defaults to `false` (branding shown). The `hide-branding`
101
+ * HTML attribute maps to this.
102
+ */
103
+ hideBranding?: boolean;
98
104
  /**
99
105
  * Suggested starter prompts shown as clickable chips before the first message.
100
106
  * Clicking one sends it. Capped at 5 for layout.
@@ -172,6 +178,7 @@ export function resolveConfig(config: ChatWidgetConfig): ResolvedConfig {
172
178
  greeting: config.greeting ?? 'Hi! How can I help you today?',
173
179
  connectionErrorMessage: config.connectionErrorMessage ?? "We couldn't reach the chat. Please try again in a moment.",
174
180
  startOpen: config.startOpen ?? false,
181
+ hideBranding: config.hideBranding ?? false,
175
182
  examplePrompts: (config.examplePrompts ?? []).filter((p) => p.trim().length > 0).slice(0, 5),
176
183
  requireName: config.requireName ?? false,
177
184
  requireEmail: config.requireEmail ?? false,
package/src/element.ts CHANGED
@@ -49,7 +49,10 @@ function phoneToE164(value: string): string | null {
49
49
  }
50
50
  }
51
51
 
52
- const OBSERVED = ['endpoint', 'agent-id', 'agent-name', 'logo-url', 'placeholder', 'greeting', 'start-open', 'mode'] as const;
52
+ /** Public smooth-operator repo the "powered by" header tag + footer link here. */
53
+ const SMOOTH_OPERATOR_URL = 'https://github.com/SmooAI/smooth-operator';
54
+
55
+ const OBSERVED = ['endpoint', 'agent-id', 'agent-name', 'logo-url', 'placeholder', 'greeting', 'start-open', 'mode', 'hide-branding'] as const;
53
56
 
54
57
  /**
55
58
  * Inline SVG icons (static, trusted strings — never interpolated with user data).
@@ -204,6 +207,7 @@ export class SmoothAgentChatElement extends HTMLElement {
204
207
  greeting: this.overrides.greeting ?? this.getAttribute('greeting') ?? undefined,
205
208
  connectionErrorMessage: this.overrides.connectionErrorMessage,
206
209
  startOpen: this.overrides.startOpen ?? this.hasAttribute('start-open'),
210
+ hideBranding: this.overrides.hideBranding ?? this.hasAttribute('hide-branding'),
207
211
  examplePrompts: this.overrides.examplePrompts,
208
212
  requireName: this.overrides.requireName,
209
213
  requireEmail: this.overrides.requireEmail,
@@ -282,7 +286,11 @@ export class SmoothAgentChatElement extends HTMLElement {
282
286
  <span class="title">${escapeHtml(resolved.agentName)}</span>
283
287
  <span class="status"><span class="dot off"></span><span class="status-text"></span></span>
284
288
  </div>
285
- <span class="powered">powered by smooth-operator</span>
289
+ ${
290
+ resolved.hideBranding
291
+ ? ''
292
+ : `<a class="powered" href="${SMOOTH_OPERATOR_URL}" target="_blank" rel="noopener noreferrer">powered by smooth-operator</a>`
293
+ }
286
294
  </div>`
287
295
  : `<div class="header">
288
296
  <div class="avatar">${monogram}</div>
@@ -329,7 +337,15 @@ export class SmoothAgentChatElement extends HTMLElement {
329
337
  <button type="submit" class="pc-submit">Start chat</button>
330
338
  </form>
331
339
  </div>`;
332
- const restoreLink = this.allowChatRestore ? ` · <button type="button" class="restore-link">Restore my chats</button>` : '';
340
+ // Footer: optional "powered by" branding (hidden by hide-branding) and an
341
+ // optional "Restore my chats" affordance. The " · " separator only appears
342
+ // when both are present, and the footer is omitted entirely when neither is.
343
+ const brandingHtml = resolved.hideBranding
344
+ ? ''
345
+ : `<a href="${SMOOTH_OPERATOR_URL}" target="_blank" rel="noopener noreferrer">powered by <b>smooth&#8209;operator</b></a>`;
346
+ const restoreBtn = this.allowChatRestore ? `<button type="button" class="restore-link">Restore my chats</button>` : '';
347
+ const footerInner = [brandingHtml, restoreBtn].filter(Boolean).join(' · ');
348
+ const footerHtml = footerInner ? `<div class="footer">${footerInner}</div>` : '';
333
349
  const chatHtml = `
334
350
  <div class="messages"></div>
335
351
  <div class="interrupt hidden"></div>
@@ -338,7 +354,7 @@ export class SmoothAgentChatElement extends HTMLElement {
338
354
  <textarea rows="1" placeholder="${escapeHtml(resolved.placeholder)}"></textarea>
339
355
  <button class="send" type="button" aria-label="Send message">${ICON.send}</button>
340
356
  </div>
341
- <div class="footer">powered by <b>smooth&#8209;operator</b>${restoreLink}</div>
357
+ ${footerHtml}
342
358
  </div>`;
343
359
 
344
360
  const container = document.createElement('div');
package/src/styles.ts CHANGED
@@ -239,7 +239,8 @@ ${
239
239
  }
240
240
  .close:hover { background: color-mix(in srgb, var(--sac-text) 12%, transparent); transform: translateY(1px); }
241
241
  .close svg { width: 16px; height: 16px; opacity: .8; }
242
- .powered { margin-left: auto; font-size: 10.5px; letter-spacing: .02em; opacity: .6; }
242
+ .powered { margin-left: auto; font-size: 10.5px; letter-spacing: .02em; opacity: .6; color: inherit; text-decoration: none; }
243
+ .powered:hover { opacity: .85; text-decoration: underline; }
243
244
  .header-sep { height: 1px; margin: 0 16px; background: linear-gradient(90deg, transparent, var(--sac-border), transparent); }
244
245
 
245
246
  /* Full-page header: taller, logo-led, no close. */
@@ -511,6 +512,8 @@ ${
511
512
  color: color-mix(in srgb, var(--sac-text) 38%, transparent);
512
513
  }
513
514
  .footer b { font-weight: 600; color: color-mix(in srgb, var(--sac-text) 55%, transparent); }
515
+ .footer a { color: inherit; text-decoration: none; }
516
+ .footer a:hover { text-decoration: underline; }
514
517
 
515
518
  /* ─────────────────── Pre-chat identity form ───────────────────────── */
516
519
  .prechat { flex: 1; display: flex; flex-direction: column; justify-content: center; gap: 18px; padding: 22px 20px; }