@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/README.md +76 -19
- package/dist/chat-widget-loader.global.js +157 -0
- package/dist/chat-widget-loader.global.js.map +1 -0
- package/dist/chat-widget.global.js +162 -1
- package/dist/chat-widget.global.js.map +1 -1
- package/dist/index.d.ts +49 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +299 -2
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
- package/src/element.ts +111 -1
- package/src/index.ts +4 -0
- package/src/loader-core.ts +161 -0
- package/src/loader.ts +17 -0
- package/src/styles.ts +57 -0
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/config.ts","../src/element.ts","../src/conversation.ts"],"mappings":";;;;;;AAOA;;;;UAAiB,eAAA;EAIb;EAFA,IAAA;EAMA;EAJA,UAAA;EAQA;EANA,OAAA;EAUA;EARA,WAAA;EAYA;EAVA,SAAA;EAkBA;EAhBA,eAAA;EAoBA;EAlBA,mBAAA;EAkBsB;EAhBtB,UAAA;EA2BsB;EAzBtB,cAAA;EAyBsB;EAvBtB,MAAA;EAyBa;EAnBb,iBAAA;;EAEA,qBAAA;EAsBA;EApBA,kBAAA;EAyBO;EAvBP,sBAAA;AAAA;;;;;;;;;KAWQ,cAAA;AAAA,UAEK,gBAAA;EAuCb;;;;EAlCA,QAAA;EAyCuB;;;;EApCvB,IAAA,GAAO,cAAA;ECxCa;ED0CpB,OAAA;EC1CoB;ED4CpB,SAAA;
|
|
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
|
-
|
|
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
|