@syntrologie/adapt-chatbot 2.52.0 → 2.54.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/AdaptiveChatBarMountable.d.ts.map +1 -1
- package/dist/LeaveAMessageLit.d.ts +68 -9
- package/dist/LeaveAMessageLit.d.ts.map +1 -1
- package/dist/findSmartCanvas.d.ts +19 -0
- package/dist/findSmartCanvas.d.ts.map +1 -0
- package/dist/runtime.js +729 -456
- package/dist/runtime.js.map +4 -4
- package/dist/schema.d.ts +72 -4
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +17 -0
- package/dist/schema.js.map +2 -2
- package/dist/support-chat/__tests__/lc-fetch-stub.d.ts +26 -1
- package/dist/support-chat/__tests__/lc-fetch-stub.d.ts.map +1 -1
- package/dist/support-chat/boot.d.ts +35 -3
- package/dist/support-chat/boot.d.ts.map +1 -1
- package/dist/support-chat/inbox.d.ts +57 -0
- package/dist/support-chat/inbox.d.ts.map +1 -0
- package/dist/support-chat/inbox.test.d.ts +2 -0
- package/dist/support-chat/inbox.test.d.ts.map +1 -0
- package/dist/support-chat/leadconnector.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -1334,6 +1334,24 @@ var ChatTransport = class {
|
|
|
1334
1334
|
};
|
|
1335
1335
|
var chatTransport = new ChatTransport();
|
|
1336
1336
|
|
|
1337
|
+
// src/findSmartCanvas.ts
|
|
1338
|
+
function findSmartCanvas() {
|
|
1339
|
+
if (typeof document === "undefined") return null;
|
|
1340
|
+
const find = (root) => {
|
|
1341
|
+
const direct = root.querySelector("smart-canvas");
|
|
1342
|
+
if (direct) return direct;
|
|
1343
|
+
for (const el of root.querySelectorAll("*")) {
|
|
1344
|
+
const sr = el.shadowRoot;
|
|
1345
|
+
if (sr) {
|
|
1346
|
+
const found = find(sr);
|
|
1347
|
+
if (found) return found;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return null;
|
|
1351
|
+
};
|
|
1352
|
+
return find(document);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1337
1355
|
// src/observer/allowlist.ts
|
|
1338
1356
|
var MAX_TEXT_LEN = 200;
|
|
1339
1357
|
var SIGNIFICANT_CLICK_TAGS = /* @__PURE__ */ new Set(["button", "a"]);
|
|
@@ -2031,7 +2049,7 @@ async function pollOnce(cb, isActive) {
|
|
|
2031
2049
|
const qs = new URLSearchParams({
|
|
2032
2050
|
conversationId: id.conversationId,
|
|
2033
2051
|
locationId: id.locationId,
|
|
2034
|
-
lastMessageId:
|
|
2052
|
+
lastMessageId: "",
|
|
2035
2053
|
pageLimit: String(LC_PAGE_LIMIT)
|
|
2036
2054
|
});
|
|
2037
2055
|
try {
|
|
@@ -2051,14 +2069,15 @@ async function pollOnce(cb, isActive) {
|
|
|
2051
2069
|
const j = await r.json();
|
|
2052
2070
|
if (!isActive()) return;
|
|
2053
2071
|
const messages = j.messages ?? [];
|
|
2054
|
-
|
|
2072
|
+
const seenIdx = id.lastSeenMessageId ? messages.findIndex((m) => m.id === id.lastSeenMessageId) : -1;
|
|
2073
|
+
const fresh = seenIdx === -1 ? messages : messages.slice(0, seenIdx);
|
|
2074
|
+
for (const m of [...fresh].reverse()) {
|
|
2055
2075
|
if (m.direction === "outbound" && typeof m.body === "string") cb(m.body);
|
|
2056
2076
|
}
|
|
2057
|
-
const
|
|
2058
|
-
|
|
2059
|
-
if (nextLastMessageId) {
|
|
2077
|
+
const newestId = messages[0]?.id;
|
|
2078
|
+
if (typeof newestId === "string" && newestId) {
|
|
2060
2079
|
const current = load();
|
|
2061
|
-
save({ ...id, ...current,
|
|
2080
|
+
save({ ...id, ...current, lastSeenMessageId: newestId });
|
|
2062
2081
|
}
|
|
2063
2082
|
pollWarned = false;
|
|
2064
2083
|
} catch (err) {
|
|
@@ -2112,6 +2131,47 @@ function registerLeadConnectorBridge() {
|
|
|
2112
2131
|
registerLeadConnectorBridge();
|
|
2113
2132
|
|
|
2114
2133
|
// src/support-chat/boot.ts
|
|
2134
|
+
var SUPPORT_CHAT_WATCH_TICK_MS = 500;
|
|
2135
|
+
var SUPPORT_CHAT_WATCH_MAX_MS = 30 * 60 * 1e3;
|
|
2136
|
+
var watches = /* @__PURE__ */ new Map();
|
|
2137
|
+
function settleWatch(bridge, watch) {
|
|
2138
|
+
clearInterval(watch.timer);
|
|
2139
|
+
watches.delete(bridge);
|
|
2140
|
+
if (watch.suppressNative) bridge.hideNative();
|
|
2141
|
+
for (const cb of watch.listeners) cb();
|
|
2142
|
+
watch.listeners.clear();
|
|
2143
|
+
}
|
|
2144
|
+
function ensureWatch(bridge, suppressNative) {
|
|
2145
|
+
const existing = watches.get(bridge);
|
|
2146
|
+
if (existing) {
|
|
2147
|
+
existing.suppressNative = existing.suppressNative || suppressNative;
|
|
2148
|
+
return existing;
|
|
2149
|
+
}
|
|
2150
|
+
console.info(
|
|
2151
|
+
`supportChat.bridge: vendor '${bridge.vendor}' is not on the page yet \u2014 watching for it (delayed-script hosts load it on the first interaction)`
|
|
2152
|
+
);
|
|
2153
|
+
const watch = {
|
|
2154
|
+
startedAt: Date.now(),
|
|
2155
|
+
suppressNative,
|
|
2156
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
2157
|
+
timer: setInterval(() => {
|
|
2158
|
+
if (bridge.detect()) {
|
|
2159
|
+
settleWatch(bridge, watch);
|
|
2160
|
+
return;
|
|
2161
|
+
}
|
|
2162
|
+
if (Date.now() - watch.startedAt >= SUPPORT_CHAT_WATCH_MAX_MS) {
|
|
2163
|
+
clearInterval(watch.timer);
|
|
2164
|
+
watches.delete(bridge);
|
|
2165
|
+
watch.listeners.clear();
|
|
2166
|
+
console.warn(
|
|
2167
|
+
`supportChat.bridge: vendor '${bridge.vendor}' never appeared within ${SUPPORT_CHAT_WATCH_MAX_MS / 6e4} minutes \u2014 handoff disabled`
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
}, SUPPORT_CHAT_WATCH_TICK_MS)
|
|
2171
|
+
};
|
|
2172
|
+
watches.set(bridge, watch);
|
|
2173
|
+
return watch;
|
|
2174
|
+
}
|
|
2115
2175
|
function bootSupportChat(cfg2) {
|
|
2116
2176
|
if (!cfg2?.vendor) return void 0;
|
|
2117
2177
|
registerCrispBridge();
|
|
@@ -2126,442 +2186,29 @@ function bootSupportChat(cfg2) {
|
|
|
2126
2186
|
leadConnectorBridge.configure({ widgetId: cfg2.widgetId });
|
|
2127
2187
|
}
|
|
2128
2188
|
const bridge = getSupportChatBridge(cfg2.vendor);
|
|
2129
|
-
if (!bridge
|
|
2130
|
-
console.warn(
|
|
2131
|
-
`supportChat.bridge: vendor '${cfg2.vendor}' not detected on this page \u2014 handoff disabled`
|
|
2132
|
-
);
|
|
2189
|
+
if (!bridge) {
|
|
2190
|
+
console.warn(`supportChat.bridge: no bridge for vendor '${cfg2.vendor}' \u2014 handoff disabled`);
|
|
2133
2191
|
return void 0;
|
|
2134
2192
|
}
|
|
2135
|
-
if (
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
// src/AdaptiveChatBarMountable.ts
|
|
2140
|
-
var STATE_KEY = "__syntroChatBarMount";
|
|
2141
|
-
function getState(container) {
|
|
2142
|
-
return container[STATE_KEY] ?? null;
|
|
2143
|
-
}
|
|
2144
|
-
function setState(container, state) {
|
|
2145
|
-
container[STATE_KEY] = state;
|
|
2146
|
-
}
|
|
2147
|
-
function applyPlaceholder(bar, cfg2) {
|
|
2148
|
-
if (cfg2.placeholder !== void 0) bar.placeholder = cfg2.placeholder;
|
|
2149
|
-
if (cfg2.assistantName !== void 0) bar.assistantName = cfg2.assistantName;
|
|
2150
|
-
if (cfg2.personas !== void 0) bar.personas = cfg2.personas;
|
|
2151
|
-
if (cfg2.greeting !== void 0) bar.greeting = cfg2.greeting;
|
|
2152
|
-
if (cfg2.voiceInput !== void 0) bar.voiceInput = cfg2.voiceInput;
|
|
2153
|
-
bar.introSuggestion = cfg2.introSuggestion;
|
|
2154
|
-
bar.forceExpanded = cfg2.forceExpanded === true;
|
|
2155
|
-
if (typeof cfg2.maximized === "boolean") {
|
|
2156
|
-
bar.maximized = cfg2.maximized;
|
|
2193
|
+
if (bridge.detect()) {
|
|
2194
|
+
if (cfg2.suppressNative) bridge.hideNative();
|
|
2195
|
+
return bridge;
|
|
2157
2196
|
}
|
|
2197
|
+
ensureWatch(bridge, cfg2.suppressNative === true);
|
|
2198
|
+
return bridge;
|
|
2158
2199
|
}
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
}
|
|
2165
|
-
function refreshTemplateWidgetMap(uiTemplates) {
|
|
2166
|
-
_templateWidgetMap.clear();
|
|
2167
|
-
for (const tag of SERVER_MOUNTED_TILE_WIDGETS) {
|
|
2168
|
-
_templateWidgetMap.set(tag, tag);
|
|
2169
|
-
}
|
|
2170
|
-
if (!uiTemplates) return;
|
|
2171
|
-
const tiles = uiTemplates.tiles;
|
|
2172
|
-
if (!Array.isArray(tiles)) return;
|
|
2173
|
-
for (const t of tiles) {
|
|
2174
|
-
const id = typeof t?.id === "string" ? t.id : null;
|
|
2175
|
-
const widget = typeof t?.widget === "string" ? t.widget : null;
|
|
2176
|
-
if (id && widget) _templateWidgetMap.set(id, widget);
|
|
2200
|
+
function onSupportChatReady(bridge, cb) {
|
|
2201
|
+
if (bridge.detect()) {
|
|
2202
|
+
cb();
|
|
2203
|
+
return () => {
|
|
2204
|
+
};
|
|
2177
2205
|
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
actions: runtime2.actions,
|
|
2183
|
-
// Pass the runtime's event bus so ItemHandler can broadcast
|
|
2184
|
-
// `element.compositional_append` / `_patch` / `_remove` events
|
|
2185
|
-
// to container widgets (chips strip, FAQ accordion, nav tips).
|
|
2186
|
-
events: {
|
|
2187
|
-
publish: runtime2.events.publish.bind(runtime2.events),
|
|
2188
|
-
subscribe: runtime2.events.subscribe?.bind(runtime2.events)
|
|
2189
|
-
},
|
|
2190
|
-
handlers: [new TileHandler(resolveTileWidget), new ActionHandler(), new ItemHandler()],
|
|
2191
|
-
resolveTileWidget
|
|
2192
|
-
});
|
|
2193
|
-
setElementInstanceStore(_elementStore);
|
|
2194
|
-
return _elementStore;
|
|
2195
|
-
}
|
|
2196
|
-
var _hydrationStarted = false;
|
|
2197
|
-
function hydrateOnce(runtime2, backendUrl) {
|
|
2198
|
-
if (_hydrationStarted) return;
|
|
2199
|
-
_hydrationStarted = true;
|
|
2200
|
-
const store = getOrCreateElementStore(runtime2);
|
|
2201
|
-
const trimmed = backendUrl.replace(/\/$/, "");
|
|
2202
|
-
const endpoint = trimmed ? `${trimmed}/api/adaptive/mounted_elements` : "/api/adaptive/mounted_elements";
|
|
2203
|
-
fetchMountedElements({ endpoint, token: runtime2.token }).then((response) => {
|
|
2204
|
-
if (!response) return;
|
|
2205
|
-
void store.hydrate(response.mounted_elements);
|
|
2206
|
-
});
|
|
2207
|
-
}
|
|
2208
|
-
var PAGE_LEVEL_KINDS = /* @__PURE__ */ new Set(["view", "scroll", "idle", "hesitation", "hover", "nav"]);
|
|
2209
|
-
function buildObserverEnrich(runtimeRef) {
|
|
2210
|
-
return (ev, raw) => {
|
|
2211
|
-
const extract = runtimeRef.getProductIdentity;
|
|
2212
|
-
if (typeof extract !== "function") return ev;
|
|
2213
|
-
if (ev.kind === "click") {
|
|
2214
|
-
const chain = raw.properties.$elements;
|
|
2215
|
-
if (!Array.isArray(chain)) return ev;
|
|
2216
|
-
const id = extract(chain);
|
|
2217
|
-
if (!id) return ev;
|
|
2218
|
-
return {
|
|
2219
|
-
...ev,
|
|
2220
|
-
product: {
|
|
2221
|
-
handle: id.productHandle,
|
|
2222
|
-
url: id.productUrl,
|
|
2223
|
-
...id.productTitle ? { title: id.productTitle } : {}
|
|
2224
|
-
}
|
|
2225
|
-
};
|
|
2226
|
-
}
|
|
2227
|
-
if (!ev.product && PAGE_LEVEL_KINDS.has(ev.kind) && typeof location !== "undefined") {
|
|
2228
|
-
const id = extract([{ href: location.pathname }]);
|
|
2229
|
-
if (!id) return ev;
|
|
2230
|
-
return {
|
|
2231
|
-
...ev,
|
|
2232
|
-
product: {
|
|
2233
|
-
handle: id.productHandle,
|
|
2234
|
-
url: id.productUrl,
|
|
2235
|
-
...id.productTitle ? { title: id.productTitle } : {}
|
|
2236
|
-
}
|
|
2237
|
-
};
|
|
2238
|
-
}
|
|
2239
|
-
return ev;
|
|
2240
|
-
};
|
|
2241
|
-
}
|
|
2242
|
-
var _nudgeSurfaceWired = false;
|
|
2243
|
-
var _nudgeListener = null;
|
|
2244
|
-
function wireNudgeSurface(runtime2, backendUrl) {
|
|
2245
|
-
if (_nudgeSurfaceWired || typeof document === "undefined") return;
|
|
2246
|
-
_nudgeSurfaceWired = true;
|
|
2247
|
-
_nudgeListener = (e) => {
|
|
2248
|
-
try {
|
|
2249
|
-
const detail = e.detail;
|
|
2250
|
-
const isIntent = detail?.phase === "intent";
|
|
2251
|
-
if (!isIntent) {
|
|
2252
|
-
const store = getOrCreateElementStore(runtime2);
|
|
2253
|
-
const trimmed = backendUrl.replace(/\/$/, "");
|
|
2254
|
-
const endpoint = trimmed ? `${trimmed}/api/adaptive/mounted_elements` : "/api/adaptive/mounted_elements";
|
|
2255
|
-
void fetchMountedElements({ endpoint, token: runtime2.token }).then((response) => {
|
|
2256
|
-
if (!response) return;
|
|
2257
|
-
void store.hydrate(response.mounted_elements);
|
|
2258
|
-
});
|
|
2259
|
-
}
|
|
2260
|
-
if (detail?.suppressToast === true) return;
|
|
2261
|
-
const findCanvas = (root) => {
|
|
2262
|
-
const direct = root.querySelector("smart-canvas");
|
|
2263
|
-
if (direct) return direct;
|
|
2264
|
-
for (const el of root.querySelectorAll("*")) {
|
|
2265
|
-
const sr = el.shadowRoot;
|
|
2266
|
-
if (sr) {
|
|
2267
|
-
const found = findCanvas(sr);
|
|
2268
|
-
if (found) return found;
|
|
2269
|
-
}
|
|
2270
|
-
}
|
|
2271
|
-
return null;
|
|
2272
|
-
};
|
|
2273
|
-
const canvas = findCanvas(document);
|
|
2274
|
-
const messageId = e.detail?.messageId;
|
|
2275
|
-
const nudgeText = messageId ? (chatSession.getState().messages.find((m) => m.id === messageId)?.text ?? "").trim() : "";
|
|
2276
|
-
const title = nudgeText ? nudgeText.length > 140 ? `${nudgeText.slice(0, 137)}\u2026` : nudgeText : "I put something together for you";
|
|
2277
|
-
canvas?.previewNotification?.({
|
|
2278
|
-
title,
|
|
2279
|
-
body: "Tap to take a look"
|
|
2280
|
-
});
|
|
2281
|
-
} catch (err) {
|
|
2282
|
-
console.warn("[adaptive-chatbot] nudge surface reaction failed", err);
|
|
2283
|
-
}
|
|
2206
|
+
const watch = ensureWatch(bridge, false);
|
|
2207
|
+
watch.listeners.add(cb);
|
|
2208
|
+
return () => {
|
|
2209
|
+
watch.listeners.delete(cb);
|
|
2284
2210
|
};
|
|
2285
|
-
document.addEventListener("syntro:nudge", _nudgeListener);
|
|
2286
|
-
}
|
|
2287
|
-
function configureTransportIfPossible(cfg2) {
|
|
2288
|
-
if (!cfg2.runtime) return;
|
|
2289
|
-
const backendUrl = cfg2.runtime.backendUrl;
|
|
2290
|
-
if (!backendUrl) return;
|
|
2291
|
-
const elementsActive = cfg2.elementsEnabled === true && cfg2.uiTemplates != null;
|
|
2292
|
-
refreshTemplateWidgetMap(elementsActive ? cfg2.uiTemplates : void 0);
|
|
2293
|
-
const forwardedProps = elementsActive ? { elementsEnabled: true, uiTemplates: cfg2.uiTemplates } : void 0;
|
|
2294
|
-
if (elementsActive) publishTemplatesIfPossible(backendUrl, cfg2.uiTemplates, cfg2.runtime?.token);
|
|
2295
|
-
const runtime2 = cfg2.runtime;
|
|
2296
|
-
const onElementMutation = elementsActive ? (mutations) => {
|
|
2297
|
-
void getOrCreateElementStore(runtime2).apply(mutations);
|
|
2298
|
-
} : void 0;
|
|
2299
|
-
if (elementsActive) {
|
|
2300
|
-
hydrateOnce(runtime2, backendUrl);
|
|
2301
|
-
}
|
|
2302
|
-
const persona = pickPersona(cfg2.personas ?? null);
|
|
2303
|
-
chatTransport.configure({
|
|
2304
|
-
...cfg2,
|
|
2305
|
-
backendUrl,
|
|
2306
|
-
forwardedProps,
|
|
2307
|
-
onElementMutation,
|
|
2308
|
-
activeLidSlot: cfg2._syntroSlotName,
|
|
2309
|
-
personaId: persona?.id,
|
|
2310
|
-
personaName: persona?.name,
|
|
2311
|
-
personaRole: persona?.role_title
|
|
2312
|
-
});
|
|
2313
|
-
void chatTransport.startSessionStream();
|
|
2314
|
-
wireNudgeSurface(runtime2, backendUrl);
|
|
2315
|
-
startObserverIfPossible(cfg2);
|
|
2316
|
-
}
|
|
2317
|
-
var _templatesPublished = false;
|
|
2318
|
-
function publishTemplatesIfPossible(backendUrl, uiTemplates, token) {
|
|
2319
|
-
if (_templatesPublished || typeof window === "undefined") return;
|
|
2320
|
-
if (!uiTemplates || typeof uiTemplates !== "object") return;
|
|
2321
|
-
_templatesPublished = true;
|
|
2322
|
-
const headers2 = { "Content-Type": "application/json" };
|
|
2323
|
-
if (token) headers2.Authorization = `Bearer ${token}`;
|
|
2324
|
-
const editorToken = readEditorToken();
|
|
2325
|
-
if (editorToken) headers2["X-Syntro-Editor-Token"] = editorToken;
|
|
2326
|
-
const trimmed = backendUrl.replace(/\/$/, "");
|
|
2327
|
-
void fetch(`${trimmed}/api/adaptive/templates`, {
|
|
2328
|
-
method: "POST",
|
|
2329
|
-
headers: headers2,
|
|
2330
|
-
credentials: "include",
|
|
2331
|
-
body: JSON.stringify({ ui_templates: uiTemplates })
|
|
2332
|
-
}).catch(() => {
|
|
2333
|
-
});
|
|
2334
|
-
}
|
|
2335
|
-
var _diveDeeperWired = false;
|
|
2336
|
-
function wireDiveDeeperListener() {
|
|
2337
|
-
if (_diveDeeperWired || typeof window === "undefined") return;
|
|
2338
|
-
_diveDeeperWired = true;
|
|
2339
|
-
window.addEventListener(DIVE_DEEPER_EVENT, (e) => {
|
|
2340
|
-
const detail = e.detail ?? {};
|
|
2341
|
-
const prompt = typeof detail.prompt === "string" && detail.prompt.trim() ? detail.prompt : detail.title ? "I'd like to dive deeper on this, I didn't see what I needed." : "";
|
|
2342
|
-
if (!prompt) return;
|
|
2343
|
-
const origin = { kind: "dive-deeper" };
|
|
2344
|
-
if (detail.title) origin.title = detail.title;
|
|
2345
|
-
if (detail.context) origin.context = detail.context;
|
|
2346
|
-
chatSession.send(prompt, { forwardedProps: { origin } });
|
|
2347
|
-
if (!chatSession.hasTransport()) {
|
|
2348
|
-
chatSession.error("Chat backend not configured \u2014 set backendUrl in the canvas config.");
|
|
2349
|
-
}
|
|
2350
|
-
});
|
|
2351
2211
|
}
|
|
2352
|
-
var _observerStarted = false;
|
|
2353
|
-
var _observerRuntime = null;
|
|
2354
|
-
var _observerHandle = null;
|
|
2355
|
-
var _observerBusUnsubscribe = null;
|
|
2356
|
-
function startObserverIfPossible(cfg2) {
|
|
2357
|
-
if (typeof window === "undefined") return;
|
|
2358
|
-
const runtimeRef = cfg2.runtime;
|
|
2359
|
-
if (!runtimeRef?.events?.subscribe) return;
|
|
2360
|
-
if (_observerStarted && _observerRuntime === runtimeRef) return;
|
|
2361
|
-
if (_observerStarted) {
|
|
2362
|
-
_observerBusUnsubscribe?.();
|
|
2363
|
-
_observerHandle?.stop();
|
|
2364
|
-
_observerBusUnsubscribe = null;
|
|
2365
|
-
_observerHandle = null;
|
|
2366
|
-
_observerStarted = false;
|
|
2367
|
-
}
|
|
2368
|
-
const trimmed = (runtimeRef.backendUrl ?? "").replace(/\/$/, "");
|
|
2369
|
-
const url = trimmed ? `${trimmed}/api/adaptive/observation` : "/api/adaptive/observation";
|
|
2370
|
-
const runtimeId = (method) => {
|
|
2371
|
-
try {
|
|
2372
|
-
const v = runtimeRef.telemetry?.[method]?.();
|
|
2373
|
-
return typeof v === "string" && v.length > 0 ? v : null;
|
|
2374
|
-
} catch {
|
|
2375
|
-
return null;
|
|
2376
|
-
}
|
|
2377
|
-
};
|
|
2378
|
-
const getDistinctId = () => runtimeId("getDistinctId");
|
|
2379
|
-
const getSessionId = () => runtimeId("getSessionId");
|
|
2380
|
-
const token = () => {
|
|
2381
|
-
const t = runtimeRef.token;
|
|
2382
|
-
return typeof t === "string" ? t : "";
|
|
2383
|
-
};
|
|
2384
|
-
const enrich = buildObserverEnrich(runtimeRef);
|
|
2385
|
-
const handle = startObserver({ url, token, getDistinctId, getSessionId, enrich });
|
|
2386
|
-
window.__syntroObserverStats = () => handle.stats();
|
|
2387
|
-
const unsubscribe = runtimeRef.events.subscribe({}, (evt) => {
|
|
2388
|
-
const raw = busEventToRawEvent(evt);
|
|
2389
|
-
if (raw) handle.ingest(raw);
|
|
2390
|
-
});
|
|
2391
|
-
_observerRuntime = runtimeRef;
|
|
2392
|
-
_observerHandle = handle;
|
|
2393
|
-
_observerBusUnsubscribe = unsubscribe;
|
|
2394
|
-
_observerStarted = true;
|
|
2395
|
-
}
|
|
2396
|
-
function wireListeners(bar, state) {
|
|
2397
|
-
const onMessageSent = (e) => {
|
|
2398
|
-
const text = e.detail.text;
|
|
2399
|
-
chatSession.send(text, { activeLidSlot: state.cfg._syntroSlotName });
|
|
2400
|
-
if (!chatSession.hasTransport()) {
|
|
2401
|
-
chatSession.error("Chat backend not configured \u2014 set backendUrl in the canvas config.");
|
|
2402
|
-
}
|
|
2403
|
-
};
|
|
2404
|
-
const onInterrupt = () => {
|
|
2405
|
-
chatSession.interrupt();
|
|
2406
|
-
};
|
|
2407
|
-
const onToolCallApproved = (e) => {
|
|
2408
|
-
const detail = e.detail;
|
|
2409
|
-
chatSession.resolveToolCall(detail.toolCallId, {}, detail.approved);
|
|
2410
|
-
};
|
|
2411
|
-
const onClose = () => {
|
|
2412
|
-
state.cfg.onClose?.();
|
|
2413
|
-
};
|
|
2414
|
-
bar.addEventListener("chat-message-sent", onMessageSent);
|
|
2415
|
-
bar.addEventListener("chat-interrupt", onInterrupt);
|
|
2416
|
-
bar.addEventListener("canvas-close", onClose);
|
|
2417
|
-
bar.addEventListener("trail-toolcall-approved", onToolCallApproved);
|
|
2418
|
-
return () => {
|
|
2419
|
-
bar.removeEventListener("chat-message-sent", onMessageSent);
|
|
2420
|
-
bar.removeEventListener("chat-interrupt", onInterrupt);
|
|
2421
|
-
bar.removeEventListener("canvas-close", onClose);
|
|
2422
|
-
bar.removeEventListener("trail-toolcall-approved", onToolCallApproved);
|
|
2423
|
-
};
|
|
2424
|
-
}
|
|
2425
|
-
var AdaptiveChatBarMountable = {
|
|
2426
|
-
mount(container, mountConfig) {
|
|
2427
|
-
const cfg2 = mountConfig ?? {};
|
|
2428
|
-
configureTransportIfPossible(cfg2);
|
|
2429
|
-
bootSupportChat(cfg2.supportChat);
|
|
2430
|
-
wireDiveDeeperListener();
|
|
2431
|
-
const bar = document.createElement("adaptive-chat-bar");
|
|
2432
|
-
applyPlaceholder(bar, cfg2);
|
|
2433
|
-
const widgets = cfg2.runtime?.widgets;
|
|
2434
|
-
if (widgets) {
|
|
2435
|
-
bar.mountInlineWidget = (widgetId, container2, props) => {
|
|
2436
|
-
if (!widgets.has(widgetId)) return null;
|
|
2437
|
-
const handle = widgets.mount(widgetId, container2, props);
|
|
2438
|
-
const element = container2.firstElementChild;
|
|
2439
|
-
if (!element) {
|
|
2440
|
-
handle.unmount?.();
|
|
2441
|
-
return null;
|
|
2442
|
-
}
|
|
2443
|
-
return { element, cleanup: () => handle.unmount?.() };
|
|
2444
|
-
};
|
|
2445
|
-
}
|
|
2446
|
-
const unsubSession = chatSession.subscribe((s) => {
|
|
2447
|
-
bar.messages = [...s.messages];
|
|
2448
|
-
bar.inFlight = s.inFlight;
|
|
2449
|
-
bar.thinkingText = s.thinkingText;
|
|
2450
|
-
});
|
|
2451
|
-
const runtimeEvents = cfg2.runtime ? cfg2.runtime.events : void 0;
|
|
2452
|
-
const refreshReceipts = () => {
|
|
2453
|
-
const store = _elementStore;
|
|
2454
|
-
if (store) {
|
|
2455
|
-
bar.tileReceipts = store.listTileReceipts();
|
|
2456
|
-
bar.inlineWidgets = store.listInlineWidgets();
|
|
2457
|
-
}
|
|
2458
|
-
};
|
|
2459
|
-
let unsubReceipts;
|
|
2460
|
-
if (runtimeEvents?.subscribe) {
|
|
2461
|
-
unsubReceipts = runtimeEvents.subscribe(
|
|
2462
|
-
{ names: [TILE_MOUNTED_EVENT, TILE_UNMOUNTED_EVENT] },
|
|
2463
|
-
refreshReceipts
|
|
2464
|
-
);
|
|
2465
|
-
}
|
|
2466
|
-
refreshReceipts();
|
|
2467
|
-
const state = { bar, cleanup: () => {
|
|
2468
|
-
}, cfg: cfg2 };
|
|
2469
|
-
const unwireListeners = wireListeners(bar, state);
|
|
2470
|
-
container.appendChild(bar);
|
|
2471
|
-
const unsubFallback = chatTransport.onFallback(() => {
|
|
2472
|
-
bar.remove();
|
|
2473
|
-
container.innerHTML = renderFallbackHtml(state.cfg.fallback);
|
|
2474
|
-
});
|
|
2475
|
-
state.cleanup = () => {
|
|
2476
|
-
unsubSession();
|
|
2477
|
-
unsubFallback();
|
|
2478
|
-
unsubReceipts?.();
|
|
2479
|
-
unwireListeners();
|
|
2480
|
-
bar.remove();
|
|
2481
|
-
setState(container, null);
|
|
2482
|
-
};
|
|
2483
|
-
setState(container, state);
|
|
2484
|
-
return state.cleanup;
|
|
2485
|
-
},
|
|
2486
|
-
update(container, mountConfig) {
|
|
2487
|
-
const state = getState(container);
|
|
2488
|
-
if (!state) return;
|
|
2489
|
-
const cfg2 = mountConfig ?? {};
|
|
2490
|
-
configureTransportIfPossible(cfg2);
|
|
2491
|
-
applyPlaceholder(state.bar, cfg2);
|
|
2492
|
-
state.cfg = cfg2;
|
|
2493
|
-
}
|
|
2494
|
-
};
|
|
2495
|
-
|
|
2496
|
-
// src/AdaptiveChipsStripMountable.ts
|
|
2497
|
-
var STATE_KEY2 = "__syntroChipsStripMount";
|
|
2498
|
-
function getState2(container) {
|
|
2499
|
-
return container[STATE_KEY2] ?? null;
|
|
2500
|
-
}
|
|
2501
|
-
function setState2(container, state) {
|
|
2502
|
-
container[STATE_KEY2] = state;
|
|
2503
|
-
}
|
|
2504
|
-
function applyProps(strip, cfg2) {
|
|
2505
|
-
if (cfg2.chips !== void 0) strip.chips = cfg2.chips;
|
|
2506
|
-
if (cfg2.runtime !== void 0) {
|
|
2507
|
-
strip.runtimeRef = cfg2.runtime;
|
|
2508
|
-
}
|
|
2509
|
-
if (cfg2.chromeless !== void 0) strip.chromeless = cfg2.chromeless;
|
|
2510
|
-
}
|
|
2511
|
-
var AdaptiveChipsStripMountable = {
|
|
2512
|
-
mount(container, mountConfig) {
|
|
2513
|
-
const cfg2 = mountConfig ?? {};
|
|
2514
|
-
const strip = document.createElement("adaptive-chips-strip");
|
|
2515
|
-
applyProps(strip, cfg2);
|
|
2516
|
-
const state = {
|
|
2517
|
-
strip,
|
|
2518
|
-
listeners: {
|
|
2519
|
-
revealed: (e) => {
|
|
2520
|
-
const cb = cfg2.onChipRevealed;
|
|
2521
|
-
if (cb) {
|
|
2522
|
-
cb(
|
|
2523
|
-
e.detail
|
|
2524
|
-
);
|
|
2525
|
-
}
|
|
2526
|
-
},
|
|
2527
|
-
dismissed: (e) => {
|
|
2528
|
-
const cb = cfg2.onChipDismissed;
|
|
2529
|
-
if (cb) cb(e.detail);
|
|
2530
|
-
}
|
|
2531
|
-
}
|
|
2532
|
-
};
|
|
2533
|
-
strip.addEventListener("chip-revealed", state.listeners.revealed);
|
|
2534
|
-
strip.addEventListener("chip-dismissed", state.listeners.dismissed);
|
|
2535
|
-
container.appendChild(strip);
|
|
2536
|
-
setState2(container, state);
|
|
2537
|
-
return () => {
|
|
2538
|
-
strip.removeEventListener("chip-revealed", state.listeners.revealed);
|
|
2539
|
-
strip.removeEventListener("chip-dismissed", state.listeners.dismissed);
|
|
2540
|
-
strip.remove();
|
|
2541
|
-
setState2(container, null);
|
|
2542
|
-
};
|
|
2543
|
-
},
|
|
2544
|
-
update(container, mountConfig) {
|
|
2545
|
-
const state = getState2(container);
|
|
2546
|
-
if (!state) return;
|
|
2547
|
-
const cfg2 = mountConfig ?? {};
|
|
2548
|
-
applyProps(state.strip, cfg2);
|
|
2549
|
-
state.strip.removeEventListener("chip-revealed", state.listeners.revealed);
|
|
2550
|
-
state.strip.removeEventListener("chip-dismissed", state.listeners.dismissed);
|
|
2551
|
-
state.listeners.revealed = (e) => {
|
|
2552
|
-
const cb = cfg2.onChipRevealed;
|
|
2553
|
-
if (cb) {
|
|
2554
|
-
cb(e.detail);
|
|
2555
|
-
}
|
|
2556
|
-
};
|
|
2557
|
-
state.listeners.dismissed = (e) => {
|
|
2558
|
-
const cb = cfg2.onChipDismissed;
|
|
2559
|
-
if (cb) cb(e.detail);
|
|
2560
|
-
};
|
|
2561
|
-
state.strip.addEventListener("chip-revealed", state.listeners.revealed);
|
|
2562
|
-
state.strip.addEventListener("chip-dismissed", state.listeners.dismissed);
|
|
2563
|
-
}
|
|
2564
|
-
};
|
|
2565
2212
|
|
|
2566
2213
|
// src/LeaveAMessageLit.ts
|
|
2567
2214
|
import { html, LitElement, nothing } from "lit";
|
|
@@ -2706,8 +2353,14 @@ var trailHostStyles = {
|
|
|
2706
2353
|
width: "100%",
|
|
2707
2354
|
boxSizing: "border-box"
|
|
2708
2355
|
};
|
|
2709
|
-
var
|
|
2710
|
-
var
|
|
2356
|
+
var LEAVE_A_MESSAGE_PERSIST_NAMESPACE = "supportchat";
|
|
2357
|
+
var LEAVE_A_MESSAGE_PERSIST_KEY = "leave-a-message";
|
|
2358
|
+
var PERSIST_NAMESPACE = LEAVE_A_MESSAGE_PERSIST_NAMESPACE;
|
|
2359
|
+
var PERSIST_KEY = LEAVE_A_MESSAGE_PERSIST_KEY;
|
|
2360
|
+
var liveCards = /* @__PURE__ */ new Set();
|
|
2361
|
+
function liveLeaveAMessageCards() {
|
|
2362
|
+
return liveCards;
|
|
2363
|
+
}
|
|
2711
2364
|
var lamUidCounter = 0;
|
|
2712
2365
|
var LeaveAMessageLit = class extends LitElement {
|
|
2713
2366
|
constructor() {
|
|
@@ -2740,6 +2393,21 @@ var LeaveAMessageLit = class extends LitElement {
|
|
|
2740
2393
|
* 'flipped' branch). Seeded with the visitor's own compose-step message
|
|
2741
2394
|
* in `_submitMessage`. */
|
|
2742
2395
|
this._transcript = [];
|
|
2396
|
+
/**
|
|
2397
|
+
* Tap on the reply announcement: open the thread and ask the canvas to
|
|
2398
|
+
* bring this tile to the front. `tile-recall` is the same composed event
|
|
2399
|
+
* AdaptiveChatBar already raises for its own recalls — velvet cycles the
|
|
2400
|
+
* deck to the card and un-maximizes the chat; the host opens the canvas
|
|
2401
|
+
* if it was closed.
|
|
2402
|
+
*/
|
|
2403
|
+
this._recall = () => {
|
|
2404
|
+
this._flip();
|
|
2405
|
+
const instanceId = this._instanceId();
|
|
2406
|
+
if (!instanceId) return;
|
|
2407
|
+
this.dispatchEvent(
|
|
2408
|
+
new CustomEvent("tile-recall", { bubbles: true, composed: true, detail: { instanceId } })
|
|
2409
|
+
);
|
|
2410
|
+
};
|
|
2743
2411
|
/** Paint our own focus ring from the theme. The border now lives on the
|
|
2744
2412
|
* field SHELL (`data-lam-field-wrap`), not the borderless inner
|
|
2745
2413
|
* textarea/input, so the ring targets that ancestor via `closest()`. */
|
|
@@ -2756,10 +2424,10 @@ var LeaveAMessageLit = class extends LitElement {
|
|
|
2756
2424
|
};
|
|
2757
2425
|
this._onReply = (text) => {
|
|
2758
2426
|
this._transcript = [...this._transcript, { role: "operator", text }];
|
|
2427
|
+
const reading = this.state === "flipped" && this._isOnScreen();
|
|
2759
2428
|
if (this.state !== "flipped") {
|
|
2760
2429
|
this.state = "mail";
|
|
2761
2430
|
this.runtime?.events.publish("supportchat:reply_received", { text });
|
|
2762
|
-
this._toastReplyArrived();
|
|
2763
2431
|
this.dispatchEvent(
|
|
2764
2432
|
new CustomEvent("syntro:supportchat-reply", {
|
|
2765
2433
|
bubbles: true,
|
|
@@ -2768,6 +2436,7 @@ var LeaveAMessageLit = class extends LitElement {
|
|
|
2768
2436
|
})
|
|
2769
2437
|
);
|
|
2770
2438
|
}
|
|
2439
|
+
if (!reading) this._announceReply();
|
|
2771
2440
|
this._persist();
|
|
2772
2441
|
this.requestUpdate();
|
|
2773
2442
|
};
|
|
@@ -2811,6 +2480,8 @@ var LeaveAMessageLit = class extends LitElement {
|
|
|
2811
2480
|
this._flip = () => {
|
|
2812
2481
|
this.state = "flipped";
|
|
2813
2482
|
this._touched = false;
|
|
2483
|
+
this._noticeHandle?.dismiss();
|
|
2484
|
+
this._noticeHandle = void 0;
|
|
2814
2485
|
this._persist();
|
|
2815
2486
|
};
|
|
2816
2487
|
this._sendReply = () => {
|
|
@@ -2859,6 +2530,10 @@ var LeaveAMessageLit = class extends LitElement {
|
|
|
2859
2530
|
super.connectedCallback();
|
|
2860
2531
|
this._loadPersisted();
|
|
2861
2532
|
this.addEventListener("keydown", this._onKeyDown);
|
|
2533
|
+
liveCards.add(this);
|
|
2534
|
+
if (this.bridge) {
|
|
2535
|
+
this._readyUnsub = onSupportChatReady(this.bridge, () => this.requestUpdate());
|
|
2536
|
+
}
|
|
2862
2537
|
}
|
|
2863
2538
|
updated() {
|
|
2864
2539
|
if (this.state !== "form" && this.bridge && this.bridge.detect() && !this._unsubscribe) {
|
|
@@ -2867,13 +2542,36 @@ var LeaveAMessageLit = class extends LitElement {
|
|
|
2867
2542
|
}
|
|
2868
2543
|
disconnectedCallback() {
|
|
2869
2544
|
this.removeEventListener("keydown", this._onKeyDown);
|
|
2545
|
+
liveCards.delete(this);
|
|
2546
|
+
this._readyUnsub?.();
|
|
2547
|
+
this._readyUnsub = void 0;
|
|
2870
2548
|
this._unsubscribe?.();
|
|
2871
2549
|
this._unsubscribe = void 0;
|
|
2550
|
+
this._noticeHandle?.dismiss();
|
|
2551
|
+
this._noticeHandle = void 0;
|
|
2872
2552
|
super.disconnectedCallback();
|
|
2873
2553
|
}
|
|
2554
|
+
/** Is this card's own vendor subscription delivering replies? While it
|
|
2555
|
+
* is, the page-level inbox leaves every message to the card. */
|
|
2556
|
+
get isListening() {
|
|
2557
|
+
return this._unsubscribe !== void 0;
|
|
2558
|
+
}
|
|
2559
|
+
/** Hand the card a team message that arrived through the page-level
|
|
2560
|
+
* inbox (support-chat/inbox.ts) while the card was mounted but not yet
|
|
2561
|
+
* listening — a compose-step card the concierge put up moments before
|
|
2562
|
+
* the team's own message landed. */
|
|
2563
|
+
receiveOperatorMessage(text) {
|
|
2564
|
+
this._onReply(text);
|
|
2565
|
+
}
|
|
2566
|
+
/** Open the thread face — what tapping the mail face or an announcement
|
|
2567
|
+
* does. Used by the page-level inbox when its announcement is tapped
|
|
2568
|
+
* after a card has already mounted and hydrated the thread. */
|
|
2569
|
+
showThread() {
|
|
2570
|
+
this._flip();
|
|
2571
|
+
}
|
|
2874
2572
|
/** Read the injected persistence store, if any (I2). */
|
|
2875
2573
|
_persistStore() {
|
|
2876
|
-
return this.runtime?.state?.
|
|
2574
|
+
return this.runtime?.state?.ns?.(PERSIST_NAMESPACE);
|
|
2877
2575
|
}
|
|
2878
2576
|
/** Hydrate {state, transcript, email} from a prior page's mount. */
|
|
2879
2577
|
_loadPersisted() {
|
|
@@ -2897,30 +2595,67 @@ var LeaveAMessageLit = class extends LitElement {
|
|
|
2897
2595
|
store.set(PERSIST_KEY, snapshot);
|
|
2898
2596
|
}
|
|
2899
2597
|
/**
|
|
2900
|
-
*
|
|
2598
|
+
* Is the visitor LOOKING at this card right now? True only when the card
|
|
2599
|
+
* has a laid-out box inside the viewport, the document is visible, and a
|
|
2600
|
+
* hit-test at the card's center lands on the card itself — so a deck card
|
|
2601
|
+
* stacked behind another, a card under a maximized chat, or a closed
|
|
2602
|
+
* canvas all read as "not on screen". The hit-test descends through open
|
|
2603
|
+
* shadow roots (`document.elementFromPoint` stops at the outermost host).
|
|
2604
|
+
* Canvas-agnostic on purpose: the card never asks a canvas which tile is
|
|
2605
|
+
* front. Anything this can't evaluate (jsdom, no layout) is "not on
|
|
2606
|
+
* screen": when unsure, announce — a missed reply costs more than an
|
|
2607
|
+
* extra toast.
|
|
2608
|
+
*/
|
|
2609
|
+
_isOnScreen() {
|
|
2610
|
+
if (typeof document === "undefined" || typeof window === "undefined") return false;
|
|
2611
|
+
if (document.visibilityState !== "visible") return false;
|
|
2612
|
+
const r = this.getBoundingClientRect();
|
|
2613
|
+
if (r.width <= 0 || r.height <= 0) return false;
|
|
2614
|
+
const cx = r.left + r.width / 2;
|
|
2615
|
+
const cy = r.top + r.height / 2;
|
|
2616
|
+
if (cx < 0 || cy < 0 || cx > window.innerWidth || cy > window.innerHeight) return false;
|
|
2617
|
+
if (typeof document.elementFromPoint !== "function") return false;
|
|
2618
|
+
let hit = document.elementFromPoint(cx, cy);
|
|
2619
|
+
while (hit?.shadowRoot && typeof hit.shadowRoot.elementFromPoint === "function") {
|
|
2620
|
+
const inner = hit.shadowRoot.elementFromPoint(cx, cy);
|
|
2621
|
+
if (!inner || inner === hit) break;
|
|
2622
|
+
hit = inner;
|
|
2623
|
+
}
|
|
2624
|
+
let node = hit;
|
|
2625
|
+
while (node) {
|
|
2626
|
+
if (node === this) return true;
|
|
2627
|
+
node = node.parentNode ?? node.host ?? null;
|
|
2628
|
+
}
|
|
2629
|
+
return false;
|
|
2630
|
+
}
|
|
2631
|
+
/** The tile id sc-mount stamps on every widget's container, so the card can
|
|
2632
|
+
* name itself to the canvas when the visitor taps its announcement. */
|
|
2633
|
+
_instanceId() {
|
|
2634
|
+
return this.closest("[data-tile-id]")?.getAttribute("data-tile-id") ?? void 0;
|
|
2635
|
+
}
|
|
2636
|
+
/**
|
|
2637
|
+
* Announce a reply through the canvas element. Pierces shadow roots to
|
|
2901
2638
|
* find `<smart-canvas>` (same resolution AdaptiveChatBarMountable uses), and
|
|
2902
2639
|
* no-ops when no canvas is present — the tile's own "You got mail!" face is
|
|
2903
|
-
* always the durable signal.
|
|
2640
|
+
* always the durable signal. The announcement is PERSISTENT: a human's
|
|
2641
|
+
* reply is the one thing the visitor was waiting on, so it stays until
|
|
2642
|
+
* tapped or until the visitor opens the thread themselves (`_flip`).
|
|
2904
2643
|
*/
|
|
2905
|
-
|
|
2644
|
+
_announceReply() {
|
|
2906
2645
|
if (typeof document === "undefined") return;
|
|
2907
2646
|
const title = this.replyToast?.title?.trim();
|
|
2908
2647
|
if (!title) return;
|
|
2909
|
-
const find = (root) => {
|
|
2910
|
-
const direct = root.querySelector("smart-canvas");
|
|
2911
|
-
if (direct) return direct;
|
|
2912
|
-
for (const el of root.querySelectorAll("*")) {
|
|
2913
|
-
const sr = el.shadowRoot;
|
|
2914
|
-
if (sr) {
|
|
2915
|
-
const found = find(sr);
|
|
2916
|
-
if (found) return found;
|
|
2917
|
-
}
|
|
2918
|
-
}
|
|
2919
|
-
return null;
|
|
2920
|
-
};
|
|
2921
2648
|
try {
|
|
2922
|
-
const canvas =
|
|
2923
|
-
|
|
2649
|
+
const canvas = findSmartCanvas();
|
|
2650
|
+
this._noticeHandle?.dismiss();
|
|
2651
|
+
this._noticeHandle = void 0;
|
|
2652
|
+
const handle = canvas?.previewNotification?.({
|
|
2653
|
+
title,
|
|
2654
|
+
body: this.replyToast?.body,
|
|
2655
|
+
persistent: true,
|
|
2656
|
+
onTap: this._recall
|
|
2657
|
+
});
|
|
2658
|
+
if (handle && typeof handle.dismiss === "function") this._noticeHandle = handle;
|
|
2924
2659
|
} catch (err) {
|
|
2925
2660
|
console.warn("supportChat.widget: reply toast failed", err);
|
|
2926
2661
|
}
|
|
@@ -3109,6 +2844,544 @@ function registerLeaveAMessageLit() {
|
|
|
3109
2844
|
}
|
|
3110
2845
|
registerLeaveAMessageLit();
|
|
3111
2846
|
|
|
2847
|
+
// src/support-chat/inbox.ts
|
|
2848
|
+
var LEAVE_A_MESSAGE_WIDGET_ID = "adaptive-chatbot:leave-a-message";
|
|
2849
|
+
var SUPPORT_CHAT_INBOX_INSTANCE_ID = "supportchat-inbox";
|
|
2850
|
+
function findLeaveAMessageTemplate(uiTemplates) {
|
|
2851
|
+
const tiles = uiTemplates?.tiles;
|
|
2852
|
+
if (!Array.isArray(tiles)) return void 0;
|
|
2853
|
+
const t = tiles.find((x) => x?.widget === LEAVE_A_MESSAGE_WIDGET_ID);
|
|
2854
|
+
if (!t) return void 0;
|
|
2855
|
+
const slot = typeof t.default_slot === "string" && t.default_slot ? t.default_slot : "drawer";
|
|
2856
|
+
const props = t.default_widget_props && typeof t.default_widget_props === "object" ? t.default_widget_props : {};
|
|
2857
|
+
return { slot, props };
|
|
2858
|
+
}
|
|
2859
|
+
var _wired;
|
|
2860
|
+
function wireSupportChatInbox(opts) {
|
|
2861
|
+
const { bridge, inbox, runtime: runtime2, uiTemplates } = opts;
|
|
2862
|
+
const store = runtime2?.state?.ns?.(LEAVE_A_MESSAGE_PERSIST_NAMESPACE);
|
|
2863
|
+
let replyUnsub;
|
|
2864
|
+
let mountUnsub;
|
|
2865
|
+
let unmountUnsub;
|
|
2866
|
+
let notice;
|
|
2867
|
+
let localSlot;
|
|
2868
|
+
const readThread = () => store?.get(LEAVE_A_MESSAGE_PERSIST_KEY) ?? {};
|
|
2869
|
+
const openThread = () => {
|
|
2870
|
+
const thread = readThread();
|
|
2871
|
+
store?.set(LEAVE_A_MESSAGE_PERSIST_KEY, { ...thread, state: "flipped" });
|
|
2872
|
+
notice = void 0;
|
|
2873
|
+
const live = liveLeaveAMessageCards().values().next().value;
|
|
2874
|
+
if (live) {
|
|
2875
|
+
live.showThread();
|
|
2876
|
+
return;
|
|
2877
|
+
}
|
|
2878
|
+
const template = findLeaveAMessageTemplate(uiTemplates);
|
|
2879
|
+
if (!template || !inbox || !runtime2) {
|
|
2880
|
+
console.warn(
|
|
2881
|
+
`supportChat.inbox: cannot open the thread \u2014 uiTemplates declares no ${LEAVE_A_MESSAGE_WIDGET_ID} tile for this workspace`
|
|
2882
|
+
);
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2885
|
+
const props = {
|
|
2886
|
+
slot: template.slot,
|
|
2887
|
+
instance_id: SUPPORT_CHAT_INBOX_INSTANCE_ID,
|
|
2888
|
+
widget: LEAVE_A_MESSAGE_WIDGET_ID,
|
|
2889
|
+
props: { ...template.props, title: inbox.title, message: "", context_summary: "" }
|
|
2890
|
+
};
|
|
2891
|
+
localSlot = template.slot;
|
|
2892
|
+
runtime2.events.publish(TILE_MOUNTED_EVENT, props);
|
|
2893
|
+
};
|
|
2894
|
+
const onMessage = (text) => {
|
|
2895
|
+
const card = liveLeaveAMessageCards().values().next().value;
|
|
2896
|
+
if (card) {
|
|
2897
|
+
if (!card.isListening) card.receiveOperatorMessage(text);
|
|
2898
|
+
return;
|
|
2899
|
+
}
|
|
2900
|
+
if (!inbox) {
|
|
2901
|
+
console.warn(
|
|
2902
|
+
`supportChat.inbox: a '${bridge.vendor}' team message arrived with no handoff card mounted and no supportChat.inbox authored \u2014 dropped`
|
|
2903
|
+
);
|
|
2904
|
+
return;
|
|
2905
|
+
}
|
|
2906
|
+
const thread = readThread();
|
|
2907
|
+
store?.set(LEAVE_A_MESSAGE_PERSIST_KEY, {
|
|
2908
|
+
...thread,
|
|
2909
|
+
state: thread.state === "flipped" ? "flipped" : "mail",
|
|
2910
|
+
transcript: [...thread.transcript ?? [], { role: "operator", text }]
|
|
2911
|
+
});
|
|
2912
|
+
const canvas = findSmartCanvas();
|
|
2913
|
+
notice?.dismiss();
|
|
2914
|
+
notice = void 0;
|
|
2915
|
+
const handle = canvas?.previewNotification?.({
|
|
2916
|
+
title: inbox.toastTitle,
|
|
2917
|
+
body: text,
|
|
2918
|
+
persistent: true,
|
|
2919
|
+
onTap: openThread
|
|
2920
|
+
});
|
|
2921
|
+
if (handle && typeof handle.dismiss === "function") notice = handle;
|
|
2922
|
+
};
|
|
2923
|
+
const readyUnsub = onSupportChatReady(bridge, () => {
|
|
2924
|
+
replyUnsub = bridge.onOperatorReply(onMessage);
|
|
2925
|
+
});
|
|
2926
|
+
if (runtime2?.events.subscribe) {
|
|
2927
|
+
mountUnsub = runtime2.events.subscribe({ names: [TILE_MOUNTED_EVENT] }, (e) => {
|
|
2928
|
+
const p = e.props;
|
|
2929
|
+
if (!localSlot || p?.widget !== LEAVE_A_MESSAGE_WIDGET_ID) return;
|
|
2930
|
+
if (p.instance_id === SUPPORT_CHAT_INBOX_INSTANCE_ID) return;
|
|
2931
|
+
const unmount = {
|
|
2932
|
+
slot: localSlot,
|
|
2933
|
+
instance_id: SUPPORT_CHAT_INBOX_INSTANCE_ID
|
|
2934
|
+
};
|
|
2935
|
+
localSlot = void 0;
|
|
2936
|
+
runtime2.events.publish(TILE_UNMOUNTED_EVENT, unmount);
|
|
2937
|
+
});
|
|
2938
|
+
unmountUnsub = runtime2.events.subscribe({ names: [TILE_UNMOUNTED_EVENT] }, (e) => {
|
|
2939
|
+
const p = e.props;
|
|
2940
|
+
if (p?.instance_id === SUPPORT_CHAT_INBOX_INSTANCE_ID) localSlot = void 0;
|
|
2941
|
+
});
|
|
2942
|
+
}
|
|
2943
|
+
const cleanup = () => {
|
|
2944
|
+
readyUnsub();
|
|
2945
|
+
replyUnsub?.();
|
|
2946
|
+
replyUnsub = void 0;
|
|
2947
|
+
mountUnsub?.();
|
|
2948
|
+
mountUnsub = void 0;
|
|
2949
|
+
unmountUnsub?.();
|
|
2950
|
+
unmountUnsub = void 0;
|
|
2951
|
+
notice?.dismiss();
|
|
2952
|
+
notice = void 0;
|
|
2953
|
+
if (_wired === cleanup) _wired = void 0;
|
|
2954
|
+
};
|
|
2955
|
+
_wired = cleanup;
|
|
2956
|
+
return cleanup;
|
|
2957
|
+
}
|
|
2958
|
+
function isSupportChatInboxWired() {
|
|
2959
|
+
return _wired !== void 0;
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
// src/AdaptiveChatBarMountable.ts
|
|
2963
|
+
var STATE_KEY = "__syntroChatBarMount";
|
|
2964
|
+
function getState(container) {
|
|
2965
|
+
return container[STATE_KEY] ?? null;
|
|
2966
|
+
}
|
|
2967
|
+
function setState(container, state) {
|
|
2968
|
+
container[STATE_KEY] = state;
|
|
2969
|
+
}
|
|
2970
|
+
function applyPlaceholder(bar, cfg2) {
|
|
2971
|
+
if (cfg2.placeholder !== void 0) bar.placeholder = cfg2.placeholder;
|
|
2972
|
+
if (cfg2.assistantName !== void 0) bar.assistantName = cfg2.assistantName;
|
|
2973
|
+
if (cfg2.personas !== void 0) bar.personas = cfg2.personas;
|
|
2974
|
+
if (cfg2.greeting !== void 0) bar.greeting = cfg2.greeting;
|
|
2975
|
+
if (cfg2.voiceInput !== void 0) bar.voiceInput = cfg2.voiceInput;
|
|
2976
|
+
bar.introSuggestion = cfg2.introSuggestion;
|
|
2977
|
+
bar.forceExpanded = cfg2.forceExpanded === true;
|
|
2978
|
+
if (typeof cfg2.maximized === "boolean") {
|
|
2979
|
+
bar.maximized = cfg2.maximized;
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
var _elementStore = null;
|
|
2983
|
+
var _templateWidgetMap = /* @__PURE__ */ new Map();
|
|
2984
|
+
var SERVER_MOUNTED_TILE_WIDGETS = ["syntro-product-reco-carousel"];
|
|
2985
|
+
function resolveTileWidget(templateId) {
|
|
2986
|
+
return _templateWidgetMap.get(templateId);
|
|
2987
|
+
}
|
|
2988
|
+
function refreshTemplateWidgetMap(uiTemplates) {
|
|
2989
|
+
_templateWidgetMap.clear();
|
|
2990
|
+
for (const tag of SERVER_MOUNTED_TILE_WIDGETS) {
|
|
2991
|
+
_templateWidgetMap.set(tag, tag);
|
|
2992
|
+
}
|
|
2993
|
+
if (!uiTemplates) return;
|
|
2994
|
+
const tiles = uiTemplates.tiles;
|
|
2995
|
+
if (!Array.isArray(tiles)) return;
|
|
2996
|
+
for (const t of tiles) {
|
|
2997
|
+
const id = typeof t?.id === "string" ? t.id : null;
|
|
2998
|
+
const widget = typeof t?.widget === "string" ? t.widget : null;
|
|
2999
|
+
if (id && widget) _templateWidgetMap.set(id, widget);
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
function getOrCreateElementStore(runtime2) {
|
|
3003
|
+
if (_elementStore) return _elementStore;
|
|
3004
|
+
_elementStore = new ElementInstanceStore({
|
|
3005
|
+
actions: runtime2.actions,
|
|
3006
|
+
// Pass the runtime's event bus so ItemHandler can broadcast
|
|
3007
|
+
// `element.compositional_append` / `_patch` / `_remove` events
|
|
3008
|
+
// to container widgets (chips strip, FAQ accordion, nav tips).
|
|
3009
|
+
events: {
|
|
3010
|
+
publish: runtime2.events.publish.bind(runtime2.events),
|
|
3011
|
+
subscribe: runtime2.events.subscribe?.bind(runtime2.events)
|
|
3012
|
+
},
|
|
3013
|
+
handlers: [new TileHandler(resolveTileWidget), new ActionHandler(), new ItemHandler()],
|
|
3014
|
+
resolveTileWidget
|
|
3015
|
+
});
|
|
3016
|
+
setElementInstanceStore(_elementStore);
|
|
3017
|
+
return _elementStore;
|
|
3018
|
+
}
|
|
3019
|
+
var _hydrationStarted = false;
|
|
3020
|
+
function hydrateOnce(runtime2, backendUrl) {
|
|
3021
|
+
if (_hydrationStarted) return;
|
|
3022
|
+
_hydrationStarted = true;
|
|
3023
|
+
const store = getOrCreateElementStore(runtime2);
|
|
3024
|
+
const trimmed = backendUrl.replace(/\/$/, "");
|
|
3025
|
+
const endpoint = trimmed ? `${trimmed}/api/adaptive/mounted_elements` : "/api/adaptive/mounted_elements";
|
|
3026
|
+
fetchMountedElements({ endpoint, token: runtime2.token }).then((response) => {
|
|
3027
|
+
if (!response) return;
|
|
3028
|
+
void store.hydrate(response.mounted_elements);
|
|
3029
|
+
});
|
|
3030
|
+
}
|
|
3031
|
+
var PAGE_LEVEL_KINDS = /* @__PURE__ */ new Set(["view", "scroll", "idle", "hesitation", "hover", "nav"]);
|
|
3032
|
+
function buildObserverEnrich(runtimeRef) {
|
|
3033
|
+
return (ev, raw) => {
|
|
3034
|
+
const extract = runtimeRef.getProductIdentity;
|
|
3035
|
+
if (typeof extract !== "function") return ev;
|
|
3036
|
+
if (ev.kind === "click") {
|
|
3037
|
+
const chain = raw.properties.$elements;
|
|
3038
|
+
if (!Array.isArray(chain)) return ev;
|
|
3039
|
+
const id = extract(chain);
|
|
3040
|
+
if (!id) return ev;
|
|
3041
|
+
return {
|
|
3042
|
+
...ev,
|
|
3043
|
+
product: {
|
|
3044
|
+
handle: id.productHandle,
|
|
3045
|
+
url: id.productUrl,
|
|
3046
|
+
...id.productTitle ? { title: id.productTitle } : {}
|
|
3047
|
+
}
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
if (!ev.product && PAGE_LEVEL_KINDS.has(ev.kind) && typeof location !== "undefined") {
|
|
3051
|
+
const id = extract([{ href: location.pathname }]);
|
|
3052
|
+
if (!id) return ev;
|
|
3053
|
+
return {
|
|
3054
|
+
...ev,
|
|
3055
|
+
product: {
|
|
3056
|
+
handle: id.productHandle,
|
|
3057
|
+
url: id.productUrl,
|
|
3058
|
+
...id.productTitle ? { title: id.productTitle } : {}
|
|
3059
|
+
}
|
|
3060
|
+
};
|
|
3061
|
+
}
|
|
3062
|
+
return ev;
|
|
3063
|
+
};
|
|
3064
|
+
}
|
|
3065
|
+
var _nudgeSurfaceWired = false;
|
|
3066
|
+
var _nudgeListener = null;
|
|
3067
|
+
function wireNudgeSurface(runtime2, backendUrl) {
|
|
3068
|
+
if (_nudgeSurfaceWired || typeof document === "undefined") return;
|
|
3069
|
+
_nudgeSurfaceWired = true;
|
|
3070
|
+
_nudgeListener = (e) => {
|
|
3071
|
+
try {
|
|
3072
|
+
const detail = e.detail;
|
|
3073
|
+
const isIntent = detail?.phase === "intent";
|
|
3074
|
+
if (!isIntent) {
|
|
3075
|
+
const store = getOrCreateElementStore(runtime2);
|
|
3076
|
+
const trimmed = backendUrl.replace(/\/$/, "");
|
|
3077
|
+
const endpoint = trimmed ? `${trimmed}/api/adaptive/mounted_elements` : "/api/adaptive/mounted_elements";
|
|
3078
|
+
void fetchMountedElements({ endpoint, token: runtime2.token }).then((response) => {
|
|
3079
|
+
if (!response) return;
|
|
3080
|
+
void store.hydrate(response.mounted_elements);
|
|
3081
|
+
});
|
|
3082
|
+
}
|
|
3083
|
+
if (detail?.suppressToast === true) return;
|
|
3084
|
+
const canvas = findSmartCanvas();
|
|
3085
|
+
const messageId = e.detail?.messageId;
|
|
3086
|
+
const nudgeText = messageId ? (chatSession.getState().messages.find((m) => m.id === messageId)?.text ?? "").trim() : "";
|
|
3087
|
+
const title = nudgeText ? nudgeText.length > 140 ? `${nudgeText.slice(0, 137)}\u2026` : nudgeText : "I put something together for you";
|
|
3088
|
+
canvas?.previewNotification?.({
|
|
3089
|
+
title,
|
|
3090
|
+
body: "Tap to take a look"
|
|
3091
|
+
});
|
|
3092
|
+
} catch (err) {
|
|
3093
|
+
console.warn("[adaptive-chatbot] nudge surface reaction failed", err);
|
|
3094
|
+
}
|
|
3095
|
+
};
|
|
3096
|
+
document.addEventListener("syntro:nudge", _nudgeListener);
|
|
3097
|
+
}
|
|
3098
|
+
function configureTransportIfPossible(cfg2) {
|
|
3099
|
+
if (!cfg2.runtime) return;
|
|
3100
|
+
const backendUrl = cfg2.runtime.backendUrl;
|
|
3101
|
+
if (!backendUrl) return;
|
|
3102
|
+
const elementsActive = cfg2.elementsEnabled === true && cfg2.uiTemplates != null;
|
|
3103
|
+
refreshTemplateWidgetMap(elementsActive ? cfg2.uiTemplates : void 0);
|
|
3104
|
+
const forwardedProps = elementsActive ? { elementsEnabled: true, uiTemplates: cfg2.uiTemplates } : void 0;
|
|
3105
|
+
if (elementsActive) publishTemplatesIfPossible(backendUrl, cfg2.uiTemplates, cfg2.runtime?.token);
|
|
3106
|
+
const runtime2 = cfg2.runtime;
|
|
3107
|
+
const onElementMutation = elementsActive ? (mutations) => {
|
|
3108
|
+
void getOrCreateElementStore(runtime2).apply(mutations);
|
|
3109
|
+
} : void 0;
|
|
3110
|
+
if (elementsActive) {
|
|
3111
|
+
hydrateOnce(runtime2, backendUrl);
|
|
3112
|
+
}
|
|
3113
|
+
const persona = pickPersona(cfg2.personas ?? null);
|
|
3114
|
+
chatTransport.configure({
|
|
3115
|
+
...cfg2,
|
|
3116
|
+
backendUrl,
|
|
3117
|
+
forwardedProps,
|
|
3118
|
+
onElementMutation,
|
|
3119
|
+
activeLidSlot: cfg2._syntroSlotName,
|
|
3120
|
+
personaId: persona?.id,
|
|
3121
|
+
personaName: persona?.name,
|
|
3122
|
+
personaRole: persona?.role_title
|
|
3123
|
+
});
|
|
3124
|
+
void chatTransport.startSessionStream();
|
|
3125
|
+
wireNudgeSurface(runtime2, backendUrl);
|
|
3126
|
+
startObserverIfPossible(cfg2);
|
|
3127
|
+
}
|
|
3128
|
+
var _templatesPublished = false;
|
|
3129
|
+
function publishTemplatesIfPossible(backendUrl, uiTemplates, token) {
|
|
3130
|
+
if (_templatesPublished || typeof window === "undefined") return;
|
|
3131
|
+
if (!uiTemplates || typeof uiTemplates !== "object") return;
|
|
3132
|
+
_templatesPublished = true;
|
|
3133
|
+
const headers2 = { "Content-Type": "application/json" };
|
|
3134
|
+
if (token) headers2.Authorization = `Bearer ${token}`;
|
|
3135
|
+
const editorToken = readEditorToken();
|
|
3136
|
+
if (editorToken) headers2["X-Syntro-Editor-Token"] = editorToken;
|
|
3137
|
+
const trimmed = backendUrl.replace(/\/$/, "");
|
|
3138
|
+
void fetch(`${trimmed}/api/adaptive/templates`, {
|
|
3139
|
+
method: "POST",
|
|
3140
|
+
headers: headers2,
|
|
3141
|
+
credentials: "include",
|
|
3142
|
+
body: JSON.stringify({ ui_templates: uiTemplates })
|
|
3143
|
+
}).catch(() => {
|
|
3144
|
+
});
|
|
3145
|
+
}
|
|
3146
|
+
var _diveDeeperWired = false;
|
|
3147
|
+
function wireDiveDeeperListener() {
|
|
3148
|
+
if (_diveDeeperWired || typeof window === "undefined") return;
|
|
3149
|
+
_diveDeeperWired = true;
|
|
3150
|
+
window.addEventListener(DIVE_DEEPER_EVENT, (e) => {
|
|
3151
|
+
const detail = e.detail ?? {};
|
|
3152
|
+
const prompt = typeof detail.prompt === "string" && detail.prompt.trim() ? detail.prompt : detail.title ? "I'd like to dive deeper on this, I didn't see what I needed." : "";
|
|
3153
|
+
if (!prompt) return;
|
|
3154
|
+
const origin = { kind: "dive-deeper" };
|
|
3155
|
+
if (detail.title) origin.title = detail.title;
|
|
3156
|
+
if (detail.context) origin.context = detail.context;
|
|
3157
|
+
chatSession.send(prompt, { forwardedProps: { origin } });
|
|
3158
|
+
if (!chatSession.hasTransport()) {
|
|
3159
|
+
chatSession.error("Chat backend not configured \u2014 set backendUrl in the canvas config.");
|
|
3160
|
+
}
|
|
3161
|
+
});
|
|
3162
|
+
}
|
|
3163
|
+
var _observerStarted = false;
|
|
3164
|
+
var _observerRuntime = null;
|
|
3165
|
+
var _observerHandle = null;
|
|
3166
|
+
var _observerBusUnsubscribe = null;
|
|
3167
|
+
function startObserverIfPossible(cfg2) {
|
|
3168
|
+
if (typeof window === "undefined") return;
|
|
3169
|
+
const runtimeRef = cfg2.runtime;
|
|
3170
|
+
if (!runtimeRef?.events?.subscribe) return;
|
|
3171
|
+
if (_observerStarted && _observerRuntime === runtimeRef) return;
|
|
3172
|
+
if (_observerStarted) {
|
|
3173
|
+
_observerBusUnsubscribe?.();
|
|
3174
|
+
_observerHandle?.stop();
|
|
3175
|
+
_observerBusUnsubscribe = null;
|
|
3176
|
+
_observerHandle = null;
|
|
3177
|
+
_observerStarted = false;
|
|
3178
|
+
}
|
|
3179
|
+
const trimmed = (runtimeRef.backendUrl ?? "").replace(/\/$/, "");
|
|
3180
|
+
const url = trimmed ? `${trimmed}/api/adaptive/observation` : "/api/adaptive/observation";
|
|
3181
|
+
const runtimeId = (method) => {
|
|
3182
|
+
try {
|
|
3183
|
+
const v = runtimeRef.telemetry?.[method]?.();
|
|
3184
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
3185
|
+
} catch {
|
|
3186
|
+
return null;
|
|
3187
|
+
}
|
|
3188
|
+
};
|
|
3189
|
+
const getDistinctId = () => runtimeId("getDistinctId");
|
|
3190
|
+
const getSessionId = () => runtimeId("getSessionId");
|
|
3191
|
+
const token = () => {
|
|
3192
|
+
const t = runtimeRef.token;
|
|
3193
|
+
return typeof t === "string" ? t : "";
|
|
3194
|
+
};
|
|
3195
|
+
const enrich = buildObserverEnrich(runtimeRef);
|
|
3196
|
+
const handle = startObserver({ url, token, getDistinctId, getSessionId, enrich });
|
|
3197
|
+
window.__syntroObserverStats = () => handle.stats();
|
|
3198
|
+
const unsubscribe = runtimeRef.events.subscribe({}, (evt) => {
|
|
3199
|
+
const raw = busEventToRawEvent(evt);
|
|
3200
|
+
if (raw) handle.ingest(raw);
|
|
3201
|
+
});
|
|
3202
|
+
_observerRuntime = runtimeRef;
|
|
3203
|
+
_observerHandle = handle;
|
|
3204
|
+
_observerBusUnsubscribe = unsubscribe;
|
|
3205
|
+
_observerStarted = true;
|
|
3206
|
+
}
|
|
3207
|
+
function wireListeners(bar, state) {
|
|
3208
|
+
const onMessageSent = (e) => {
|
|
3209
|
+
const text = e.detail.text;
|
|
3210
|
+
chatSession.send(text, { activeLidSlot: state.cfg._syntroSlotName });
|
|
3211
|
+
if (!chatSession.hasTransport()) {
|
|
3212
|
+
chatSession.error("Chat backend not configured \u2014 set backendUrl in the canvas config.");
|
|
3213
|
+
}
|
|
3214
|
+
};
|
|
3215
|
+
const onInterrupt = () => {
|
|
3216
|
+
chatSession.interrupt();
|
|
3217
|
+
};
|
|
3218
|
+
const onToolCallApproved = (e) => {
|
|
3219
|
+
const detail = e.detail;
|
|
3220
|
+
chatSession.resolveToolCall(detail.toolCallId, {}, detail.approved);
|
|
3221
|
+
};
|
|
3222
|
+
const onClose = () => {
|
|
3223
|
+
state.cfg.onClose?.();
|
|
3224
|
+
};
|
|
3225
|
+
bar.addEventListener("chat-message-sent", onMessageSent);
|
|
3226
|
+
bar.addEventListener("chat-interrupt", onInterrupt);
|
|
3227
|
+
bar.addEventListener("canvas-close", onClose);
|
|
3228
|
+
bar.addEventListener("trail-toolcall-approved", onToolCallApproved);
|
|
3229
|
+
return () => {
|
|
3230
|
+
bar.removeEventListener("chat-message-sent", onMessageSent);
|
|
3231
|
+
bar.removeEventListener("chat-interrupt", onInterrupt);
|
|
3232
|
+
bar.removeEventListener("canvas-close", onClose);
|
|
3233
|
+
bar.removeEventListener("trail-toolcall-approved", onToolCallApproved);
|
|
3234
|
+
};
|
|
3235
|
+
}
|
|
3236
|
+
var AdaptiveChatBarMountable = {
|
|
3237
|
+
mount(container, mountConfig) {
|
|
3238
|
+
const cfg2 = mountConfig ?? {};
|
|
3239
|
+
configureTransportIfPossible(cfg2);
|
|
3240
|
+
const bridge = bootSupportChat(cfg2.supportChat);
|
|
3241
|
+
if (bridge && !isSupportChatInboxWired()) {
|
|
3242
|
+
wireSupportChatInbox({
|
|
3243
|
+
bridge,
|
|
3244
|
+
inbox: cfg2.supportChat?.inbox,
|
|
3245
|
+
runtime: cfg2.runtime,
|
|
3246
|
+
uiTemplates: cfg2.uiTemplates
|
|
3247
|
+
});
|
|
3248
|
+
}
|
|
3249
|
+
wireDiveDeeperListener();
|
|
3250
|
+
const bar = document.createElement("adaptive-chat-bar");
|
|
3251
|
+
applyPlaceholder(bar, cfg2);
|
|
3252
|
+
const widgets = cfg2.runtime?.widgets;
|
|
3253
|
+
if (widgets) {
|
|
3254
|
+
bar.mountInlineWidget = (widgetId, container2, props) => {
|
|
3255
|
+
if (!widgets.has(widgetId)) return null;
|
|
3256
|
+
const handle = widgets.mount(widgetId, container2, props);
|
|
3257
|
+
const element = container2.firstElementChild;
|
|
3258
|
+
if (!element) {
|
|
3259
|
+
handle.unmount?.();
|
|
3260
|
+
return null;
|
|
3261
|
+
}
|
|
3262
|
+
return { element, cleanup: () => handle.unmount?.() };
|
|
3263
|
+
};
|
|
3264
|
+
}
|
|
3265
|
+
const unsubSession = chatSession.subscribe((s) => {
|
|
3266
|
+
bar.messages = [...s.messages];
|
|
3267
|
+
bar.inFlight = s.inFlight;
|
|
3268
|
+
bar.thinkingText = s.thinkingText;
|
|
3269
|
+
});
|
|
3270
|
+
const runtimeEvents = cfg2.runtime ? cfg2.runtime.events : void 0;
|
|
3271
|
+
const refreshReceipts = () => {
|
|
3272
|
+
const store = _elementStore;
|
|
3273
|
+
if (store) {
|
|
3274
|
+
bar.tileReceipts = store.listTileReceipts();
|
|
3275
|
+
bar.inlineWidgets = store.listInlineWidgets();
|
|
3276
|
+
}
|
|
3277
|
+
};
|
|
3278
|
+
let unsubReceipts;
|
|
3279
|
+
if (runtimeEvents?.subscribe) {
|
|
3280
|
+
unsubReceipts = runtimeEvents.subscribe(
|
|
3281
|
+
{ names: [TILE_MOUNTED_EVENT, TILE_UNMOUNTED_EVENT] },
|
|
3282
|
+
refreshReceipts
|
|
3283
|
+
);
|
|
3284
|
+
}
|
|
3285
|
+
refreshReceipts();
|
|
3286
|
+
const state = { bar, cleanup: () => {
|
|
3287
|
+
}, cfg: cfg2 };
|
|
3288
|
+
const unwireListeners = wireListeners(bar, state);
|
|
3289
|
+
container.appendChild(bar);
|
|
3290
|
+
const unsubFallback = chatTransport.onFallback(() => {
|
|
3291
|
+
bar.remove();
|
|
3292
|
+
container.innerHTML = renderFallbackHtml(state.cfg.fallback);
|
|
3293
|
+
});
|
|
3294
|
+
state.cleanup = () => {
|
|
3295
|
+
unsubSession();
|
|
3296
|
+
unsubFallback();
|
|
3297
|
+
unsubReceipts?.();
|
|
3298
|
+
unwireListeners();
|
|
3299
|
+
bar.remove();
|
|
3300
|
+
setState(container, null);
|
|
3301
|
+
};
|
|
3302
|
+
setState(container, state);
|
|
3303
|
+
return state.cleanup;
|
|
3304
|
+
},
|
|
3305
|
+
update(container, mountConfig) {
|
|
3306
|
+
const state = getState(container);
|
|
3307
|
+
if (!state) return;
|
|
3308
|
+
const cfg2 = mountConfig ?? {};
|
|
3309
|
+
configureTransportIfPossible(cfg2);
|
|
3310
|
+
applyPlaceholder(state.bar, cfg2);
|
|
3311
|
+
state.cfg = cfg2;
|
|
3312
|
+
}
|
|
3313
|
+
};
|
|
3314
|
+
|
|
3315
|
+
// src/AdaptiveChipsStripMountable.ts
|
|
3316
|
+
var STATE_KEY2 = "__syntroChipsStripMount";
|
|
3317
|
+
function getState2(container) {
|
|
3318
|
+
return container[STATE_KEY2] ?? null;
|
|
3319
|
+
}
|
|
3320
|
+
function setState2(container, state) {
|
|
3321
|
+
container[STATE_KEY2] = state;
|
|
3322
|
+
}
|
|
3323
|
+
function applyProps(strip, cfg2) {
|
|
3324
|
+
if (cfg2.chips !== void 0) strip.chips = cfg2.chips;
|
|
3325
|
+
if (cfg2.runtime !== void 0) {
|
|
3326
|
+
strip.runtimeRef = cfg2.runtime;
|
|
3327
|
+
}
|
|
3328
|
+
if (cfg2.chromeless !== void 0) strip.chromeless = cfg2.chromeless;
|
|
3329
|
+
}
|
|
3330
|
+
var AdaptiveChipsStripMountable = {
|
|
3331
|
+
mount(container, mountConfig) {
|
|
3332
|
+
const cfg2 = mountConfig ?? {};
|
|
3333
|
+
const strip = document.createElement("adaptive-chips-strip");
|
|
3334
|
+
applyProps(strip, cfg2);
|
|
3335
|
+
const state = {
|
|
3336
|
+
strip,
|
|
3337
|
+
listeners: {
|
|
3338
|
+
revealed: (e) => {
|
|
3339
|
+
const cb = cfg2.onChipRevealed;
|
|
3340
|
+
if (cb) {
|
|
3341
|
+
cb(
|
|
3342
|
+
e.detail
|
|
3343
|
+
);
|
|
3344
|
+
}
|
|
3345
|
+
},
|
|
3346
|
+
dismissed: (e) => {
|
|
3347
|
+
const cb = cfg2.onChipDismissed;
|
|
3348
|
+
if (cb) cb(e.detail);
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
};
|
|
3352
|
+
strip.addEventListener("chip-revealed", state.listeners.revealed);
|
|
3353
|
+
strip.addEventListener("chip-dismissed", state.listeners.dismissed);
|
|
3354
|
+
container.appendChild(strip);
|
|
3355
|
+
setState2(container, state);
|
|
3356
|
+
return () => {
|
|
3357
|
+
strip.removeEventListener("chip-revealed", state.listeners.revealed);
|
|
3358
|
+
strip.removeEventListener("chip-dismissed", state.listeners.dismissed);
|
|
3359
|
+
strip.remove();
|
|
3360
|
+
setState2(container, null);
|
|
3361
|
+
};
|
|
3362
|
+
},
|
|
3363
|
+
update(container, mountConfig) {
|
|
3364
|
+
const state = getState2(container);
|
|
3365
|
+
if (!state) return;
|
|
3366
|
+
const cfg2 = mountConfig ?? {};
|
|
3367
|
+
applyProps(state.strip, cfg2);
|
|
3368
|
+
state.strip.removeEventListener("chip-revealed", state.listeners.revealed);
|
|
3369
|
+
state.strip.removeEventListener("chip-dismissed", state.listeners.dismissed);
|
|
3370
|
+
state.listeners.revealed = (e) => {
|
|
3371
|
+
const cb = cfg2.onChipRevealed;
|
|
3372
|
+
if (cb) {
|
|
3373
|
+
cb(e.detail);
|
|
3374
|
+
}
|
|
3375
|
+
};
|
|
3376
|
+
state.listeners.dismissed = (e) => {
|
|
3377
|
+
const cb = cfg2.onChipDismissed;
|
|
3378
|
+
if (cb) cb(e.detail);
|
|
3379
|
+
};
|
|
3380
|
+
state.strip.addEventListener("chip-revealed", state.listeners.revealed);
|
|
3381
|
+
state.strip.addEventListener("chip-dismissed", state.listeners.dismissed);
|
|
3382
|
+
}
|
|
3383
|
+
};
|
|
3384
|
+
|
|
3112
3385
|
// src/NavLinkMountable.ts
|
|
3113
3386
|
var STATE_KEY3 = "__syntroNavLinkMount";
|
|
3114
3387
|
var SAFE_NAVIGATION_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|