@onedeskpro/chatbot-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,782 @@
1
+ const D = 3e4;
2
+ function v(i) {
3
+ return typeof i == "object" && i !== null && typeof i.message == "string";
4
+ }
5
+ class C {
6
+ constructor(t) {
7
+ this.baseUrl = t.baseUrl.replace(/\/$/, ""), this.apiKey = t.apiKey, this.timeoutMs = t.timeoutMs ?? 3e4;
8
+ }
9
+ fail(t, e, n, s) {
10
+ const a = {
11
+ statusCode: e,
12
+ message: t,
13
+ code: n,
14
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
15
+ path: s
16
+ };
17
+ throw Object.assign(new Error(t), { apiError: a, status: e });
18
+ }
19
+ async request(t, e, n, s) {
20
+ const a = new URL(`${this.baseUrl}${e}`);
21
+ if (s)
22
+ for (const [p, y] of Object.entries(s))
23
+ a.searchParams.set(p, y);
24
+ const l = {
25
+ "Content-Type": "application/json",
26
+ "X-API-Key": this.apiKey
27
+ }, r = new AbortController(), o = setTimeout(() => r.abort(), this.timeoutMs);
28
+ let c;
29
+ try {
30
+ c = await fetch(a.toString(), {
31
+ method: t,
32
+ headers: l,
33
+ signal: r.signal,
34
+ ...n !== void 0 ? { body: JSON.stringify(n) } : {}
35
+ });
36
+ } catch (p) {
37
+ r.signal.aborted && this.fail(
38
+ `Request timed out after ${this.timeoutMs}ms.`,
39
+ 0,
40
+ "TIMEOUT",
41
+ e
42
+ ), this.fail(
43
+ p instanceof Error ? p.message : "Network request failed.",
44
+ 0,
45
+ "NETWORK_ERROR",
46
+ e
47
+ );
48
+ } finally {
49
+ clearTimeout(o);
50
+ }
51
+ const d = await c.text();
52
+ let h = null;
53
+ if (d)
54
+ try {
55
+ h = JSON.parse(d);
56
+ } catch {
57
+ h = null;
58
+ }
59
+ if (!c.ok) {
60
+ if (v(h))
61
+ throw Object.assign(new Error(h.message), {
62
+ apiError: h,
63
+ status: c.status
64
+ });
65
+ this.fail(
66
+ `Request failed with status ${c.status} ${c.statusText}`.trim(),
67
+ c.status,
68
+ "HTTP_ERROR",
69
+ e
70
+ );
71
+ }
72
+ return h === null && this.fail("The server returned an empty or malformed response.", c.status, "BAD_RESPONSE", e), h.data;
73
+ }
74
+ async sendMessage(t) {
75
+ return this.request("POST", "/sdk/chat", t);
76
+ }
77
+ async fetchHistory(t) {
78
+ return this.request("GET", "/sdk/chat-history", void 0, { sessionId: t });
79
+ }
80
+ async fetchConfig() {
81
+ return this.request("GET", "/sdk/config");
82
+ }
83
+ }
84
+ const E = "https://api.onedeskpro.com";
85
+ class k {
86
+ constructor() {
87
+ this._listeners = {};
88
+ }
89
+ on(t, e) {
90
+ return this._listeners[t] || (this._listeners[t] = []), this._listeners[t].push(e), () => this.off(t, e);
91
+ }
92
+ off(t, e) {
93
+ const n = this._listeners[t];
94
+ n && (this._listeners[t] = n.filter((s) => s !== e));
95
+ }
96
+ emit(t, ...e) {
97
+ const n = this._listeners[t];
98
+ if (n)
99
+ for (const s of n) s(...e);
100
+ }
101
+ removeAllListeners() {
102
+ for (const t of Object.keys(this._listeners))
103
+ delete this._listeners[t];
104
+ }
105
+ }
106
+ const g = "onedeskpro_chatbot_session_id";
107
+ function M(i) {
108
+ try {
109
+ return typeof window < "u" ? window.localStorage.getItem(i) : null;
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+ function S(i, t) {
115
+ try {
116
+ typeof window < "u" && window.localStorage.setItem(i, t);
117
+ } catch {
118
+ }
119
+ }
120
+ class T {
121
+ constructor() {
122
+ this.sessionId = null;
123
+ }
124
+ init(t) {
125
+ if (t)
126
+ return this.sessionId = t, this.persist(), this.sessionId;
127
+ const e = M(g);
128
+ return e ? this.sessionId = e : (this.sessionId = this.generateId(), this.persist()), this.sessionId;
129
+ }
130
+ get() {
131
+ return this.sessionId;
132
+ }
133
+ reset() {
134
+ return this.sessionId = this.generateId(), this.persist(), this.sessionId;
135
+ }
136
+ persist() {
137
+ this.sessionId && S(g, this.sessionId);
138
+ }
139
+ generateId() {
140
+ return typeof crypto < "u" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2) + Date.now().toString(36);
141
+ }
142
+ }
143
+ function I(i, t) {
144
+ const e = `
145
+ --ttcb-bg: #ffffff;
146
+ --ttcb-bg-secondary: #f3f4f6;
147
+ --ttcb-text: #111827;
148
+ --ttcb-text-muted: #6b7280;
149
+ --ttcb-border: #e5e7eb;
150
+ --ttcb-user-bubble: ${i};
151
+ --ttcb-user-text: #ffffff;
152
+ --ttcb-ai-bubble: #f3f4f6;
153
+ --ttcb-ai-text: #111827;
154
+ --ttcb-input-bg: #ffffff;
155
+ --ttcb-shadow: 0 20px 60px rgba(0,0,0,0.15);
156
+ `, n = `
157
+ --ttcb-bg: #1f2937;
158
+ --ttcb-bg-secondary: #111827;
159
+ --ttcb-text: #f9fafb;
160
+ --ttcb-text-muted: #9ca3af;
161
+ --ttcb-border: #374151;
162
+ --ttcb-user-bubble: ${i};
163
+ --ttcb-user-text: #ffffff;
164
+ --ttcb-ai-bubble: #374151;
165
+ --ttcb-ai-text: #f9fafb;
166
+ --ttcb-input-bg: #111827;
167
+ --ttcb-shadow: 0 20px 60px rgba(0,0,0,0.4);
168
+ `;
169
+ let s;
170
+ return t === "dark" ? s = `:host { ${n} }` : t === "light" ? s = `:host { ${e} }` : s = `
171
+ :host { ${e} }
172
+ @media (prefers-color-scheme: dark) { :host { ${n} } }
173
+ `, `
174
+ ${s}
175
+
176
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
177
+
178
+ .ttcb-fab {
179
+ position: fixed;
180
+ width: 56px; height: 56px;
181
+ border-radius: 50%;
182
+ background: ${i};
183
+ border: none; cursor: pointer;
184
+ display: flex; align-items: center; justify-content: center;
185
+ box-shadow: 0 4px 20px rgba(0,0,0,0.2);
186
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
187
+ z-index: 9999;
188
+ }
189
+ .ttcb-fab:hover { transform: scale(1.08); box-shadow: 0 6px 24px rgba(0,0,0,0.25); }
190
+ .ttcb-fab svg { width: 24px; height: 24px; fill: #fff; }
191
+ .ttcb-fab.bottom-right { bottom: 24px; right: 24px; }
192
+ .ttcb-fab.bottom-left { bottom: 24px; left: 24px; }
193
+
194
+ .ttcb-panel {
195
+ position: fixed;
196
+ width: 380px; height: 560px;
197
+ background: var(--ttcb-bg);
198
+ border: 1px solid var(--ttcb-border);
199
+ border-radius: 16px;
200
+ display: flex; flex-direction: column;
201
+ box-shadow: var(--ttcb-shadow);
202
+ overflow: hidden;
203
+ z-index: 9998;
204
+ transition: opacity 0.2s ease, transform 0.25s ease;
205
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
206
+ }
207
+ .ttcb-panel.bottom-right { bottom: 92px; right: 24px; }
208
+ .ttcb-panel.bottom-left { bottom: 92px; left: 24px; }
209
+ .ttcb-panel.hidden {
210
+ opacity: 0; pointer-events: none;
211
+ transform: translateY(12px) scale(0.97);
212
+ }
213
+
214
+ .ttcb-header {
215
+ padding: 14px 16px;
216
+ border-bottom: 1px solid var(--ttcb-border);
217
+ display: flex; align-items: center; gap: 10px;
218
+ background: var(--ttcb-bg);
219
+ flex-shrink: 0;
220
+ }
221
+ .ttcb-avatar {
222
+ width: 32px; height: 32px; border-radius: 50%;
223
+ background: ${i}22;
224
+ display: flex; align-items: center; justify-content: center;
225
+ flex-shrink: 0;
226
+ }
227
+ .ttcb-avatar svg { width: 16px; height: 16px; fill: ${i}; }
228
+ .ttcb-header-title { font-weight: 600; font-size: 14px; color: var(--ttcb-text); flex: 1; }
229
+ .ttcb-close-btn, .ttcb-new-session-btn {
230
+ background: none; border: none; cursor: pointer;
231
+ color: var(--ttcb-text-muted); padding: 4px; border-radius: 6px;
232
+ display: flex; align-items: center; justify-content: center;
233
+ }
234
+ .ttcb-close-btn:hover, .ttcb-new-session-btn:hover { background: var(--ttcb-bg-secondary); }
235
+ .ttcb-close-btn svg { width: 18px; height: 18px; fill: currentColor; }
236
+ .ttcb-new-session-btn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; }
237
+
238
+ .ttcb-messages {
239
+ flex: 1; overflow-y: auto; padding: 16px;
240
+ display: flex; flex-direction: column; gap: 12px;
241
+ scroll-behavior: smooth;
242
+ font-size: 14px;
243
+ }
244
+
245
+ .ttcb-empty {
246
+ display: flex; flex-direction: column; align-items: center;
247
+ justify-content: center; text-align: center;
248
+ padding: 32px 16px; flex: 1;
249
+ color: var(--ttcb-text-muted); font-size: 13px;
250
+ }
251
+ .ttcb-empty-icon {
252
+ width: 48px; height: 48px; border-radius: 50%;
253
+ background: var(--ttcb-bg-secondary);
254
+ display: flex; align-items: center; justify-content: center;
255
+ margin-bottom: 12px;
256
+ }
257
+ .ttcb-empty-icon svg { width: 22px; height: 22px; fill: var(--ttcb-text-muted); }
258
+ .ttcb-empty strong { color: var(--ttcb-text); }
259
+
260
+ .ttcb-initial-loading {
261
+ display: flex; flex-direction: column; align-items: center;
262
+ justify-content: center; flex: 1; padding: 32px 16px;
263
+ color: var(--ttcb-text-muted); font-size: 13px;
264
+ }
265
+ .ttcb-spinner {
266
+ width: 24px; height: 24px;
267
+ border: 2px solid var(--ttcb-border);
268
+ border-top-color: var(--ttcb-text-muted);
269
+ border-radius: 50%;
270
+ animation: ttcb-spin 0.7s linear infinite;
271
+ margin-bottom: 10px;
272
+ }
273
+ @keyframes ttcb-spin { to { transform: rotate(360deg); } }
274
+
275
+ .ttcb-blocked {
276
+ display: flex; flex-direction: column; align-items: center;
277
+ justify-content: center; text-align: center;
278
+ padding: 32px 20px; flex: 1;
279
+ }
280
+ .ttcb-blocked-icon {
281
+ width: 44px; height: 44px; border-radius: 50%;
282
+ background: var(--ttcb-bg-secondary);
283
+ display: flex; align-items: center; justify-content: center;
284
+ margin-bottom: 14px;
285
+ }
286
+ .ttcb-blocked-icon svg { width: 20px; height: 20px; fill: var(--ttcb-text-muted); }
287
+ .ttcb-blocked-title { font-size: 14px; font-weight: 600; color: var(--ttcb-text); margin-bottom: 8px; }
288
+ .ttcb-blocked-desc { font-size: 12px; color: var(--ttcb-text-muted); line-height: 1.5; max-width: 220px; }
289
+
290
+ .ttcb-msg { display: flex; gap: 8px; max-width: 100%; }
291
+ .ttcb-msg.human { flex-direction: row-reverse; }
292
+ .ttcb-msg-avatar {
293
+ width: 28px; height: 28px; border-radius: 50%;
294
+ background: var(--ttcb-bg-secondary);
295
+ display: flex; align-items: center; justify-content: center;
296
+ flex-shrink: 0; margin-top: 2px;
297
+ }
298
+ .ttcb-msg-avatar svg { width: 13px; height: 13px; fill: var(--ttcb-text-muted); }
299
+ .ttcb-bubble {
300
+ max-width: 78%; padding: 9px 13px;
301
+ border-radius: 16px 16px 16px 4px;
302
+ line-height: 1.5;
303
+ color: var(--ttcb-ai-text);
304
+ background: var(--ttcb-ai-bubble);
305
+ white-space: pre-wrap; word-break: break-word;
306
+ font-size: 14px;
307
+ }
308
+ .ttcb-msg.human .ttcb-bubble {
309
+ background: var(--ttcb-user-bubble);
310
+ color: var(--ttcb-user-text);
311
+ border-radius: 16px 16px 4px 16px;
312
+ }
313
+
314
+ /* Markdown styles inside AI bubble */
315
+ .ttcb-bubble p { margin: 0 0 6px 0; }
316
+ .ttcb-bubble p:last-child { margin-bottom: 0; }
317
+ .ttcb-bubble strong { font-weight: 600; }
318
+ .ttcb-bubble em { font-style: italic; }
319
+ .ttcb-bubble code {
320
+ background: rgba(0,0,0,0.08);
321
+ padding: 1px 5px; border-radius: 3px;
322
+ font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
323
+ font-size: 12px;
324
+ }
325
+ .ttcb-bubble pre {
326
+ background: rgba(0,0,0,0.08);
327
+ padding: 10px 12px; border-radius: 6px;
328
+ overflow-x: auto; margin: 6px 0;
329
+ }
330
+ .ttcb-bubble pre code { background: none; padding: 0; font-size: 12px; }
331
+ .ttcb-bubble h1, .ttcb-bubble h2, .ttcb-bubble h3 {
332
+ font-weight: 600; margin: 8px 0 4px; line-height: 1.3;
333
+ }
334
+ .ttcb-bubble h1 { font-size: 15px; }
335
+ .ttcb-bubble h2 { font-size: 14px; }
336
+ .ttcb-bubble h3 { font-size: 13px; }
337
+ .ttcb-bubble ul, .ttcb-bubble ol { padding-left: 18px; margin: 4px 0; }
338
+ .ttcb-bubble li { margin: 2px 0; }
339
+ .ttcb-bubble a { color: inherit; text-decoration: underline; }
340
+
341
+ .ttcb-typing { display: flex; gap: 4px; padding: 8px 2px; align-items: center; }
342
+ .ttcb-typing span {
343
+ width: 6px; height: 6px; border-radius: 50%;
344
+ background: var(--ttcb-text-muted);
345
+ animation: ttcb-bounce 1.2s infinite;
346
+ }
347
+ .ttcb-typing span:nth-child(2) { animation-delay: 0.15s; }
348
+ .ttcb-typing span:nth-child(3) { animation-delay: 0.3s; }
349
+ @keyframes ttcb-bounce {
350
+ 0%, 80%, 100% { transform: translateY(0); opacity: 0.4; }
351
+ 40% { transform: translateY(-5px); opacity: 1; }
352
+ }
353
+
354
+ .ttcb-footer {
355
+ border-top: 1px solid var(--ttcb-border);
356
+ padding: 12px;
357
+ background: var(--ttcb-bg);
358
+ flex-shrink: 0;
359
+ }
360
+ .ttcb-form { display: flex; gap: 8px; }
361
+ .ttcb-input {
362
+ flex: 1; padding: 9px 12px;
363
+ border: 1px solid var(--ttcb-border);
364
+ border-radius: 8px;
365
+ font-size: 14px; outline: none;
366
+ background: var(--ttcb-input-bg);
367
+ color: var(--ttcb-text);
368
+ transition: border-color 0.15s;
369
+ font-family: inherit;
370
+ }
371
+ .ttcb-input:focus { border-color: ${i}; }
372
+ .ttcb-input::placeholder { color: var(--ttcb-text-muted); }
373
+ .ttcb-input:disabled { opacity: 0.6; cursor: not-allowed; }
374
+ .ttcb-send-btn {
375
+ padding: 0 14px; height: 38px;
376
+ background: ${i}; color: #fff;
377
+ border: none; border-radius: 8px; cursor: pointer;
378
+ display: flex; align-items: center; justify-content: center;
379
+ transition: opacity 0.15s; flex-shrink: 0;
380
+ }
381
+ .ttcb-send-btn:hover { opacity: 0.9; }
382
+ .ttcb-send-btn:disabled { opacity: 0.5; cursor: not-allowed; }
383
+ .ttcb-send-btn svg { width: 16px; height: 16px; fill: #fff; }
384
+
385
+ @media (max-width: 440px) {
386
+ .ttcb-panel { width: calc(100vw - 16px); right: 8px !important; left: 8px !important; bottom: 84px !important; }
387
+ .ttcb-fab.bottom-right { right: 16px; }
388
+ .ttcb-fab.bottom-left { left: 16px; }
389
+ }
390
+ `;
391
+ }
392
+ function u(i) {
393
+ return i.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
394
+ }
395
+ function L(i) {
396
+ const t = [];
397
+ let e = i.replace(/```[\w]*\n?([\s\S]*?)```/g, (r, o) => {
398
+ const c = t.length;
399
+ return t.push(`<pre><code>${u(o.trim())}</code></pre>`), `${c}`;
400
+ });
401
+ e = e.replace(/`([^`\n]+)`/g, (r, o) => {
402
+ const c = t.length;
403
+ return t.push(`<code>${u(o)}</code>`), `${c}`;
404
+ }), e = u(e);
405
+ const n = e.split(`
406
+ `), s = [];
407
+ let a = "";
408
+ const l = () => {
409
+ a && (s.push(`</${a}>`), a = "");
410
+ };
411
+ for (const r of n) {
412
+ let o;
413
+ (o = r.match(/^### (.+)/)) ? (l(), s.push(`<h3>${o[1]}</h3>`)) : (o = r.match(/^## (.+)/)) ? (l(), s.push(`<h2>${o[1]}</h2>`)) : (o = r.match(/^# (.+)/)) ? (l(), s.push(`<h1>${o[1]}</h1>`)) : (o = r.match(/^[*-] (.+)/)) ? (a !== "ul" && (l(), s.push("<ul>"), a = "ul"), s.push(`<li>${o[1]}</li>`)) : (o = r.match(/^\d+\. (.+)/)) ? (a !== "ol" && (l(), s.push("<ol>"), a = "ol"), s.push(`<li>${o[1]}</li>`)) : (l(), s.push(r));
414
+ }
415
+ return l(), e = s.join(`
416
+ `), e = e.replace(/\*\*(.+?)\*\*/gs, "<strong>$1</strong>").replace(/__(.+?)__/gs, "<strong>$1</strong>").replace(/\*(.+?)\*/gs, "<em>$1</em>").replace(/_(.+?)_/gs, "<em>$1</em>").replace(
417
+ /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,
418
+ '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
419
+ ), e = e.split(/\n{2,}/).map((r) => {
420
+ const o = r.trim();
421
+ return o ? /^<(h[1-3]|ul|ol|pre|li)/.test(o) ? o : o.split(/(\x04\d+\x05)/g).map((c) => {
422
+ if (/^\x04\d+\x05$/.test(c)) return c;
423
+ const d = c.trim();
424
+ return d ? `<p>${d.replace(/\n/g, "<br>")}</p>` : "";
425
+ }).join("") : "";
426
+ }).filter(Boolean).join(""), e = e.replace(/\x04(\d+)\x05/g, (r, o) => t[+o] ?? "").replace(/\x02(\d+)\x03/g, (r, o) => t[+o] ?? ""), e;
427
+ }
428
+ function N() {
429
+ return `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
430
+ <path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/>
431
+ </svg>`;
432
+ }
433
+ function f() {
434
+ return `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
435
+ <path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/>
436
+ </svg>`;
437
+ }
438
+ function _() {
439
+ return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
440
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
441
+ <line x1="12" y1="8" x2="12" y2="14"/>
442
+ <line x1="9" y1="11" x2="15" y2="11"/>
443
+ </svg>`;
444
+ }
445
+ function $() {
446
+ return `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
447
+ <path d="M20 2H4C2.9 2 2 2.9 2 4v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/>
448
+ </svg>`;
449
+ }
450
+ function B() {
451
+ return `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
452
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
453
+ </svg>`;
454
+ }
455
+ function A() {
456
+ return `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
457
+ <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
458
+ </svg>`;
459
+ }
460
+ function m() {
461
+ return `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
462
+ <path d="M20 9V7c0-1.1-.9-2-2-2h-3c0-1.66-1.34-3-3-3S9 3.34 9 5H6c-1.1 0-2 .9-2 2v2c-1.66 0-3 1.34-3 3s1.34 3 3 3v4c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-4c1.66 0 3-1.34 3-3s-1.34-3-3-3zm-2 10H6V7h12v12zm-9-6c-.83 0-1.5-.67-1.5-1.5S8.17 10 9 10s1.5.67 1.5 1.5S9.83 13 9 13zm6 0c-.83 0-1.5-.67-1.5-1.5S14.17 10 15 10s1.5.67 1.5 1.5S15.83 13 15 13z"/>
463
+ </svg>`;
464
+ }
465
+ function O() {
466
+ return `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
467
+ <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
468
+ </svg>`;
469
+ }
470
+ function x(i, t) {
471
+ const e = document.createElement("div");
472
+ e.className = `ttcb-msg ${t}`;
473
+ const n = document.createElement("div");
474
+ n.className = "ttcb-msg-avatar", n.innerHTML = t === "human" ? O() : m();
475
+ const s = document.createElement("div");
476
+ return s.className = "ttcb-bubble", t === "human" ? s.textContent = i : s.innerHTML = L(i), e.appendChild(n), e.appendChild(s), e;
477
+ }
478
+ function H() {
479
+ const i = document.createElement("div");
480
+ i.className = "ttcb-msg ai", i.id = "ttcb-typing";
481
+ const t = document.createElement("div");
482
+ t.className = "ttcb-msg-avatar", t.innerHTML = m();
483
+ const e = document.createElement("div");
484
+ return e.className = "ttcb-typing", e.innerHTML = "<span></span><span></span><span></span>", i.appendChild(t), i.appendChild(e), i;
485
+ }
486
+ class z {
487
+ constructor(t, e) {
488
+ this.isOpen = !1, this.destroyed = !1, this.position = t.position ?? "bottom-right", this.callbacks = e, this.mount(t);
489
+ }
490
+ mount(t) {
491
+ this.host = document.createElement("div"), this.host.id = "onedeskpro-chatbot-host", this.shadow = this.host.attachShadow({ mode: "closed" });
492
+ const e = document.createElement("style");
493
+ e.textContent = I(
494
+ t.primaryColor ?? "#2563EB",
495
+ t.theme ?? "auto"
496
+ ), this.shadow.appendChild(e);
497
+ const n = document.createElement("button");
498
+ n.className = `ttcb-fab ${this.position}`, n.setAttribute("aria-label", "Open chat"), n.innerHTML = $(), n.addEventListener("click", () => this.toggle()), this.shadow.appendChild(n), this.panel = document.createElement("div"), this.panel.className = `ttcb-panel ${this.position} hidden`, this.panel.setAttribute("role", "dialog"), this.panel.setAttribute("aria-modal", "true"), this.panel.setAttribute("aria-label", t.chatbotName ?? "AI Assistant");
499
+ const s = document.createElement("div");
500
+ s.className = "ttcb-header";
501
+ const a = document.createElement("div");
502
+ a.className = "ttcb-avatar", a.innerHTML = m();
503
+ const l = document.createElement("span");
504
+ l.className = "ttcb-header-title", l.textContent = t.chatbotName ?? "AI Assistant";
505
+ const r = document.createElement("button");
506
+ r.className = "ttcb-new-session-btn", r.setAttribute("aria-label", "New conversation"), r.title = "New conversation", r.innerHTML = _(), r.addEventListener("click", () => this.callbacks.onReset());
507
+ const o = document.createElement("button");
508
+ o.className = "ttcb-close-btn", o.setAttribute("aria-label", "Close chat"), o.innerHTML = B(), o.addEventListener("click", () => this.close()), s.appendChild(a), s.appendChild(l), s.appendChild(r), s.appendChild(o), this.panel.appendChild(s), this.messagesContainer = document.createElement("div"), this.messagesContainer.className = "ttcb-messages", this.messagesContainer.setAttribute("aria-live", "polite"), this.messagesContainer.setAttribute("aria-label", "Chat messages"), this.panel.appendChild(this.messagesContainer);
509
+ const c = document.createElement("div");
510
+ c.className = "ttcb-footer";
511
+ const d = document.createElement("form");
512
+ d.className = "ttcb-form", d.addEventListener("submit", (h) => {
513
+ h.preventDefault(), this.handleSend();
514
+ }), this.input = document.createElement("input"), this.input.type = "text", this.input.className = "ttcb-input", this.input.placeholder = t.placeholder ?? "Type your message…", this.input.setAttribute("aria-label", "Message input"), this.input.setAttribute("autocomplete", "off"), this.sendBtn = document.createElement("button"), this.sendBtn.type = "submit", this.sendBtn.className = "ttcb-send-btn", this.sendBtn.innerHTML = A(), this.sendBtn.setAttribute("aria-label", "Send message"), d.appendChild(this.input), d.appendChild(this.sendBtn), c.appendChild(d), this.panel.appendChild(c), this.shadow.appendChild(this.panel), this.attachToDocument(), this.showInitialLoading(), t.autoOpen && this.open();
515
+ }
516
+ /**
517
+ * `document.body` is null when the CDN script runs from <head> without defer,
518
+ * so fall back to waiting for the parse to finish rather than throwing.
519
+ */
520
+ attachToDocument() {
521
+ const t = () => {
522
+ this.destroyed || (document.body ?? document.documentElement).appendChild(this.host);
523
+ };
524
+ document.body ? t() : document.addEventListener("DOMContentLoaded", t, { once: !0 });
525
+ }
526
+ showInitialLoading() {
527
+ this.messagesContainer.innerHTML = "", this.input.disabled = !0, this.sendBtn.disabled = !0;
528
+ const t = document.createElement("div");
529
+ t.className = "ttcb-initial-loading", t.id = "ttcb-initial-loading";
530
+ const e = document.createElement("div");
531
+ e.className = "ttcb-spinner";
532
+ const n = document.createElement("p");
533
+ n.textContent = "Loading…", t.appendChild(e), t.appendChild(n), this.messagesContainer.appendChild(t);
534
+ }
535
+ showEmptyState(t) {
536
+ const e = document.createElement("div");
537
+ e.className = "ttcb-empty", e.id = "ttcb-empty";
538
+ const n = document.createElement("div");
539
+ n.className = "ttcb-empty-icon", n.innerHTML = m();
540
+ const s = document.createElement("p"), a = document.createElement("strong");
541
+ a.textContent = t, s.appendChild(document.createTextNode("Welcome to ")), s.appendChild(a);
542
+ const l = document.createElement("p");
543
+ l.textContent = "Ask me anything — I'm here to help!", l.style.marginTop = "6px", l.style.fontSize = "12px", e.appendChild(n), e.appendChild(s), e.appendChild(l), this.messagesContainer.appendChild(e);
544
+ }
545
+ removeEmptyState() {
546
+ const t = this.shadow.getElementById("ttcb-empty");
547
+ t && t.remove();
548
+ }
549
+ handleSend() {
550
+ const t = this.input.value.trim();
551
+ t && (this.input.value = "", this.callbacks.onSend(t));
552
+ }
553
+ // ─── Public API ────────────────────────────────────────────────────────────
554
+ appendMessage(t) {
555
+ this.removeEmptyState();
556
+ const e = "message" in t ? t.message : t, n = x(e.content, e.type);
557
+ this.messagesContainer.appendChild(n), this.scrollToBottom();
558
+ }
559
+ setMessages(t) {
560
+ if (this.messagesContainer.innerHTML = "", t.length !== 0) {
561
+ for (const e of t) {
562
+ const n = x(e.message.content, e.message.type);
563
+ this.messagesContainer.appendChild(n);
564
+ }
565
+ this.scrollToBottom();
566
+ }
567
+ }
568
+ setLoading(t) {
569
+ if (this.sendBtn.disabled = t, this.input.disabled = t, t)
570
+ this.shadow.getElementById("ttcb-typing") || (this.messagesContainer.appendChild(H()), this.scrollToBottom());
571
+ else {
572
+ const e = this.shadow.getElementById("ttcb-typing");
573
+ e && e.remove();
574
+ }
575
+ }
576
+ open() {
577
+ this.isOpen = !0, this.panel.classList.remove("hidden"), this.input.focus(), this.callbacks.onOpen();
578
+ }
579
+ close() {
580
+ this.isOpen = !1, this.panel.classList.add("hidden"), this.callbacks.onClose();
581
+ }
582
+ toggle() {
583
+ this.isOpen ? this.close() : this.open();
584
+ }
585
+ readyToChat(t) {
586
+ this.messagesContainer.innerHTML = "", this.input.disabled = !1, this.sendBtn.disabled = !1;
587
+ const e = this.shadow.querySelector(".ttcb-header-title");
588
+ e && t.chatbotName && (e.textContent = t.chatbotName), t.welcomeMessage ? this.appendMessage({ type: "ai", content: t.welcomeMessage }) : this.showEmptyState(t.chatbotName ?? "AI Assistant");
589
+ }
590
+ showBlocked(t) {
591
+ this.messagesContainer.innerHTML = "";
592
+ const e = document.createElement("div");
593
+ e.className = "ttcb-blocked";
594
+ const n = document.createElement("div");
595
+ n.className = "ttcb-blocked-icon";
596
+ const s = document.createElement("p");
597
+ s.className = "ttcb-blocked-title";
598
+ const a = document.createElement("p");
599
+ a.className = "ttcb-blocked-desc", t === "no-prompt" ? (n.innerHTML = f(), s.textContent = "Business Context Required", a.textContent = "Add a system prompt for this agent in the admin panel.") : t === "no-directories" ? (n.innerHTML = f(), s.textContent = "Knowledge Base Required", a.textContent = "Assign at least one knowledge-base directory to this agent in the admin panel.") : (n.innerHTML = N(), s.textContent = "Chatbot Settings Required", a.textContent = "The chatbot has not been configured yet. Please set it up in the admin panel."), e.appendChild(n), e.appendChild(s), e.appendChild(a), this.messagesContainer.appendChild(e);
600
+ }
601
+ clearMessages(t) {
602
+ this.messagesContainer.innerHTML = "", this.showEmptyState(t);
603
+ }
604
+ destroy() {
605
+ this.destroyed = !0, this.host.remove();
606
+ }
607
+ scrollToBottom() {
608
+ this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;
609
+ }
610
+ }
611
+ const R = {
612
+ apiBaseUrl: E,
613
+ chatbotName: "AI Assistant",
614
+ primaryColor: "#2563EB",
615
+ theme: "auto",
616
+ position: "bottom-right",
617
+ welcomeMessage: "",
618
+ placeholder: "Type your message…",
619
+ autoOpen: !1,
620
+ sessionId: "",
621
+ requestTimeoutMs: 3e4
622
+ };
623
+ function U(i) {
624
+ return Object.fromEntries(
625
+ Object.entries(i).filter(([, t]) => t !== void 0)
626
+ );
627
+ }
628
+ let j = 0;
629
+ const w = () => Date.now() * 1e3 + j++ % 1e3;
630
+ class q {
631
+ constructor() {
632
+ this.emitter = new k(), this.session = new T(), this.widget = null, this.callerSetName = !1, this.initToken = 0, this._state = {
633
+ isOpen: !1,
634
+ isLoading: !1,
635
+ isReady: !1,
636
+ blockReason: null,
637
+ messages: [],
638
+ sessionId: null,
639
+ error: null
640
+ };
641
+ }
642
+ /**
643
+ * The single place state is written. Replaces `_state` rather than mutating it
644
+ * and announces the change, so subscribers (React especially) stay in sync with
645
+ * everything — not just the transitions that have a dedicated event.
646
+ */
647
+ setState(t) {
648
+ this._state = { ...this._state, ...t }, this.emitter.emit("state-change", this._state);
649
+ }
650
+ async init(t) {
651
+ var l, r, o;
652
+ if (!t.apiKey)
653
+ throw new Error("[onedeskpro-chatbot] `apiKey` is required to initialise the chatbot.");
654
+ const e = ++this.initToken;
655
+ (l = this.widget) == null || l.destroy(), this.widget = null, this.callerSetName = t.chatbotName !== void 0, this.options = { ...R, ...U(t) }, this.apiClient = new C({
656
+ baseUrl: this.options.apiBaseUrl,
657
+ apiKey: this.options.apiKey,
658
+ timeoutMs: this.options.requestTimeoutMs
659
+ });
660
+ const n = this.session.init(this.options.sessionId || void 0);
661
+ this.setState({ sessionId: n }), typeof window < "u" && typeof document < "u" && (this.widget = new z(this.options, {
662
+ onSend: (c) => void this.sendMessage(c),
663
+ onOpen: () => {
664
+ this.setState({ isOpen: !0 }), this.emitter.emit("open");
665
+ },
666
+ onClose: () => {
667
+ this.setState({ isOpen: !1 }), this.emitter.emit("close");
668
+ },
669
+ onReset: () => this.resetSession()
670
+ }));
671
+ const s = await this.fetchConfig();
672
+ if (e !== this.initToken) return;
673
+ s && !this.callerSetName && s.agentName.trim() && (this.options.chatbotName = s.agentName);
674
+ const a = (s == null ? void 0 : s.blockReason) ?? null;
675
+ if (this.setState({ blockReason: a, isReady: a === null }), a)
676
+ (r = this.widget) == null || r.showBlocked(a);
677
+ else if ((o = this.widget) == null || o.readyToChat(this.options), await this.loadHistory(e), e !== this.initToken) return;
678
+ this.emitter.emit("ready");
679
+ }
680
+ /** Returns null on failure — a network blip must not block the user from trying. */
681
+ async fetchConfig() {
682
+ try {
683
+ return await this.apiClient.fetchConfig();
684
+ } catch {
685
+ return null;
686
+ }
687
+ }
688
+ async sendMessage(t) {
689
+ var a, l, r, o;
690
+ const e = this.session.get();
691
+ if (!this.apiClient || !e)
692
+ throw new Error("[onedeskpro-chatbot] Call init() before sendMessage().");
693
+ const n = t.trim();
694
+ if (!n || this._state.isLoading) return;
695
+ const s = {
696
+ id: w(),
697
+ sessionId: e,
698
+ message: { type: "human", content: n }
699
+ };
700
+ this.setState({
701
+ messages: [...this._state.messages, s],
702
+ isLoading: !0,
703
+ error: null
704
+ }), (a = this.widget) == null || a.appendMessage(s), (l = this.widget) == null || l.setLoading(!0), this.emitter.emit("message", s);
705
+ try {
706
+ const c = await this.apiClient.sendMessage({ chatInput: n, sessionId: e }), d = {
707
+ id: w(),
708
+ sessionId: e,
709
+ message: { type: "ai", content: c.text }
710
+ };
711
+ this.setState({ messages: [...this._state.messages, d], isLoading: !1 }), (r = this.widget) == null || r.appendMessage(d), this.emitter.emit("message", d);
712
+ } catch (c) {
713
+ const d = c instanceof Error ? c : new Error(String(c));
714
+ this.setState({ error: d.message, isLoading: !1 }), this.emitter.emit("error", d);
715
+ } finally {
716
+ (o = this.widget) == null || o.setLoading(!1);
717
+ }
718
+ }
719
+ async loadHistory(t) {
720
+ var n;
721
+ const e = this.session.get();
722
+ if (e)
723
+ try {
724
+ const s = await this.apiClient.fetchHistory(e);
725
+ if (t !== this.initToken) return;
726
+ this.setState({ messages: s }), s.length > 0 && ((n = this.widget) == null || n.setMessages(s));
727
+ } catch {
728
+ }
729
+ }
730
+ on(t, e) {
731
+ return this.emitter.on(t, e);
732
+ }
733
+ resetSession() {
734
+ var e;
735
+ if (!this._state.isReady) return;
736
+ const t = this.session.reset();
737
+ this.setState({ sessionId: t, messages: [], error: null }), (e = this.widget) == null || e.clearMessages(this.options.chatbotName), this.emitter.emit("session-reset");
738
+ }
739
+ open() {
740
+ var t;
741
+ (t = this.widget) == null || t.open();
742
+ }
743
+ close() {
744
+ var t;
745
+ (t = this.widget) == null || t.close();
746
+ }
747
+ toggle() {
748
+ var t;
749
+ (t = this.widget) == null || t.toggle();
750
+ }
751
+ getState() {
752
+ return { ...this._state };
753
+ }
754
+ /**
755
+ * Unmounts the widget and cancels any in-flight init. Subscriptions are left
756
+ * alone on purpose: they belong to whoever registered them, and tearing them
757
+ * down here would silently kill listeners this instance does not own — every
758
+ * useChatbot() in the tree, for one. They are released with the instance.
759
+ */
760
+ destroy() {
761
+ var t;
762
+ this.initToken++, (t = this.widget) == null || t.destroy(), this.widget = null, this.setState({ isOpen: !1, isLoading: !1, isReady: !1 });
763
+ }
764
+ }
765
+ let b = null;
766
+ const K = {
767
+ async init(i) {
768
+ return b = new q(), await b.init(i), b;
769
+ },
770
+ getInstance() {
771
+ return b;
772
+ }
773
+ };
774
+ export {
775
+ C as ApiClient,
776
+ q as ChatbotCore,
777
+ E as DEFAULT_API_BASE_URL,
778
+ D as DEFAULT_REQUEST_TIMEOUT_MS,
779
+ k as EventEmitter,
780
+ K as OnedeskProChatbot,
781
+ T as SessionManager
782
+ };