@oyehello/chat 0.3.0 → 0.4.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 +2 -0
- package/dist/index.cjs +108 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -3
- package/dist/index.d.ts +29 -3
- package/dist/index.global.js +116 -34
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +108 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -52,7 +52,19 @@ type ChatEvent = {
|
|
|
52
52
|
} | {
|
|
53
53
|
type: 'agent_typing';
|
|
54
54
|
active: boolean;
|
|
55
|
+
} | {
|
|
56
|
+
type: 'suggestions';
|
|
57
|
+
turnId: string;
|
|
58
|
+
items: string[];
|
|
55
59
|
};
|
|
60
|
+
interface WidgetConfig {
|
|
61
|
+
teaser: string;
|
|
62
|
+
brand: {
|
|
63
|
+
name: string | null;
|
|
64
|
+
logoUrl: string | null;
|
|
65
|
+
accentColor: string | null;
|
|
66
|
+
} | null;
|
|
67
|
+
}
|
|
56
68
|
interface OpenSessionResponse {
|
|
57
69
|
sessionId: string;
|
|
58
70
|
sessionToken: string;
|
|
@@ -116,6 +128,9 @@ type ChatClientEvent = {
|
|
|
116
128
|
} | {
|
|
117
129
|
type: 'agentTyping';
|
|
118
130
|
active: boolean;
|
|
131
|
+
} | {
|
|
132
|
+
type: 'suggestions';
|
|
133
|
+
items: string[];
|
|
119
134
|
};
|
|
120
135
|
type ChatToolHandler = (args: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
121
136
|
interface ChatClient {
|
|
@@ -127,6 +142,7 @@ interface ChatClient {
|
|
|
127
142
|
on(listener: (e: ChatClientEvent) => void): () => void;
|
|
128
143
|
registerTools(handlers: Record<string, ChatToolHandler>): void;
|
|
129
144
|
getAttachmentUrl(storageRef: string): Promise<string>;
|
|
145
|
+
getWidgetConfig(): Promise<WidgetConfig>;
|
|
130
146
|
}
|
|
131
147
|
declare function createChatClient(options: ChatClientOptions): ChatClient;
|
|
132
148
|
|
|
@@ -158,20 +174,30 @@ declare class OyehelloChatElement extends HTMLElement {
|
|
|
158
174
|
private renderedCount;
|
|
159
175
|
private renderedBotFinal;
|
|
160
176
|
private timers;
|
|
177
|
+
private serverSuggestions;
|
|
178
|
+
private teaserText;
|
|
179
|
+
private teaserShownOnce;
|
|
180
|
+
private idleTimer;
|
|
181
|
+
private idleReset;
|
|
161
182
|
private oc;
|
|
162
183
|
private bodyEl;
|
|
163
184
|
private textareaEl;
|
|
164
185
|
private fileEl;
|
|
165
186
|
private titleEl;
|
|
166
187
|
private statusEl;
|
|
167
|
-
private errEl;
|
|
168
188
|
private emojiListEl;
|
|
169
189
|
private emojiSearchEl;
|
|
170
190
|
connectedCallback(): void;
|
|
191
|
+
private armIdleTeaser;
|
|
192
|
+
private teardownIdle;
|
|
193
|
+
private loadWidgetConfig;
|
|
194
|
+
private applyTeaserText;
|
|
195
|
+
private applyBrandLogo;
|
|
171
196
|
disconnectedCallback(): void;
|
|
172
197
|
registerTools(handlers: Record<string, ChatToolHandler>): void;
|
|
173
198
|
private key;
|
|
174
|
-
private get
|
|
199
|
+
private get attrSuggestions();
|
|
200
|
+
private get chipLabels();
|
|
175
201
|
private renderShell;
|
|
176
202
|
private renderError;
|
|
177
203
|
private updateClasses;
|
|
@@ -200,4 +226,4 @@ declare class OyehelloChatElement extends HTMLElement {
|
|
|
200
226
|
private escapeAttr;
|
|
201
227
|
}
|
|
202
228
|
|
|
203
|
-
export { ChatApiError, type ChatAttachmentDescriptor, type ChatClient, type ChatClientEvent, type ChatClientOptions, type ChatClientState, type ChatEvent, type ChatMessage, type ChatToolHandler, type OpenSessionResponse, OyehelloChatElement, createChatClient };
|
|
229
|
+
export { ChatApiError, type ChatAttachmentDescriptor, type ChatClient, type ChatClientEvent, type ChatClientOptions, type ChatClientState, type ChatEvent, type ChatMessage, type ChatToolHandler, type OpenSessionResponse, OyehelloChatElement, type WidgetConfig, createChatClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -52,7 +52,19 @@ type ChatEvent = {
|
|
|
52
52
|
} | {
|
|
53
53
|
type: 'agent_typing';
|
|
54
54
|
active: boolean;
|
|
55
|
+
} | {
|
|
56
|
+
type: 'suggestions';
|
|
57
|
+
turnId: string;
|
|
58
|
+
items: string[];
|
|
55
59
|
};
|
|
60
|
+
interface WidgetConfig {
|
|
61
|
+
teaser: string;
|
|
62
|
+
brand: {
|
|
63
|
+
name: string | null;
|
|
64
|
+
logoUrl: string | null;
|
|
65
|
+
accentColor: string | null;
|
|
66
|
+
} | null;
|
|
67
|
+
}
|
|
56
68
|
interface OpenSessionResponse {
|
|
57
69
|
sessionId: string;
|
|
58
70
|
sessionToken: string;
|
|
@@ -116,6 +128,9 @@ type ChatClientEvent = {
|
|
|
116
128
|
} | {
|
|
117
129
|
type: 'agentTyping';
|
|
118
130
|
active: boolean;
|
|
131
|
+
} | {
|
|
132
|
+
type: 'suggestions';
|
|
133
|
+
items: string[];
|
|
119
134
|
};
|
|
120
135
|
type ChatToolHandler = (args: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
121
136
|
interface ChatClient {
|
|
@@ -127,6 +142,7 @@ interface ChatClient {
|
|
|
127
142
|
on(listener: (e: ChatClientEvent) => void): () => void;
|
|
128
143
|
registerTools(handlers: Record<string, ChatToolHandler>): void;
|
|
129
144
|
getAttachmentUrl(storageRef: string): Promise<string>;
|
|
145
|
+
getWidgetConfig(): Promise<WidgetConfig>;
|
|
130
146
|
}
|
|
131
147
|
declare function createChatClient(options: ChatClientOptions): ChatClient;
|
|
132
148
|
|
|
@@ -158,20 +174,30 @@ declare class OyehelloChatElement extends HTMLElement {
|
|
|
158
174
|
private renderedCount;
|
|
159
175
|
private renderedBotFinal;
|
|
160
176
|
private timers;
|
|
177
|
+
private serverSuggestions;
|
|
178
|
+
private teaserText;
|
|
179
|
+
private teaserShownOnce;
|
|
180
|
+
private idleTimer;
|
|
181
|
+
private idleReset;
|
|
161
182
|
private oc;
|
|
162
183
|
private bodyEl;
|
|
163
184
|
private textareaEl;
|
|
164
185
|
private fileEl;
|
|
165
186
|
private titleEl;
|
|
166
187
|
private statusEl;
|
|
167
|
-
private errEl;
|
|
168
188
|
private emojiListEl;
|
|
169
189
|
private emojiSearchEl;
|
|
170
190
|
connectedCallback(): void;
|
|
191
|
+
private armIdleTeaser;
|
|
192
|
+
private teardownIdle;
|
|
193
|
+
private loadWidgetConfig;
|
|
194
|
+
private applyTeaserText;
|
|
195
|
+
private applyBrandLogo;
|
|
171
196
|
disconnectedCallback(): void;
|
|
172
197
|
registerTools(handlers: Record<string, ChatToolHandler>): void;
|
|
173
198
|
private key;
|
|
174
|
-
private get
|
|
199
|
+
private get attrSuggestions();
|
|
200
|
+
private get chipLabels();
|
|
175
201
|
private renderShell;
|
|
176
202
|
private renderError;
|
|
177
203
|
private updateClasses;
|
|
@@ -200,4 +226,4 @@ declare class OyehelloChatElement extends HTMLElement {
|
|
|
200
226
|
private escapeAttr;
|
|
201
227
|
}
|
|
202
228
|
|
|
203
|
-
export { ChatApiError, type ChatAttachmentDescriptor, type ChatClient, type ChatClientEvent, type ChatClientOptions, type ChatClientState, type ChatEvent, type ChatMessage, type ChatToolHandler, type OpenSessionResponse, OyehelloChatElement, createChatClient };
|
|
229
|
+
export { ChatApiError, type ChatAttachmentDescriptor, type ChatClient, type ChatClientEvent, type ChatClientOptions, type ChatClientState, type ChatEvent, type ChatMessage, type ChatToolHandler, type OpenSessionResponse, OyehelloChatElement, type WidgetConfig, createChatClient };
|
package/dist/index.global.js
CHANGED
|
@@ -37,6 +37,15 @@ var OyehelloChat = (function (exports) {
|
|
|
37
37
|
if (!res.ok) throw await asError(res);
|
|
38
38
|
return await res.json();
|
|
39
39
|
}
|
|
40
|
+
async function getWidgetConfig(opts) {
|
|
41
|
+
const res = await opts.fetchImpl(`${opts.baseUrl}/chat/widget-config?agentId=${encodeURIComponent(opts.agentId)}`, {
|
|
42
|
+
method: "GET",
|
|
43
|
+
credentials: "omit",
|
|
44
|
+
headers: { Authorization: `Bearer ${opts.apiKey}` }
|
|
45
|
+
});
|
|
46
|
+
if (!res.ok) throw await asError(res);
|
|
47
|
+
return await res.json();
|
|
48
|
+
}
|
|
40
49
|
async function postMessage(opts) {
|
|
41
50
|
const body = { text: opts.text };
|
|
42
51
|
if (opts.attachments && opts.attachments.length > 0) body.attachments = opts.attachments;
|
|
@@ -214,6 +223,8 @@ var OyehelloChat = (function (exports) {
|
|
|
214
223
|
}
|
|
215
224
|
} else if (ev.type === "agent_typing") {
|
|
216
225
|
emit({ type: "agentTyping", active: !!ev.active });
|
|
226
|
+
} else if (ev.type === "suggestions") {
|
|
227
|
+
emit({ type: "suggestions", items: Array.isArray(ev.items) ? ev.items : [] });
|
|
217
228
|
}
|
|
218
229
|
};
|
|
219
230
|
const openStream = async () => {
|
|
@@ -281,6 +292,9 @@ var OyehelloChat = (function (exports) {
|
|
|
281
292
|
if (!session) throw new Error("call start() before getAttachmentUrl");
|
|
282
293
|
return getAttachmentUrl({ baseUrl: options.baseUrl, sessionId: session.sessionId, sessionToken: session.sessionToken, storageRef, fetchImpl });
|
|
283
294
|
},
|
|
295
|
+
getWidgetConfig() {
|
|
296
|
+
return getWidgetConfig({ baseUrl: options.baseUrl, apiKey: options.apiKey, agentId: options.agentId, fetchImpl });
|
|
297
|
+
},
|
|
284
298
|
async start() {
|
|
285
299
|
if (state === "opening" || state === "ready") return;
|
|
286
300
|
closed = false;
|
|
@@ -1972,8 +1986,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
1972
1986
|
DOMPurify.isSupported = false;
|
|
1973
1987
|
return DOMPurify;
|
|
1974
1988
|
}
|
|
1975
|
-
let
|
|
1976
|
-
const originalDocument =
|
|
1989
|
+
let document2 = window2.document;
|
|
1990
|
+
const originalDocument = document2;
|
|
1977
1991
|
const currentScript = originalDocument.currentScript;
|
|
1978
1992
|
window2.DocumentFragment;
|
|
1979
1993
|
const HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap;
|
|
@@ -1999,9 +2013,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
1999
2013
|
return getNodeName ? getNodeName(node) : node.nodeName;
|
|
2000
2014
|
};
|
|
2001
2015
|
if (typeof HTMLTemplateElement === "function") {
|
|
2002
|
-
const template =
|
|
2016
|
+
const template = document2.createElement("template");
|
|
2003
2017
|
if (template.content && template.content.ownerDocument) {
|
|
2004
|
-
|
|
2018
|
+
document2 = template.content.ownerDocument;
|
|
2005
2019
|
}
|
|
2006
2020
|
}
|
|
2007
2021
|
let trustedTypesPolicy;
|
|
@@ -2039,7 +2053,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
2039
2053
|
}
|
|
2040
2054
|
return defaultTrustedTypesPolicy;
|
|
2041
2055
|
};
|
|
2042
|
-
const _document =
|
|
2056
|
+
const _document = document2, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName;
|
|
2043
2057
|
const importNode = originalDocument.importNode;
|
|
2044
2058
|
let hooks = _createHooksMap();
|
|
2045
2059
|
DOMPurify.isSupported = typeof entries === "function" && typeof getParentNode === "function" && implementation && implementation.createHTMLDocument !== void 0;
|
|
@@ -2164,7 +2178,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
2164
2178
|
const DEFAULT_PARSER_MEDIA_TYPE = "text/html";
|
|
2165
2179
|
let transformCaseFunc = null;
|
|
2166
2180
|
let CONFIG = null;
|
|
2167
|
-
const formElement =
|
|
2181
|
+
const formElement = document2.createElement("form");
|
|
2168
2182
|
const isRegexOrFunction = function isRegexOrFunction2(testValue) {
|
|
2169
2183
|
return testValue instanceof RegExp || testValue instanceof Function;
|
|
2170
2184
|
};
|
|
@@ -2592,7 +2606,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
2592
2606
|
}
|
|
2593
2607
|
const body = doc.body || doc.documentElement;
|
|
2594
2608
|
if (dirty && leadingWhitespace) {
|
|
2595
|
-
body.insertBefore(
|
|
2609
|
+
body.insertBefore(document2.createTextNode(leadingWhitespace), body.childNodes[0] || null);
|
|
2596
2610
|
}
|
|
2597
2611
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
2598
2612
|
return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? "html" : "body")[0];
|
|
@@ -2827,7 +2841,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
2827
2841
|
if (_isPatchLinkageAttribute(lcName, lcTag)) {
|
|
2828
2842
|
return false;
|
|
2829
2843
|
}
|
|
2830
|
-
if (SANITIZE_DOM && (lcName === "id" || lcName === "name") && (value in
|
|
2844
|
+
if (SANITIZE_DOM && (lcName === "id" || lcName === "name") && (value in document2 || value in formElement)) {
|
|
2831
2845
|
return false;
|
|
2832
2846
|
}
|
|
2833
2847
|
const nameIsPermitted = ALLOWED_ATTR2[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
|
|
@@ -3407,8 +3421,11 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3407
3421
|
.i-expand { opacity: 1; } .i-shrink { opacity: 0; }
|
|
3408
3422
|
.oc.max .i-expand { opacity: 0; } .oc.max .i-shrink { opacity: 1; }
|
|
3409
3423
|
|
|
3410
|
-
|
|
3411
|
-
.err
|
|
3424
|
+
/* Inline connection error, shown inside the chat window (no banner) */
|
|
3425
|
+
.oc-err { margin: auto; display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 24px 16px; text-align: center; }
|
|
3426
|
+
.oc-err-t { font-size: 13px; line-height: 1.5; color: var(--oc-fg-muted); text-wrap: pretty; }
|
|
3427
|
+
.oc-err-retry { padding: 7px 15px; font-size: 12.5px; font-weight: 500; border-radius: var(--oc-r-full); border: 1px solid var(--oc-border); background: var(--oc-tint-2); color: var(--oc-fg); cursor: pointer; transition: background .18s var(--oc-ease); }
|
|
3428
|
+
.oc-err-retry:hover { background: var(--oc-tint-3); }
|
|
3412
3429
|
|
|
3413
3430
|
/* \u2500\u2500 Message list \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
3414
3431
|
.body { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 16px; padding: 16px 16px 26px; background: var(--oc-bg); scroll-behavior: smooth; }
|
|
@@ -3676,6 +3693,13 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3676
3693
|
renderedCount = 0;
|
|
3677
3694
|
renderedBotFinal = 0;
|
|
3678
3695
|
timers = [];
|
|
3696
|
+
// Server (flow) suggestions for the latest turn; null until the first `suggestions` event, then the
|
|
3697
|
+
// widget prefers these over the static `suggestions` attribute.
|
|
3698
|
+
serverSuggestions = null;
|
|
3699
|
+
teaserText = "Need a hand? Ask me anything.";
|
|
3700
|
+
teaserShownOnce = false;
|
|
3701
|
+
idleTimer = null;
|
|
3702
|
+
idleReset = null;
|
|
3679
3703
|
// ── shadow refs ──
|
|
3680
3704
|
oc;
|
|
3681
3705
|
bodyEl;
|
|
@@ -3683,7 +3707,6 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3683
3707
|
fileEl;
|
|
3684
3708
|
titleEl;
|
|
3685
3709
|
statusEl;
|
|
3686
|
-
errEl;
|
|
3687
3710
|
emojiListEl;
|
|
3688
3711
|
emojiSearchEl;
|
|
3689
3712
|
connectedCallback() {
|
|
@@ -3695,6 +3718,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3695
3718
|
this.renderError("oyehello-chat: api-key, agent-id and base-url are required");
|
|
3696
3719
|
return;
|
|
3697
3720
|
}
|
|
3721
|
+
this.teaserText = this.getAttribute("teaser") || this.teaserText;
|
|
3698
3722
|
this.client = createChatClient({ apiKey, agentId, baseUrl });
|
|
3699
3723
|
if (Object.keys(this.pendingTools).length > 0) {
|
|
3700
3724
|
this.client.registerTools(this.pendingTools);
|
|
@@ -3706,6 +3730,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3706
3730
|
this.updateHeader();
|
|
3707
3731
|
this.updateStatus();
|
|
3708
3732
|
this.updateClasses();
|
|
3733
|
+
void this.loadWidgetConfig();
|
|
3709
3734
|
let wasOpen = false;
|
|
3710
3735
|
try {
|
|
3711
3736
|
wasOpen = sessionStorage.getItem(this.key("open")) === "1";
|
|
@@ -3719,24 +3744,81 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3719
3744
|
this.timers.push(setTimeout(() => {
|
|
3720
3745
|
this.oc?.classList.remove("ring");
|
|
3721
3746
|
}, 6e3));
|
|
3722
|
-
|
|
3747
|
+
this.armIdleTeaser();
|
|
3748
|
+
}
|
|
3749
|
+
// Show the teaser once the visitor has been idle (no page interaction) for 10s — unless the panel
|
|
3750
|
+
// is already open or the teaser was dismissed this session. Fires at most once.
|
|
3751
|
+
armIdleTeaser() {
|
|
3723
3752
|
let dismissed = false;
|
|
3724
3753
|
try {
|
|
3725
3754
|
dismissed = sessionStorage.getItem(this.key("teaser")) === "0";
|
|
3726
3755
|
} catch {
|
|
3727
3756
|
}
|
|
3728
|
-
if (
|
|
3729
|
-
|
|
3730
|
-
|
|
3757
|
+
if (dismissed) return;
|
|
3758
|
+
const reset = () => {
|
|
3759
|
+
if (this.idleTimer) clearTimeout(this.idleTimer);
|
|
3760
|
+
this.idleTimer = setTimeout(() => {
|
|
3761
|
+
if (!this.uiOpen && !this.teaserShownOnce) {
|
|
3731
3762
|
this.teaserActive = true;
|
|
3763
|
+
this.teaserShownOnce = true;
|
|
3732
3764
|
this.updateClasses();
|
|
3765
|
+
this.teardownIdle();
|
|
3733
3766
|
}
|
|
3734
|
-
},
|
|
3767
|
+
}, 1e4);
|
|
3768
|
+
};
|
|
3769
|
+
this.idleReset = reset;
|
|
3770
|
+
for (const ev of ["mousemove", "keydown", "scroll", "touchstart", "click"]) document.addEventListener(ev, reset, { passive: true });
|
|
3771
|
+
reset();
|
|
3772
|
+
}
|
|
3773
|
+
teardownIdle() {
|
|
3774
|
+
if (this.idleTimer) {
|
|
3775
|
+
clearTimeout(this.idleTimer);
|
|
3776
|
+
this.idleTimer = null;
|
|
3777
|
+
}
|
|
3778
|
+
if (this.idleReset) {
|
|
3779
|
+
for (const ev of ["mousemove", "keydown", "scroll", "touchstart", "click"]) document.removeEventListener(ev, this.idleReset);
|
|
3780
|
+
this.idleReset = null;
|
|
3735
3781
|
}
|
|
3736
3782
|
}
|
|
3783
|
+
async loadWidgetConfig() {
|
|
3784
|
+
try {
|
|
3785
|
+
const cfg = await this.client.getWidgetConfig();
|
|
3786
|
+
if (!this.getAttribute("teaser") && cfg.teaser) {
|
|
3787
|
+
this.teaserText = cfg.teaser;
|
|
3788
|
+
this.applyTeaserText();
|
|
3789
|
+
}
|
|
3790
|
+
if (cfg.brand) {
|
|
3791
|
+
if (cfg.brand.accentColor) this.style.setProperty("--oc-accent", cfg.brand.accentColor);
|
|
3792
|
+
if (cfg.brand.name && !this.getAttribute("title")) {
|
|
3793
|
+
this.agentName = cfg.brand.name;
|
|
3794
|
+
this.updateHeader();
|
|
3795
|
+
}
|
|
3796
|
+
if (cfg.brand.logoUrl) this.applyBrandLogo(cfg.brand.logoUrl);
|
|
3797
|
+
}
|
|
3798
|
+
} catch {
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
applyTeaserText() {
|
|
3802
|
+
const el = this.shadowRoot?.querySelector(".teaser-btn span:last-child");
|
|
3803
|
+
if (el) el.textContent = this.teaserText;
|
|
3804
|
+
}
|
|
3805
|
+
applyBrandLogo(url) {
|
|
3806
|
+
const set = (host) => {
|
|
3807
|
+
if (!host) return;
|
|
3808
|
+
host.textContent = "";
|
|
3809
|
+
const img = document.createElement("img");
|
|
3810
|
+
img.src = url;
|
|
3811
|
+
img.alt = "";
|
|
3812
|
+
img.style.cssText = "width:100%;height:100%;object-fit:contain";
|
|
3813
|
+
host.appendChild(img);
|
|
3814
|
+
};
|
|
3815
|
+
set(this.shadowRoot?.querySelector(".hd .logo"));
|
|
3816
|
+
set(this.shadowRoot?.querySelector(".teaser-btn .logo"));
|
|
3817
|
+
}
|
|
3737
3818
|
disconnectedCallback() {
|
|
3738
3819
|
this.off?.();
|
|
3739
3820
|
void this.client?.end();
|
|
3821
|
+
this.teardownIdle();
|
|
3740
3822
|
for (const t of this.timers) clearTimeout(t);
|
|
3741
3823
|
for (const url of this.imagePreviewCache.values()) {
|
|
3742
3824
|
if (typeof URL.revokeObjectURL === "function") URL.revokeObjectURL(url);
|
|
@@ -3750,18 +3832,22 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3750
3832
|
key(k2) {
|
|
3751
3833
|
return `oyehello-chat:${k2}:${this.getAttribute("agent-id") ?? ""}`;
|
|
3752
3834
|
}
|
|
3753
|
-
|
|
3835
|
+
// Static fallback chips from the `suggestions` attribute (for embedders without flow-driven ones).
|
|
3836
|
+
get attrSuggestions() {
|
|
3754
3837
|
return (this.getAttribute("suggestions") ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
3755
3838
|
}
|
|
3839
|
+
// Effective chips: server (flow) suggestions once received, else the static attribute fallback.
|
|
3840
|
+
get chipLabels() {
|
|
3841
|
+
return this.serverSuggestions ?? this.attrSuggestions;
|
|
3842
|
+
}
|
|
3756
3843
|
// ── Shell (built once) ─────────────────────────────────────────────────────────────────────────
|
|
3757
3844
|
renderShell() {
|
|
3758
3845
|
const root = this.shadowRoot;
|
|
3759
|
-
const hasTeaser = !!this.getAttribute("teaser");
|
|
3760
3846
|
const hasPrivacy = !!this.getAttribute("privacy-url");
|
|
3761
|
-
const teaserText = this.
|
|
3847
|
+
const teaserText = this.teaserText;
|
|
3762
3848
|
root.innerHTML = `
|
|
3763
3849
|
<style>${STYLES}</style>
|
|
3764
|
-
<div class="oc
|
|
3850
|
+
<div class="oc has-teaser${hasPrivacy ? " has-privacy" : ""}">
|
|
3765
3851
|
<div class="panel" role="dialog" aria-label="Chat">
|
|
3766
3852
|
<div class="hd">
|
|
3767
3853
|
<span class="hd-grid"></span>
|
|
@@ -3770,7 +3856,6 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3770
3856
|
<button type="button" class="hd-btn max" aria-label="Expand"><span class="i i-expand">${ICON.expand}</span><span class="i i-shrink">${ICON.shrink}</span></button>
|
|
3771
3857
|
<button type="button" class="hd-btn min" aria-label="Minimise">${ICON.minus}</button>
|
|
3772
3858
|
</div>
|
|
3773
|
-
<div class="err" hidden></div>
|
|
3774
3859
|
<div class="body"></div>
|
|
3775
3860
|
<span class="emoji-scrim"></span>
|
|
3776
3861
|
<div class="emoji-panel">
|
|
@@ -3814,7 +3899,6 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3814
3899
|
this.fileEl = root.querySelector('input[type="file"]');
|
|
3815
3900
|
this.titleEl = root.querySelector(".hd-title");
|
|
3816
3901
|
this.statusEl = root.querySelector(".hd-status .t");
|
|
3817
|
-
this.errEl = root.querySelector(".err");
|
|
3818
3902
|
this.emojiListEl = root.querySelector(".emoji-list");
|
|
3819
3903
|
this.emojiSearchEl = root.querySelector(".emoji-search input");
|
|
3820
3904
|
root.querySelector(".launcher").addEventListener("click", () => this.toggle());
|
|
@@ -3895,6 +3979,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3895
3979
|
this.uiOpen = true;
|
|
3896
3980
|
this.teaserActive = false;
|
|
3897
3981
|
this.unseen = 0;
|
|
3982
|
+
this.teardownIdle();
|
|
3898
3983
|
try {
|
|
3899
3984
|
sessionStorage.setItem(this.key("open"), "1");
|
|
3900
3985
|
} catch {
|
|
@@ -3937,6 +4022,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3937
4022
|
this.updateHeader();
|
|
3938
4023
|
} else if (e.type === "handoff") this.handoffState = e.state;
|
|
3939
4024
|
else if (e.type === "agentTyping") this.agentTyping = e.active;
|
|
4025
|
+
else if (e.type === "suggestions") this.serverSuggestions = e.items;
|
|
3940
4026
|
else if (e.type === "error") this.lastError = e.error.message;
|
|
3941
4027
|
this.updateStatus();
|
|
3942
4028
|
this.renderMessages();
|
|
@@ -3951,16 +4037,6 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3951
4037
|
const handoff = HANDOFF_STATUS[this.handoffState];
|
|
3952
4038
|
const text2 = this.state === "opening" ? "Connecting\u2026" : handoff ?? (this.getAttribute("status") ?? DEFAULT_STATUS);
|
|
3953
4039
|
if (this.statusEl) this.statusEl.textContent = text2;
|
|
3954
|
-
if (this.state === "error" && this.lastError) {
|
|
3955
|
-
this.errEl.hidden = false;
|
|
3956
|
-
this.errEl.innerHTML = `<span>Couldn't start chat: ${escapeHtml(this.lastError)}</span><button class="retry" type="button">Retry</button>`;
|
|
3957
|
-
this.errEl.querySelector(".retry").addEventListener("click", () => {
|
|
3958
|
-
void this.client?.start();
|
|
3959
|
-
});
|
|
3960
|
-
} else {
|
|
3961
|
-
this.errEl.hidden = true;
|
|
3962
|
-
this.errEl.innerHTML = "";
|
|
3963
|
-
}
|
|
3964
4040
|
}
|
|
3965
4041
|
// ── Message list + scroll ─────────────────────────────────────────────────────────────────────
|
|
3966
4042
|
renderMessages() {
|
|
@@ -3978,10 +4054,13 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3978
4054
|
})();
|
|
3979
4055
|
let html2 = this.messages.map((m2, i) => this.turnHtml(m2, i === lastBot && !this.agentTyping)).join("");
|
|
3980
4056
|
if (this.agentTyping) html2 += '<span class="typing"><span></span><span></span><span></span></span>';
|
|
3981
|
-
const sugg = this.
|
|
4057
|
+
const sugg = this.chipLabels;
|
|
3982
4058
|
if (sugg.length > 0 && !this.agentTyping && this.state === "ready") {
|
|
3983
4059
|
html2 += `<span class="chips">${sugg.map((s, i) => `<button type="button" class="chip" data-chip="${i}">${escapeHtml(s)}</button>`).join("")}</span>`;
|
|
3984
4060
|
}
|
|
4061
|
+
if (this.state === "error" && this.lastError) {
|
|
4062
|
+
html2 += `<div class="oc-err"><span class="oc-err-t">${escapeHtml(this.lastError)}</span><button type="button" class="oc-err-retry">Try again</button></div>`;
|
|
4063
|
+
}
|
|
3985
4064
|
body.innerHTML = html2;
|
|
3986
4065
|
this.hydrateAttachmentImages();
|
|
3987
4066
|
this.bindChips();
|
|
@@ -4059,12 +4138,15 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4059
4138
|
this.updateClasses();
|
|
4060
4139
|
}
|
|
4061
4140
|
bindChips() {
|
|
4062
|
-
const sugg = this.
|
|
4141
|
+
const sugg = this.chipLabels;
|
|
4063
4142
|
this.bodyEl.querySelectorAll(".chip[data-chip]").forEach((btn) => {
|
|
4064
4143
|
const i = Number(btn.getAttribute("data-chip"));
|
|
4065
4144
|
const text2 = sugg[i];
|
|
4066
4145
|
if (text2) btn.addEventListener("click", () => this.sendChip(text2));
|
|
4067
4146
|
});
|
|
4147
|
+
this.bodyEl.querySelector(".oc-err-retry")?.addEventListener("click", () => {
|
|
4148
|
+
void this.client?.start();
|
|
4149
|
+
});
|
|
4068
4150
|
}
|
|
4069
4151
|
// ── Emoji ─────────────────────────────────────────────────────────────────────────────────────
|
|
4070
4152
|
toggleEmoji() {
|