@smooai/chat-widget 0.3.0 → 0.5.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.d.ts CHANGED
@@ -113,6 +113,9 @@ declare class SmoothAgentChatElement extends HTMLElement {
113
113
  private hasSent;
114
114
  /** Starter prompts shown as chips in the empty state. */
115
115
  private examplePrompts;
116
+ /** Current mid-turn interrupt (OTP / tool-confirmation), or null. */
117
+ private interrupt;
118
+ private interruptEl;
116
119
  private panelEl;
117
120
  private launcherEl;
118
121
  private messagesEl;
@@ -135,6 +138,12 @@ declare class SmoothAgentChatElement extends HTMLElement {
135
138
  closeChat(): void;
136
139
  private readConfig;
137
140
  private render;
141
+ /**
142
+ * Render (or clear) the mid-turn interrupt overlay above the composer:
143
+ * an OTP code prompt or a tool-write confirmation. Server-supplied text is
144
+ * set via `textContent` (never innerHTML); only static icons use innerHTML.
145
+ */
146
+ private renderInterrupt;
138
147
  /** Collect identity from the pre-chat form, then drop into the chat view. */
139
148
  private handlePrechatSubmit;
140
149
  /** Send a starter prompt (from a chip click). */
@@ -181,6 +190,45 @@ declare function mountChatWidget(config: ChatWidgetConfig, target?: HTMLElement)
181
190
  */
182
191
  declare function mountFullPageChat(config: Omit<ChatWidgetConfig, 'mode'>, target?: HTMLElement): SmoothAgentChatElement;
183
192
  //#endregion
193
+ //#region src/loader-core.d.ts
194
+ /**
195
+ * Tiny, dependency-free deferred loader for `@smooai/chat-widget`.
196
+ *
197
+ * The recommended embed: a host includes this *small* script eagerly, and it
198
+ * defers injecting the real (heavier) `chat-widget.global.js` module until the
199
+ * page is past its critical render — so the widget never competes with the host
200
+ * page's LCP/TBT. It triggers the real load on the **first** of:
201
+ *
202
+ * - `window` `load` → `requestIdleCallback` (idle time), or
203
+ * - user intent (`pointerdown` / `keydown` / `scroll`), or
204
+ * - an 8s fallback.
205
+ *
206
+ * After the module loads (which registers `<smooth-agent-chat>` and exposes
207
+ * `window.SmoothAgentChat`), it mounts the widget with the host's config.
208
+ *
209
+ * Deliberately imports nothing from the widget itself — this file must stay a
210
+ * few hundred bytes so including it eagerly is free.
211
+ *
212
+ * ## Config
213
+ * Set a global before/with the loader (any `ChatWidgetConfig`, plus an optional
214
+ * `src` pointing at the module bundle):
215
+ *
216
+ * ```html
217
+ * <script>window.SmoothAgentChatConfig = { endpoint: 'wss://…/ws', agentId: '…' };</script>
218
+ * <script src="https://cdn/…/chat-widget-loader.global.js" async></script>
219
+ * ```
220
+ *
221
+ * Or, for the simplest installs, `data-*` attributes on the loader's own tag
222
+ * (`data-endpoint`, `data-agent-id`, `data-primary`, `data-mode`, `data-src`).
223
+ * If no config is found, the module is still loaded (registering the element) so
224
+ * a `<smooth-agent-chat …>` placed directly in markup upgrades itself.
225
+ */
226
+ /**
227
+ * Install the deferred loader. Idempotent per call; the IIFE entry
228
+ * (`loader.ts`) invokes it once on script execution.
229
+ */
230
+ declare function initChatWidgetLoader(): void;
231
+ //#endregion
184
232
  //#region src/conversation.d.ts
185
233
  type Role = 'user' | 'assistant';
186
234
  interface ChatMessage {
@@ -274,5 +322,5 @@ declare class ConversationController {
274
322
  disconnect(): void;
275
323
  }
276
324
  //#endregion
277
- export { type ChatMessage, type ChatWidgetConfig, type ChatWidgetMode, type ChatWidgetTheme, type Citation, type ConnectionStatus, ConversationController, type ConversationEvents, ELEMENT_TAG, type Role, SmoothAgentChatElement, defineChatWidget, mountChatWidget, mountFullPageChat };
325
+ export { type ChatMessage, type ChatWidgetConfig, type ChatWidgetMode, type ChatWidgetTheme, type Citation, type ConnectionStatus, ConversationController, type ConversationEvents, ELEMENT_TAG, type Role, SmoothAgentChatElement, defineChatWidget, initChatWidgetLoader, mountChatWidget, mountFullPageChat };
278
326
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/config.ts","../src/element.ts","../src/conversation.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/config.ts","../src/element.ts","../src/loader-core.ts","../src/conversation.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -820,6 +820,63 @@ function buildStyles(theme, mode = "popover") {
820
820
  transform: translateY(-1px);
821
821
  }
822
822
 
823
+ /* ─────────────── OTP / tool-confirmation interrupt ────────────────── */
824
+ .interrupt { padding: 0 14px; }
825
+ .int-card {
826
+ border: 1px solid color-mix(in srgb, var(--sac-primary) 35%, var(--sac-border));
827
+ background: color-mix(in srgb, var(--sac-primary) 8%, var(--sac-surface-2));
828
+ border-radius: 14px;
829
+ padding: 12px 13px;
830
+ animation: sac-msg-in .3s var(--sac-ease) both;
831
+ }
832
+ .int-head { display: flex; align-items: center; gap: 8px; }
833
+ .int-ico { display: flex; color: var(--sac-primary); }
834
+ .int-ico svg { width: 17px; height: 17px; }
835
+ .int-title { font-size: 13.5px; font-weight: 650; }
836
+ .int-desc { margin-top: 5px; font-size: 12.5px; line-height: 1.45; color: color-mix(in srgb, var(--sac-text) 80%, transparent); }
837
+ .int-sent { margin-top: 6px; font-size: 11.5px; color: color-mix(in srgb, var(--sac-text) 60%, transparent); }
838
+ .int-row { display: flex; gap: 8px; margin-top: 10px; }
839
+ .int-input {
840
+ flex: 1;
841
+ min-width: 0;
842
+ border: 1px solid color-mix(in srgb, var(--sac-border) 80%, transparent);
843
+ background: var(--sac-bg);
844
+ color: var(--sac-text);
845
+ border-radius: 10px;
846
+ padding: 9px 11px;
847
+ font-family: inherit;
848
+ font-size: 14px;
849
+ letter-spacing: .14em;
850
+ outline: none;
851
+ transition: border-color .2s ease, box-shadow .2s ease;
852
+ }
853
+ .int-input:focus {
854
+ border-color: color-mix(in srgb, var(--sac-primary) 60%, transparent);
855
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--sac-primary) 14%, transparent);
856
+ }
857
+ .int-btn {
858
+ border: 1px solid color-mix(in srgb, var(--sac-border) 80%, transparent);
859
+ background: var(--sac-surface-2);
860
+ color: var(--sac-text);
861
+ border-radius: 10px;
862
+ padding: 9px 14px;
863
+ font-family: inherit;
864
+ font-size: 13px;
865
+ font-weight: 600;
866
+ cursor: pointer;
867
+ transition: transform .2s var(--sac-ease), background .2s ease, border-color .2s ease;
868
+ }
869
+ .int-btn:hover { transform: translateY(-1px); }
870
+ .int-btn.primary {
871
+ border: none;
872
+ background: linear-gradient(150deg, var(--sac-primary), var(--sac-primary-2));
873
+ color: var(--sac-primary-text);
874
+ box-shadow: 0 6px 14px -6px color-mix(in srgb, var(--sac-primary) 65%, transparent);
875
+ }
876
+ .int-row .int-btn { flex: 1; }
877
+ .int-row .int-input + .int-btn { flex: 0 0 auto; }
878
+ .int-error { margin-top: 8px; font-size: 12px; color: #f87171; }
879
+
823
880
  .hidden { display: none !important; }
824
881
 
825
882
  @media (prefers-reduced-motion: reduce) {
@@ -854,7 +911,11 @@ const ICON = {
854
911
  /** Send — an upward arrow. */
855
912
  send: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 19V6M12 6l-5.5 5.5M12 6l5.5 5.5" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
856
913
  /** Sources disclosure caret. */
857
- chev: `<svg width="11" height="11" viewBox="0 0 24 24" fill="none"><path d="m9 6 6 6-6 6" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>`
914
+ chev: `<svg width="11" height="11" viewBox="0 0 24 24" fill="none"><path d="m9 6 6 6-6 6" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
915
+ /** OTP interrupt — a padlock. */
916
+ lock: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="5" y="10.5" width="14" height="9.5" rx="2.2" stroke="currentColor" stroke-width="1.7"/><path d="M8 10.5V8a4 4 0 0 1 8 0v2.5" stroke="currentColor" stroke-width="1.7"/></svg>`,
917
+ /** Tool-confirmation interrupt — a shield. */
918
+ shield: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3 5 6v5c0 4.4 3 7.2 7 8.5 4-1.3 7-4.1 7-8.5V6l-7-3Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="m9 11.5 2 2 4-4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>`
858
919
  };
859
920
  /**
860
921
  * Return `url` only if it is a valid absolute `http(s)` URL, else `null`.
@@ -891,6 +952,9 @@ var SmoothAgentChatElement = class extends HTMLElement {
891
952
  hasSent = false;
892
953
  /** Starter prompts shown as chips in the empty state. */
893
954
  examplePrompts = [];
955
+ /** Current mid-turn interrupt (OTP / tool-confirmation), or null. */
956
+ interrupt = null;
957
+ interruptEl = null;
894
958
  panelEl = null;
895
959
  launcherEl = null;
896
960
  messagesEl = null;
@@ -983,6 +1047,10 @@ var SmoothAgentChatElement = class extends HTMLElement {
983
1047
  this.status = status;
984
1048
  this.renderStatus();
985
1049
  this.renderComposerState();
1050
+ },
1051
+ onInterrupt: (interrupt) => {
1052
+ this.interrupt = interrupt;
1053
+ this.renderInterrupt();
986
1054
  }
987
1055
  });
988
1056
  if (resolved.startOpen) this.open = true;
@@ -1025,6 +1093,7 @@ var SmoothAgentChatElement = class extends HTMLElement {
1025
1093
  </div>`;
1026
1094
  const chatHtml = `
1027
1095
  <div class="messages"></div>
1096
+ <div class="interrupt hidden"></div>
1028
1097
  <div class="composer-wrap">
1029
1098
  <div class="composer">
1030
1099
  <textarea rows="1" placeholder="${escapeHtml(resolved.placeholder)}"></textarea>
@@ -1051,6 +1120,7 @@ var SmoothAgentChatElement = class extends HTMLElement {
1051
1120
  this.dotEl = container.querySelector(".dot");
1052
1121
  this.inputEl = container.querySelector("textarea");
1053
1122
  this.sendBtn = container.querySelector(".send");
1123
+ this.interruptEl = container.querySelector(".interrupt");
1054
1124
  this.launcherEl?.addEventListener("click", () => this.openChat());
1055
1125
  container.querySelector(".close")?.addEventListener("click", () => this.closeChat());
1056
1126
  this.sendBtn?.addEventListener("click", () => this.submit());
@@ -1071,6 +1141,97 @@ var SmoothAgentChatElement = class extends HTMLElement {
1071
1141
  if (!gating) this.renderMessages(resolved.greeting);
1072
1142
  this.renderStatus();
1073
1143
  this.renderComposerState();
1144
+ this.renderInterrupt();
1145
+ }
1146
+ /**
1147
+ * Render (or clear) the mid-turn interrupt overlay above the composer:
1148
+ * an OTP code prompt or a tool-write confirmation. Server-supplied text is
1149
+ * set via `textContent` (never innerHTML); only static icons use innerHTML.
1150
+ */
1151
+ renderInterrupt() {
1152
+ const el = this.interruptEl;
1153
+ if (!el) return;
1154
+ el.replaceChildren();
1155
+ const it = this.interrupt;
1156
+ if (!it) {
1157
+ el.classList.add("hidden");
1158
+ return;
1159
+ }
1160
+ el.classList.remove("hidden");
1161
+ const card = document.createElement("div");
1162
+ card.className = "int-card";
1163
+ const head = document.createElement("div");
1164
+ head.className = "int-head";
1165
+ const ico = document.createElement("span");
1166
+ ico.className = "int-ico";
1167
+ ico.innerHTML = it.kind === "otp" ? ICON.lock : ICON.shield;
1168
+ const title = document.createElement("span");
1169
+ title.className = "int-title";
1170
+ title.textContent = it.kind === "otp" ? "Verification required" : "Confirm this action";
1171
+ head.append(ico, title);
1172
+ card.appendChild(head);
1173
+ if (it.actionDescription) {
1174
+ const desc = document.createElement("div");
1175
+ desc.className = "int-desc";
1176
+ desc.textContent = it.actionDescription;
1177
+ card.appendChild(desc);
1178
+ }
1179
+ if (it.kind === "otp") {
1180
+ if (it.sent?.maskedDestination) {
1181
+ const sent = document.createElement("div");
1182
+ sent.className = "int-sent";
1183
+ sent.textContent = `Code sent to ${it.sent.maskedDestination}${it.sent.channel ? ` via ${it.sent.channel}` : ""}.`;
1184
+ card.appendChild(sent);
1185
+ }
1186
+ const row = document.createElement("div");
1187
+ row.className = "int-row";
1188
+ const input = document.createElement("input");
1189
+ input.className = "int-input";
1190
+ input.type = "text";
1191
+ input.inputMode = "numeric";
1192
+ input.autocomplete = "one-time-code";
1193
+ input.placeholder = "Enter code";
1194
+ const submit = () => {
1195
+ const code = input.value.trim();
1196
+ if (code) this.controller?.verifyOtp(code);
1197
+ };
1198
+ input.addEventListener("keydown", (ev) => {
1199
+ if (ev.key === "Enter") {
1200
+ ev.preventDefault();
1201
+ submit();
1202
+ }
1203
+ });
1204
+ const verify = document.createElement("button");
1205
+ verify.className = "int-btn primary";
1206
+ verify.type = "button";
1207
+ verify.textContent = "Verify";
1208
+ verify.addEventListener("click", submit);
1209
+ row.append(input, verify);
1210
+ card.appendChild(row);
1211
+ if (it.error) {
1212
+ const err = document.createElement("div");
1213
+ err.className = "int-error";
1214
+ err.textContent = it.attemptsRemaining != null ? `${it.error} (${it.attemptsRemaining} left)` : it.error;
1215
+ card.appendChild(err);
1216
+ }
1217
+ queueMicrotask(() => input.focus());
1218
+ } else {
1219
+ const row = document.createElement("div");
1220
+ row.className = "int-row";
1221
+ const decline = document.createElement("button");
1222
+ decline.className = "int-btn";
1223
+ decline.type = "button";
1224
+ decline.textContent = "Decline";
1225
+ decline.addEventListener("click", () => this.controller?.confirmTool(false));
1226
+ const approve = document.createElement("button");
1227
+ approve.className = "int-btn primary";
1228
+ approve.type = "button";
1229
+ approve.textContent = "Approve";
1230
+ approve.addEventListener("click", () => this.controller?.confirmTool(true));
1231
+ row.append(decline, approve);
1232
+ card.appendChild(row);
1233
+ }
1234
+ el.appendChild(card);
1074
1235
  }
1075
1236
  /** Collect identity from the pre-chat form, then drop into the chat view. */
1076
1237
  handlePrechatSubmit(form) {
@@ -1291,6 +1452,142 @@ function mountFullPageChat(config, target = document.body) {
1291
1452
  }, target);
1292
1453
  }
1293
1454
  //#endregion
1294
- export { ConversationController, ELEMENT_TAG, SmoothAgentChatElement, defineChatWidget, mountChatWidget, mountFullPageChat };
1455
+ //#region src/loader-core.ts
1456
+ /**
1457
+ * Tiny, dependency-free deferred loader for `@smooai/chat-widget`.
1458
+ *
1459
+ * The recommended embed: a host includes this *small* script eagerly, and it
1460
+ * defers injecting the real (heavier) `chat-widget.global.js` module until the
1461
+ * page is past its critical render — so the widget never competes with the host
1462
+ * page's LCP/TBT. It triggers the real load on the **first** of:
1463
+ *
1464
+ * - `window` `load` → `requestIdleCallback` (idle time), or
1465
+ * - user intent (`pointerdown` / `keydown` / `scroll`), or
1466
+ * - an 8s fallback.
1467
+ *
1468
+ * After the module loads (which registers `<smooth-agent-chat>` and exposes
1469
+ * `window.SmoothAgentChat`), it mounts the widget with the host's config.
1470
+ *
1471
+ * Deliberately imports nothing from the widget itself — this file must stay a
1472
+ * few hundred bytes so including it eagerly is free.
1473
+ *
1474
+ * ## Config
1475
+ * Set a global before/with the loader (any `ChatWidgetConfig`, plus an optional
1476
+ * `src` pointing at the module bundle):
1477
+ *
1478
+ * ```html
1479
+ * <script>window.SmoothAgentChatConfig = { endpoint: 'wss://…/ws', agentId: '…' };<\/script>
1480
+ * <script src="https://cdn/…/chat-widget-loader.global.js" async><\/script>
1481
+ * ```
1482
+ *
1483
+ * Or, for the simplest installs, `data-*` attributes on the loader's own tag
1484
+ * (`data-endpoint`, `data-agent-id`, `data-primary`, `data-mode`, `data-src`).
1485
+ * If no config is found, the module is still loaded (registering the element) so
1486
+ * a `<smooth-agent-chat …>` placed directly in markup upgrades itself.
1487
+ */
1488
+ /** Must match `ELEMENT_TAG` in `element.ts`; inlined to avoid importing it. */
1489
+ const ELEMENT_TAG$1 = "smooth-agent-chat";
1490
+ /** Default module filename, resolved as a sibling of the loader script. */
1491
+ const DEFAULT_MODULE = "chat-widget.global.js";
1492
+ /**
1493
+ * Resolve the module URL: explicit `src` (config or `data-src`) wins, else a
1494
+ * sibling of the loader script, else the bare default filename.
1495
+ */
1496
+ function resolveModuleSrc(win, script) {
1497
+ const fromConfig = win.SmoothAgentChatConfig?.src;
1498
+ if (fromConfig) return fromConfig;
1499
+ const fromAttr = script?.getAttribute("data-src");
1500
+ if (fromAttr) return fromAttr;
1501
+ const selfSrc = script?.src;
1502
+ if (selfSrc) return selfSrc.replace(/[^/]*$/, DEFAULT_MODULE);
1503
+ return DEFAULT_MODULE;
1504
+ }
1505
+ /**
1506
+ * The widget mount config: the `SmoothAgentChatConfig` global (minus the
1507
+ * loader-only `src`), or `data-*` attributes. `undefined` ⇒ no programmatic
1508
+ * mount (a markup element will upgrade itself once the module registers it).
1509
+ */
1510
+ function resolveMountConfig(win, script) {
1511
+ if (win.SmoothAgentChatConfig) {
1512
+ const { src: _src, ...config } = win.SmoothAgentChatConfig;
1513
+ return Object.keys(config).length > 0 ? config : void 0;
1514
+ }
1515
+ if (!script) return void 0;
1516
+ const endpoint = script.getAttribute("data-endpoint");
1517
+ const agentId = script.getAttribute("data-agent-id");
1518
+ if (!endpoint && !agentId) return void 0;
1519
+ const config = {};
1520
+ if (endpoint) config.endpoint = endpoint;
1521
+ if (agentId) config.agentId = agentId;
1522
+ const primary = script.getAttribute("data-primary");
1523
+ if (primary) config.theme = { primary };
1524
+ const mode = script.getAttribute("data-mode");
1525
+ if (mode) config.mode = mode;
1526
+ return config;
1527
+ }
1528
+ /**
1529
+ * Install the deferred loader. Idempotent per call; the IIFE entry
1530
+ * (`loader.ts`) invokes it once on script execution.
1531
+ */
1532
+ function initChatWidgetLoader() {
1533
+ const win = window;
1534
+ const script = document.currentScript ?? null;
1535
+ const moduleSrc = resolveModuleSrc(win, script);
1536
+ let loaded = false;
1537
+ let idleId;
1538
+ let fallbackId;
1539
+ function mountWhenReady() {
1540
+ const config = resolveMountConfig(win, script);
1541
+ if (!config) return;
1542
+ const apply = () => {
1543
+ try {
1544
+ win.SmoothAgentChat?.mount(config);
1545
+ } catch (error) {
1546
+ console.error("[chat-widget loader] mount failed", error);
1547
+ }
1548
+ };
1549
+ if (win.SmoothAgentChat) apply();
1550
+ else if (window.customElements?.whenDefined) window.customElements.whenDefined(ELEMENT_TAG$1).then(apply, apply);
1551
+ else apply();
1552
+ }
1553
+ function teardown() {
1554
+ if (idleId !== void 0) {
1555
+ window.cancelIdleCallback?.(idleId);
1556
+ window.clearTimeout(idleId);
1557
+ }
1558
+ if (fallbackId !== void 0) window.clearTimeout(fallbackId);
1559
+ window.removeEventListener("pointerdown", load);
1560
+ window.removeEventListener("keydown", load);
1561
+ window.removeEventListener("scroll", load);
1562
+ }
1563
+ function load() {
1564
+ if (loaded) return;
1565
+ loaded = true;
1566
+ teardown();
1567
+ const tag = document.createElement("script");
1568
+ tag.src = moduleSrc;
1569
+ tag.onload = mountWhenReady;
1570
+ tag.onerror = () => console.error("[chat-widget loader] failed to load", moduleSrc);
1571
+ document.head.appendChild(tag);
1572
+ }
1573
+ function schedule() {
1574
+ if (window.requestIdleCallback) idleId = window.requestIdleCallback(() => load(), { timeout: 5e3 });
1575
+ else idleId = window.setTimeout(load, 2500);
1576
+ fallbackId = window.setTimeout(load, 8e3);
1577
+ }
1578
+ window.addEventListener("pointerdown", load, {
1579
+ once: true,
1580
+ passive: true
1581
+ });
1582
+ window.addEventListener("keydown", load, { once: true });
1583
+ window.addEventListener("scroll", load, {
1584
+ once: true,
1585
+ passive: true
1586
+ });
1587
+ if (document.readyState === "complete") schedule();
1588
+ else window.addEventListener("load", schedule, { once: true });
1589
+ }
1590
+ //#endregion
1591
+ export { ConversationController, ELEMENT_TAG, SmoothAgentChatElement, defineChatWidget, initChatWidgetLoader, mountChatWidget, mountFullPageChat };
1295
1592
 
1296
1593
  //# sourceMappingURL=index.js.map