@cas0570/chat-widget 0.0.1 → 0.0.2

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.
Files changed (38) hide show
  1. package/README.md +55 -5
  2. package/dist/geoapps-chat-widget.embed.js +837 -0
  3. package/dist/style.css +1 -1
  4. package/package.json +81 -80
  5. package/dist/components/ChatBubble.d.ts +0 -8
  6. package/dist/components/ChatWidget.d.ts +0 -3
  7. package/dist/components/ChatWindow.d.ts +0 -18
  8. package/dist/components/Icons.d.ts +0 -26
  9. package/dist/components/MarkdownRenderer.d.ts +0 -6
  10. package/dist/components/MessageInput.d.ts +0 -8
  11. package/dist/components/MessageList.d.ts +0 -9
  12. package/dist/components/PopoutWindow.d.ts +0 -25
  13. package/dist/components/ToggleButton.d.ts +0 -9
  14. package/dist/components/TypingIndicator.d.ts +0 -1
  15. package/dist/components/__tests__/ChatBubble.test.d.ts +0 -1
  16. package/dist/components/__tests__/ChatWidget.test.d.ts +0 -1
  17. package/dist/components/__tests__/ChatWindow.test.d.ts +0 -1
  18. package/dist/components/__tests__/Icons.test.d.ts +0 -1
  19. package/dist/components/__tests__/MarkdownRenderer.test.d.ts +0 -1
  20. package/dist/components/__tests__/MessageInput.test.d.ts +0 -1
  21. package/dist/components/__tests__/MessageList.test.d.ts +0 -1
  22. package/dist/components/__tests__/PopoutWindow.test.d.ts +0 -1
  23. package/dist/components/__tests__/ToggleButton.test.d.ts +0 -1
  24. package/dist/components/__tests__/TypingIndicator.test.d.ts +0 -1
  25. package/dist/components/__tests__/index.test.d.ts +0 -1
  26. package/dist/components/index.d.ts +0 -9
  27. package/dist/embed.d.ts +0 -17
  28. package/dist/geoapps-chat-widget.js +0 -1471
  29. package/dist/geoapps-chat-widget.umd.cjs +0 -597
  30. package/dist/hooks/__tests__/useChat.test.d.ts +0 -1
  31. package/dist/hooks/useChat.d.ts +0 -17
  32. package/dist/index.d.ts +0 -9
  33. package/dist/main.d.ts +0 -1
  34. package/dist/test/setup.d.ts +0 -1
  35. package/dist/types.d.ts +0 -70
  36. package/dist/utils/__tests__/chatPersistence.test.d.ts +0 -1
  37. package/dist/utils/chatPersistence.d.ts +0 -44
  38. package/dist/utils/index.d.ts +0 -1
@@ -1,1471 +0,0 @@
1
- import { jsx as s, jsxs as b } from "react/jsx-runtime";
2
- import J, { useRef as R, useState as I, useEffect as L, useCallback as A } from "react";
3
- import ie from "react-dom/client";
4
- const le = "gcw_chat_", ce = "gcw_popout_", de = "gcw_read_";
5
- let Q = !1;
6
- function Y(e) {
7
- return `${le}${e || "default"}`;
8
- }
9
- function ue(e, n, t) {
10
- if (!Q)
11
- try {
12
- Q = !0;
13
- const a = {
14
- messages: e.map((r) => ({
15
- id: r.id,
16
- role: r.role,
17
- content: r.content,
18
- timestamp: r.timestamp.toISOString(),
19
- sources: r.sources
20
- })),
21
- sessionId: n,
22
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
23
- };
24
- localStorage.setItem(Y(t), JSON.stringify(a));
25
- } catch (a) {
26
- console.warn("Failed to save chat state:", a);
27
- } finally {
28
- Q = !1;
29
- }
30
- }
31
- function ne(e) {
32
- try {
33
- const n = localStorage.getItem(Y(e));
34
- if (!n) return null;
35
- const t = JSON.parse(n), a = new Date(t.lastUpdated);
36
- return (Date.now() - a.getTime()) / (1e3 * 60 * 60) > 24 ? (oe(e), null) : {
37
- messages: t.messages.map((o) => ({
38
- id: o.id,
39
- role: o.role,
40
- content: o.content,
41
- timestamp: new Date(o.timestamp),
42
- sources: o.sources
43
- })),
44
- sessionId: t.sessionId
45
- };
46
- } catch (n) {
47
- return console.warn("Failed to load chat state:", n), null;
48
- }
49
- }
50
- function oe(e) {
51
- try {
52
- localStorage.removeItem(Y(e));
53
- } catch (n) {
54
- console.warn("Failed to clear chat state:", n);
55
- }
56
- }
57
- function pe(e, n) {
58
- const t = Y(e), a = (r) => {
59
- if (r.key === t) {
60
- if (!r.newValue) {
61
- n(null);
62
- return;
63
- }
64
- try {
65
- const o = JSON.parse(r.newValue);
66
- n({
67
- messages: o.messages.map((i) => ({
68
- id: i.id,
69
- role: i.role,
70
- content: i.content,
71
- timestamp: new Date(i.timestamp),
72
- sources: i.sources
73
- })),
74
- sessionId: o.sessionId
75
- });
76
- } catch {
77
- }
78
- }
79
- };
80
- return window.addEventListener("storage", a), () => window.removeEventListener("storage", a);
81
- }
82
- function te(e) {
83
- return `${ce}${e || "default"}`;
84
- }
85
- function K(e, n) {
86
- try {
87
- e ? localStorage.setItem(te(n), "true") : localStorage.removeItem(te(n));
88
- } catch (t) {
89
- console.warn("Failed to save popout state:", t);
90
- }
91
- }
92
- function me(e) {
93
- try {
94
- return localStorage.getItem(te(e)) === "true";
95
- } catch {
96
- return !1;
97
- }
98
- }
99
- function se(e) {
100
- return `${de}${e || "default"}`;
101
- }
102
- function Z(e, n) {
103
- try {
104
- e ? localStorage.setItem(se(n), e) : localStorage.removeItem(se(n));
105
- } catch (t) {
106
- console.warn("Failed to save read state:", t);
107
- }
108
- }
109
- function he(e) {
110
- try {
111
- return localStorage.getItem(se(e));
112
- } catch {
113
- return null;
114
- }
115
- }
116
- function ge(e) {
117
- const { apiUrl: n, tenantId: t, sessionId: a, headers: r = {}, onError: o, persist: i = !0 } = e, d = R(!1), [p, l] = I(() => {
118
- if (i) {
119
- const c = ne(t);
120
- if (c) return c.messages;
121
- }
122
- return [];
123
- }), [g, v] = I(!1), [E, x] = I(null), [m, f] = I(() => {
124
- if (i) {
125
- const c = ne(t);
126
- if (c) return c.sessionId;
127
- }
128
- return a || null;
129
- }), B = R(null);
130
- L(() => {
131
- d.current || i && p.length > 0 && ue(p, m, t);
132
- }, [p, m, t, i]), L(() => i ? pe(t, (_) => {
133
- d.current = !0, _ ? (l(_.messages), f(_.sessionId)) : (l([]), f(null)), setTimeout(() => {
134
- d.current = !1;
135
- }, 0);
136
- }) : void 0, [t, i]);
137
- const O = () => `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, X = A(async (c) => {
138
- var P;
139
- if (!c.trim() || g) return;
140
- B.current && B.current.abort(), B.current = new AbortController();
141
- const _ = {
142
- id: O(),
143
- role: "user",
144
- content: c.trim(),
145
- timestamp: /* @__PURE__ */ new Date()
146
- };
147
- l((u) => [...u, _]), v(!0), x(null);
148
- const $ = O(), W = {
149
- id: $,
150
- role: "assistant",
151
- content: "",
152
- timestamp: /* @__PURE__ */ new Date(),
153
- isStreaming: !0
154
- };
155
- l((u) => [...u, W]);
156
- try {
157
- const u = await fetch(n, {
158
- method: "POST",
159
- headers: {
160
- "Content-Type": "application/json",
161
- ...t && { "X-Tenant-ID": t },
162
- ...r
163
- },
164
- body: JSON.stringify({
165
- message: c.trim(),
166
- session_id: m
167
- }),
168
- signal: B.current.signal
169
- });
170
- if (!u.ok) {
171
- const h = await u.json().catch(() => ({}));
172
- throw new Error(h.detail || `Request failed with status ${u.status}`);
173
- }
174
- const y = u.headers.get("content-type");
175
- if (y != null && y.includes("text/event-stream"))
176
- await q(u, $);
177
- else {
178
- const h = await u.json(), C = (P = h.message.citations) == null ? void 0 : P.map((k) => ({
179
- title: k.title,
180
- url: k.url,
181
- relevance: k.relevance_score
182
- }));
183
- l((k) => k.map(
184
- (j) => j.id === $ ? {
185
- ...j,
186
- content: h.message.content,
187
- isStreaming: !1,
188
- sources: C
189
- } : j
190
- )), h.session_id && f(h.session_id);
191
- }
192
- } catch (u) {
193
- if (u.name === "AbortError")
194
- return;
195
- const y = u instanceof Error ? u : new Error("An error occurred");
196
- x(y), o == null || o(y), l((h) => h.map(
197
- (C) => C.id === $ ? {
198
- ...C,
199
- content: "Sorry, an error occurred. Please try again.",
200
- isStreaming: !1
201
- } : C
202
- ));
203
- } finally {
204
- v(!1), B.current = null;
205
- }
206
- }, [n, t, m, r, g, o]), q = async (c, _) => {
207
- var u;
208
- const $ = (u = c.body) == null ? void 0 : u.getReader();
209
- if (!$) throw new Error("No response body");
210
- const W = new TextDecoder();
211
- let P = "";
212
- try {
213
- for (; ; ) {
214
- const { done: y, value: h } = await $.read();
215
- if (y) break;
216
- const k = W.decode(h, { stream: !0 }).split(`
217
- `);
218
- for (const j of k)
219
- if (j.startsWith("data: ")) {
220
- const F = j.slice(6);
221
- if (F === "[DONE]") continue;
222
- try {
223
- const S = JSON.parse(F);
224
- S.type === "content" && S.content ? (P += S.content, l((V) => V.map(
225
- (D) => D.id === _ ? { ...D, content: P } : D
226
- ))) : S.type === "sources" && S.sources ? l((V) => V.map(
227
- (D) => D.id === _ ? { ...D, sources: S.sources } : D
228
- )) : S.conversation_id && f(S.conversation_id);
229
- } catch {
230
- }
231
- }
232
- }
233
- } finally {
234
- l((y) => y.map(
235
- (h) => h.id === _ ? { ...h, isStreaming: !1 } : h
236
- ));
237
- }
238
- }, T = A(() => {
239
- l([]), f(null), x(null), i && oe(t);
240
- }, [i, t]);
241
- return {
242
- messages: p,
243
- isLoading: g,
244
- error: E,
245
- sendMessage: X,
246
- clearMessages: T,
247
- sessionId: m
248
- };
249
- }
250
- let U = null;
251
- function fe(e, n, t) {
252
- const [a, r] = I(() => me(e.tenantId)), o = R(U);
253
- L(() => {
254
- if (a && !o.current) {
255
- const l = window.open("", "GeoAppsChatPopout");
256
- l && l.location.href !== "about:blank" && !l.closed ? (o.current = l, U = l) : (r(!1), K(!1, e.tenantId));
257
- }
258
- }, [a, e.tenantId]), L(() => {
259
- if (!o.current) return;
260
- const l = setInterval(() => {
261
- var g;
262
- (g = o.current) != null && g.closed && (r(!1), o.current = null, U = null, K(!1, e.tenantId));
263
- }, 500);
264
- return () => clearInterval(l);
265
- }, [e.tenantId, t]);
266
- const i = A(() => {
267
- if (o.current && !o.current.closed) {
268
- o.current.focus();
269
- return;
270
- }
271
- const l = {
272
- small: { width: 380, height: 520 },
273
- medium: { width: 440, height: 660 },
274
- large: { width: 560, height: 780 },
275
- fullscreen: { width: 700, height: 800 }
276
- }, { width: g, height: v } = l[n], E = (window.screen.width - g) / 2, x = (window.screen.height - v) / 2, m = window.open(
277
- "",
278
- "GeoAppsChatPopout",
279
- `width=${g},height=${v},left=${E},top=${x},resizable=yes,scrollbars=no`
280
- );
281
- if (!m) {
282
- console.error("Failed to open popup window. Check if popups are blocked.");
283
- return;
284
- }
285
- m.__CHAT_CONFIG__ = e, m.document.write(we(e)), m.document.close(), o.current = m, U = m, r(!0), K(!0, e.tenantId);
286
- }, [e, n]), d = A(() => {
287
- o.current && !o.current.closed && o.current.close(), o.current = null, U = null, r(!1), K(!1, e.tenantId);
288
- }, [e.tenantId]), p = A(() => {
289
- o.current && !o.current.closed && o.current.focus();
290
- }, []);
291
- return {
292
- isPoppedOut: a,
293
- openPopout: i,
294
- closePopout: d,
295
- focusPopout: p
296
- };
297
- }
298
- function we(e) {
299
- const n = e.messages.map((t) => ({
300
- ...t,
301
- timestamp: t.timestamp.toISOString()
302
- }));
303
- return `
304
- <!DOCTYPE html>
305
- <html lang="en">
306
- <head>
307
- <meta charset="UTF-8">
308
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
309
- <title>${G(e.title)}</title>
310
- <style>
311
- * { margin: 0; padding: 0; box-sizing: border-box; }
312
- html, body, #chat-root { height: 100%; width: 100%; }
313
- body {
314
- font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
315
- background: #f5f5f5;
316
- }
317
-
318
- /* Modern chat widget styling */
319
- .popout-container {
320
- height: 100%;
321
- display: flex;
322
- flex-direction: column;
323
- background: white;
324
- border-radius: 0;
325
- }
326
- .popout-header {
327
- padding: 0.75rem 1rem;
328
- color: white;
329
- display: flex;
330
- align-items: center;
331
- gap: 0.75rem;
332
- flex-shrink: 0;
333
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
334
- }
335
- .popout-header-icon {
336
- width: 2.5rem;
337
- height: 2.5rem;
338
- border-radius: 50%;
339
- background: rgba(255,255,255,0.2);
340
- display: flex;
341
- align-items: center;
342
- justify-content: center;
343
- flex-shrink: 0;
344
- }
345
- .popout-header-icon svg {
346
- width: 1.25rem;
347
- height: 1.25rem;
348
- }
349
- .popout-header-info {
350
- flex: 1;
351
- min-width: 0;
352
- }
353
- .popout-title {
354
- font-weight: 600;
355
- font-size: 0.875rem;
356
- }
357
- .popout-subtitle {
358
- font-size: 0.75rem;
359
- opacity: 0.9;
360
- }
361
- .popout-header-actions {
362
- display: flex;
363
- gap: 0.25rem;
364
- }
365
- .popout-header-btn {
366
- width: 2rem;
367
- height: 2rem;
368
- border-radius: 50%;
369
- border: none;
370
- background: rgba(255,255,255,0.1);
371
- color: white;
372
- cursor: pointer;
373
- display: flex;
374
- align-items: center;
375
- justify-content: center;
376
- transition: background 0.2s;
377
- }
378
- .popout-header-btn:hover {
379
- background: rgba(255,255,255,0.25);
380
- }
381
- .popout-header-btn svg {
382
- width: 1rem;
383
- height: 1rem;
384
- }
385
- .popout-messages {
386
- flex: 1;
387
- overflow-y: auto;
388
- padding: 1rem;
389
- display: flex;
390
- flex-direction: column;
391
- gap: 1rem;
392
- background: #fafafa;
393
- }
394
- .popout-messages::-webkit-scrollbar {
395
- width: 6px;
396
- }
397
- .popout-messages::-webkit-scrollbar-track {
398
- background: transparent;
399
- }
400
- .popout-messages::-webkit-scrollbar-thumb {
401
- background: #d1d5db;
402
- border-radius: 3px;
403
- }
404
- .popout-messages::-webkit-scrollbar-thumb:hover {
405
- background: #9ca3af;
406
- }
407
- /* Empty state */
408
- .popout-empty {
409
- flex: 1;
410
- display: flex;
411
- flex-direction: column;
412
- align-items: center;
413
- justify-content: center;
414
- padding: 2rem;
415
- color: #9ca3af;
416
- text-align: center;
417
- }
418
- .popout-empty svg {
419
- width: 4rem;
420
- height: 4rem;
421
- margin-bottom: 1rem;
422
- opacity: 0.5;
423
- }
424
- .popout-empty p {
425
- font-size: 0.875rem;
426
- }
427
- .popout-message-wrapper {
428
- display: flex;
429
- flex-direction: column;
430
- }
431
- .popout-message-wrapper.user {
432
- align-items: flex-end;
433
- }
434
- .popout-message-wrapper.assistant {
435
- align-items: flex-start;
436
- }
437
- .popout-message {
438
- max-width: 85%;
439
- padding: 0.75rem 1rem;
440
- border-radius: 1rem;
441
- font-size: 0.875rem;
442
- line-height: 1.5;
443
- word-wrap: break-word;
444
- }
445
- .popout-message.user {
446
- color: white;
447
- border-bottom-right-radius: 0.25rem;
448
- }
449
- .popout-message.assistant {
450
- background: #f1f5f9;
451
- color: #1f2937;
452
- border-bottom-left-radius: 0.25rem;
453
- box-shadow: 0 1px 2px rgba(0,0,0,0.05);
454
- }
455
- .popout-message p { margin-bottom: 0.5rem; }
456
- .popout-message p:last-child { margin-bottom: 0; }
457
- .popout-message strong { font-weight: 600; }
458
- .popout-message em { font-style: italic; }
459
- .popout-message code {
460
- background: rgba(0,0,0,0.06);
461
- padding: 0.125rem 0.375rem;
462
- border-radius: 0.25rem;
463
- font-size: 0.8125rem;
464
- font-family: 'SF Mono', Monaco, Consolas, monospace;
465
- }
466
- .popout-message.user code {
467
- background: rgba(255,255,255,0.2);
468
- }
469
- .popout-message pre {
470
- background: #1f2937;
471
- color: #f3f4f6;
472
- padding: 0.75rem;
473
- border-radius: 0.5rem;
474
- font-size: 0.75rem;
475
- overflow-x: auto;
476
- margin: 0.5rem 0;
477
- }
478
- .popout-message pre code {
479
- background: none;
480
- padding: 0;
481
- font-size: inherit;
482
- }
483
- .popout-message ul, .popout-message ol {
484
- padding-left: 1.25rem;
485
- margin: 0.5rem 0;
486
- }
487
- .popout-message li { margin: 0.25rem 0; }
488
- .popout-message a {
489
- color: #2563eb;
490
- text-decoration: underline;
491
- }
492
- .popout-message.user a {
493
- color: rgba(255,255,255,0.9);
494
- }
495
- .popout-input-container {
496
- padding: 0.75rem 1rem 1rem;
497
- border-top: 1px solid #e5e7eb;
498
- flex-shrink: 0;
499
- background: white;
500
- }
501
- .popout-input-wrapper {
502
- display: flex;
503
- gap: 0.5rem;
504
- align-items: flex-end;
505
- }
506
- .popout-textarea {
507
- flex: 1;
508
- padding: 0.625rem 1rem;
509
- border: 1px solid #e5e7eb;
510
- border-radius: 1.25rem;
511
- resize: none;
512
- font-size: 0.875rem;
513
- font-family: inherit;
514
- min-height: 44px;
515
- max-height: 120px;
516
- outline: none;
517
- transition: border-color 0.2s, box-shadow 0.2s;
518
- }
519
- .popout-textarea:focus {
520
- border-color: ${e.primaryColor};
521
- box-shadow: 0 0 0 3px ${e.primaryColor}15;
522
- }
523
- .popout-textarea::placeholder {
524
- color: #9ca3af;
525
- }
526
- .popout-send-btn {
527
- width: 2.5rem;
528
- height: 2.5rem;
529
- border-radius: 0.75rem;
530
- border: none;
531
- color: white;
532
- cursor: pointer;
533
- display: flex;
534
- align-items: center;
535
- justify-content: center;
536
- flex-shrink: 0;
537
- transition: opacity 0.2s, transform 0.1s;
538
- }
539
- .popout-send-btn:hover { opacity: 0.9; }
540
- .popout-send-btn:active { transform: scale(0.95); }
541
- .popout-send-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
542
- .popout-send-btn svg { width: 1.25rem; height: 1.25rem; }
543
- .popout-input-hint {
544
- font-size: 0.625rem;
545
- color: #9ca3af;
546
- text-align: center;
547
- margin-top: 0.5rem;
548
- }
549
- .popout-timestamp {
550
- font-size: 0.625rem;
551
- color: #9ca3af;
552
- margin-top: 0.25rem;
553
- padding: 0 0.25rem;
554
- }
555
- .popout-loading {
556
- display: flex;
557
- gap: 0.25rem;
558
- padding: 0.25rem 0;
559
- }
560
- .popout-loading span {
561
- width: 0.5rem;
562
- height: 0.5rem;
563
- background: #9ca3af;
564
- border-radius: 50%;
565
- animation: bounce 1.4s infinite ease-in-out both;
566
- }
567
- .popout-loading span:nth-child(1) { animation-delay: -0.32s; }
568
- .popout-loading span:nth-child(2) { animation-delay: -0.16s; }
569
- @keyframes bounce {
570
- 0%, 80%, 100% { transform: scale(0); }
571
- 40% { transform: scale(1); }
572
- }
573
- /* Typing indicator */
574
- .typing-indicator {
575
- display: flex;
576
- align-items: flex-start;
577
- }
578
- .typing-bubble {
579
- background: white;
580
- padding: 0.75rem 1rem;
581
- border-radius: 1rem;
582
- border-bottom-left-radius: 0.25rem;
583
- box-shadow: 0 1px 2px rgba(0,0,0,0.05);
584
- display: flex;
585
- gap: 0.25rem;
586
- }
587
- .typing-bubble span {
588
- width: 0.5rem;
589
- height: 0.5rem;
590
- background: #9ca3af;
591
- border-radius: 50%;
592
- animation: bounce 1.4s infinite ease-in-out both;
593
- }
594
- .typing-bubble span:nth-child(1) { animation-delay: -0.32s; }
595
- .typing-bubble span:nth-child(2) { animation-delay: -0.16s; }
596
- </style>
597
- </head>
598
- <body>
599
- <div id="chat-root">
600
- <div class="popout-container">
601
- <div class="popout-header" style="background: ${e.primaryColor}">
602
- <div class="popout-header-icon">
603
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
604
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
605
- </svg>
606
- </div>
607
- <div class="popout-header-info">
608
- <div class="popout-title">${G(e.title)}</div>
609
- <div class="popout-subtitle">${G(e.subtitle)}</div>
610
- </div>
611
- <div class="popout-header-actions">
612
- <button class="popout-header-btn" id="clear-btn" title="Clear conversation">
613
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
614
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
615
- </svg>
616
- </button>
617
- </div>
618
- </div>
619
- <div class="popout-messages" id="messages-container"></div>
620
- <div class="popout-input-container">
621
- <div class="popout-input-wrapper">
622
- <textarea
623
- class="popout-textarea"
624
- id="message-input"
625
- placeholder="${G(e.placeholder)}"
626
- rows="1"
627
- ></textarea>
628
- <button
629
- class="popout-send-btn"
630
- id="send-btn"
631
- style="background: ${e.primaryColor}"
632
- >
633
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
634
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
635
- </svg>
636
- </button>
637
- </div>
638
- <p class="popout-input-hint">Press Enter to send, Shift+Enter for new line</p>
639
- </div>
640
- </div>
641
- </div>
642
- <script>
643
- (function() {
644
- const config = ${JSON.stringify({
645
- apiUrl: e.apiUrl,
646
- tenantId: e.tenantId,
647
- sessionId: e.sessionId,
648
- primaryColor: e.primaryColor,
649
- headers: e.headers || {}
650
- })};
651
-
652
- const STORAGE_KEY = 'gcw_chat_' + (config.tenantId || 'default');
653
-
654
- let messages = ${JSON.stringify(n)}.map(m => ({
655
- ...m,
656
- timestamp: new Date(m.timestamp)
657
- }));
658
- let sessionId = config.sessionId;
659
- let isLoading = false;
660
-
661
- const messagesContainer = document.getElementById('messages-container');
662
- const messageInput = document.getElementById('message-input');
663
- const sendBtn = document.getElementById('send-btn');
664
-
665
- // Save state to localStorage for sync with main window
666
- function saveState() {
667
- try {
668
- const state = {
669
- messages: messages.map(m => ({
670
- id: m.id,
671
- role: m.role,
672
- content: m.content,
673
- timestamp: m.timestamp.toISOString(),
674
- sources: m.sources
675
- })),
676
- sessionId: sessionId,
677
- lastUpdated: new Date().toISOString()
678
- };
679
- localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
680
- } catch (e) {
681
- console.warn('Failed to save state:', e);
682
- }
683
- }
684
-
685
- function escapeHtml(text) {
686
- const div = document.createElement('div');
687
- div.textContent = text;
688
- return div.innerHTML;
689
- }
690
-
691
- function parseMarkdown(text) {
692
- if (!text) return '';
693
-
694
- // Code blocks
695
- text = text.replace(/\`\`\`([\\s\\S]*?)\`\`\`/g, '<pre><code>$1</code></pre>');
696
-
697
- // Inline code
698
- text = text.replace(/\`([^\`]+)\`/g, '<code>$1</code>');
699
-
700
- // Bold
701
- text = text.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');
702
-
703
- // Italic
704
- text = text.replace(/\\*([^*]+?)\\*/g, '<em>$1</em>');
705
-
706
- // Links
707
- text = text.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
708
-
709
- // Lists
710
- const lines = text.split('\\n');
711
- let result = [];
712
- let inList = false;
713
- let listType = null;
714
-
715
- for (const line of lines) {
716
- const ulMatch = line.match(/^[\\s]*[-*•]\\s+(.+)/);
717
- const olMatch = line.match(/^[\\s]*(\\d+)[.)]\\s+(.+)/);
718
-
719
- if (ulMatch) {
720
- if (!inList || listType !== 'ul') {
721
- if (inList) result.push('</' + listType + '>');
722
- result.push('<ul>');
723
- inList = true;
724
- listType = 'ul';
725
- }
726
- result.push('<li>' + ulMatch[1] + '</li>');
727
- } else if (olMatch) {
728
- if (!inList || listType !== 'ol') {
729
- if (inList) result.push('</' + listType + '>');
730
- result.push('<ol>');
731
- inList = true;
732
- listType = 'ol';
733
- }
734
- result.push('<li>' + olMatch[2] + '</li>');
735
- } else {
736
- if (inList) {
737
- result.push('</' + listType + '>');
738
- inList = false;
739
- listType = null;
740
- }
741
- if (line.trim()) {
742
- result.push('<p>' + line + '</p>');
743
- }
744
- }
745
- }
746
- if (inList) result.push('</' + listType + '>');
747
-
748
- return result.join('');
749
- }
750
-
751
- function formatTime(date) {
752
- return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
753
- }
754
-
755
- function renderMessages() {
756
- // Show empty state if no messages
757
- if (messages.length === 0) {
758
- messagesContainer.innerHTML = \`
759
- <div class="popout-empty">
760
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
761
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
762
- </svg>
763
- <p>Start a conversation by typing a message below</p>
764
- </div>
765
- \`;
766
- return;
767
- }
768
-
769
- messagesContainer.innerHTML = messages.map(msg => {
770
- const isUser = msg.role === 'user';
771
- const content = msg.isStreaming && !msg.content
772
- ? '<div class="popout-loading"><span></span><span></span><span></span></div>'
773
- : parseMarkdown(escapeHtml(msg.content));
774
-
775
- return \`
776
- <div class="popout-message-wrapper \${msg.role}">
777
- <div class="popout-message \${msg.role}" \${isUser ? 'style="background: ' + config.primaryColor + '"' : ''}>
778
- \${content}
779
- </div>
780
- <div class="popout-timestamp">\${formatTime(msg.timestamp)}</div>
781
- </div>
782
- \`;
783
- }).join('');
784
-
785
- messagesContainer.scrollTop = messagesContainer.scrollHeight;
786
- }
787
-
788
- async function sendMessage(content) {
789
- if (!content.trim() || isLoading) return;
790
-
791
- const userMsg = {
792
- id: 'msg_' + Date.now(),
793
- role: 'user',
794
- content: content.trim(),
795
- timestamp: new Date()
796
- };
797
- messages.push(userMsg);
798
-
799
- const assistantMsgId = 'msg_' + Date.now() + '_assistant';
800
- const assistantMsg = {
801
- id: assistantMsgId,
802
- role: 'assistant',
803
- content: '',
804
- timestamp: new Date(),
805
- isStreaming: true
806
- };
807
- messages.push(assistantMsg);
808
-
809
- isLoading = true;
810
- renderMessages();
811
- messageInput.value = '';
812
- sendBtn.disabled = true;
813
-
814
- try {
815
- const response = await fetch(config.apiUrl, {
816
- method: 'POST',
817
- headers: {
818
- 'Content-Type': 'application/json',
819
- ...(config.tenantId && { 'X-Tenant-ID': config.tenantId }),
820
- ...config.headers
821
- },
822
- body: JSON.stringify({
823
- message: content.trim(),
824
- session_id: sessionId
825
- })
826
- });
827
-
828
- if (!response.ok) throw new Error('Request failed');
829
-
830
- const data = await response.json();
831
-
832
- const msgIndex = messages.findIndex(m => m.id === assistantMsgId);
833
- if (msgIndex !== -1) {
834
- messages[msgIndex] = {
835
- ...messages[msgIndex],
836
- content: data.message.content,
837
- isStreaming: false
838
- };
839
- }
840
-
841
- if (data.session_id) {
842
- sessionId = data.session_id;
843
- }
844
-
845
- // Save to localStorage after successful response
846
- saveState();
847
- } catch (err) {
848
- const msgIndex = messages.findIndex(m => m.id === assistantMsgId);
849
- if (msgIndex !== -1) {
850
- messages[msgIndex] = {
851
- ...messages[msgIndex],
852
- content: 'Sorry, an error occurred. Please try again.',
853
- isStreaming: false
854
- };
855
- }
856
- } finally {
857
- isLoading = false;
858
- sendBtn.disabled = false;
859
- renderMessages();
860
- saveState(); // Always save state after message exchange
861
- }
862
- }
863
-
864
- // Auto-resize textarea
865
- messageInput.addEventListener('input', function() {
866
- this.style.height = 'auto';
867
- this.style.height = Math.min(this.scrollHeight, 120) + 'px';
868
- });
869
-
870
- // Send on Enter (Shift+Enter for newline)
871
- messageInput.addEventListener('keydown', function(e) {
872
- if (e.key === 'Enter' && !e.shiftKey) {
873
- e.preventDefault();
874
- sendMessage(this.value);
875
- }
876
- });
877
-
878
- sendBtn.addEventListener('click', function() {
879
- sendMessage(messageInput.value);
880
- });
881
-
882
- // Clear conversation button
883
- const clearBtn = document.getElementById('clear-btn');
884
- if (clearBtn) {
885
- clearBtn.addEventListener('click', function() {
886
- if (confirm('Are you sure you want to clear the conversation?')) {
887
- messages = [];
888
- sessionId = null;
889
- saveState();
890
- renderMessages();
891
- }
892
- });
893
- }
894
-
895
- // Initial render
896
- renderMessages();
897
- messageInput.focus();
898
- })();
899
- <\/script>
900
- </body>
901
- </html>
902
- `;
903
- }
904
- function G(e) {
905
- return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
906
- }
907
- function be({ content: e, className: n }) {
908
- if (!e) return null;
909
- const t = ve(e);
910
- return /* @__PURE__ */ s("div", { className: n, children: t.map((a, r) => /* @__PURE__ */ s(J.Fragment, { children: a }, r)) });
911
- }
912
- function ve(e) {
913
- const n = e.split(`
914
- `), t = [];
915
- let a = null;
916
- const r = () => {
917
- a && (a.type === "ul" ? t.push(
918
- /* @__PURE__ */ s("ul", { className: "gcw-md-list gcw-md-list--ul", children: a.items.map((o, i) => /* @__PURE__ */ s("li", { className: "gcw-md-list__item", children: o }, i)) }, `list-${t.length}`)
919
- ) : t.push(
920
- /* @__PURE__ */ s("ol", { className: "gcw-md-list gcw-md-list--ol", children: a.items.map((o, i) => /* @__PURE__ */ s("li", { className: "gcw-md-list__item", children: o }, i)) }, `list-${t.length}`)
921
- ), a = null);
922
- };
923
- for (let o = 0; o < n.length; o++) {
924
- const i = n[o];
925
- if (i.startsWith("#### ")) {
926
- r(), t.push(
927
- /* @__PURE__ */ s("h5", { className: "gcw-md-heading gcw-md-heading--h5", children: z(i.slice(5)) }, o)
928
- );
929
- continue;
930
- }
931
- if (i.startsWith("### ")) {
932
- r(), t.push(
933
- /* @__PURE__ */ s("h4", { className: "gcw-md-heading gcw-md-heading--h4", children: z(i.slice(4)) }, o)
934
- );
935
- continue;
936
- }
937
- if (i.startsWith("## ")) {
938
- r(), t.push(
939
- /* @__PURE__ */ s("h3", { className: "gcw-md-heading gcw-md-heading--h3", children: z(i.slice(3)) }, o)
940
- );
941
- continue;
942
- }
943
- if (i.startsWith("# ")) {
944
- r(), t.push(
945
- /* @__PURE__ */ s("h2", { className: "gcw-md-heading gcw-md-heading--h2", children: z(i.slice(2)) }, o)
946
- );
947
- continue;
948
- }
949
- const d = i.match(/^[\s]*[-*•]\s+(.+)/);
950
- if (d) {
951
- (!a || a.type !== "ul") && (r(), a = { type: "ul", items: [] }), a.items.push(z(d[1]));
952
- continue;
953
- }
954
- const p = i.match(/^[\s]*(\d+)[.)]\s+(.+)/);
955
- if (p) {
956
- (!a || a.type !== "ol") && (r(), a = { type: "ol", items: [] }), a.items.push(z(p[2]));
957
- continue;
958
- }
959
- if (i.startsWith("```")) {
960
- r();
961
- const l = [];
962
- for (o++; o < n.length && !n[o].startsWith("```"); )
963
- l.push(n[o]), o++;
964
- t.push(
965
- /* @__PURE__ */ s("pre", { className: "gcw-md-code-block", children: /* @__PURE__ */ s("code", { children: l.join(`
966
- `) }) }, `code-${t.length}`)
967
- );
968
- continue;
969
- }
970
- if (i.trim() === "") {
971
- r();
972
- continue;
973
- }
974
- r(), t.push(
975
- /* @__PURE__ */ s("p", { className: "gcw-md-paragraph", children: z(i) }, o)
976
- );
977
- }
978
- return r(), t;
979
- }
980
- function z(e) {
981
- if (!e) return null;
982
- const n = [];
983
- let t = e, a = 0;
984
- for (; t.length > 0; ) {
985
- const r = t.match(/^\*\*(.+?)\*\*/);
986
- if (r) {
987
- n.push(/* @__PURE__ */ s("strong", { className: "gcw-md-bold", children: r[1] }, a++)), t = t.slice(r[0].length);
988
- continue;
989
- }
990
- const o = t.match(/^\*([^*]+?)\*/);
991
- if (o) {
992
- n.push(/* @__PURE__ */ s("em", { className: "gcw-md-italic", children: o[1] }, a++)), t = t.slice(o[0].length);
993
- continue;
994
- }
995
- const i = t.match(/^`([^`]+)`/);
996
- if (i) {
997
- n.push(
998
- /* @__PURE__ */ s("code", { className: "gcw-md-code", children: i[1] }, a++)
999
- ), t = t.slice(i[0].length);
1000
- continue;
1001
- }
1002
- const d = t.match(/^\[([^\]]+)\]\(([^)]+)\)/);
1003
- if (d) {
1004
- n.push(
1005
- /* @__PURE__ */ s(
1006
- "a",
1007
- {
1008
- href: d[2],
1009
- target: "_blank",
1010
- rel: "noopener noreferrer",
1011
- className: "gcw-md-link",
1012
- children: d[1]
1013
- },
1014
- a++
1015
- )
1016
- ), t = t.slice(d[0].length);
1017
- continue;
1018
- }
1019
- const p = t.search(/[\*`\[]/);
1020
- if (p === -1) {
1021
- n.push(t);
1022
- break;
1023
- } else p === 0 ? (n.push(t[0]), t = t.slice(1)) : (n.push(t.slice(0, p)), t = t.slice(p));
1024
- }
1025
- return n.length === 1 ? n[0] : n;
1026
- }
1027
- function ye() {
1028
- return /* @__PURE__ */ b("div", { className: "gcw-typing__dots", children: [
1029
- /* @__PURE__ */ s("span", { className: "gcw-typing__dot" }),
1030
- /* @__PURE__ */ s("span", { className: "gcw-typing__dot" }),
1031
- /* @__PURE__ */ s("span", { className: "gcw-typing__dot" })
1032
- ] });
1033
- }
1034
- function ke({ className: e }) {
1035
- return /* @__PURE__ */ s("svg", { className: e, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" }) });
1036
- }
1037
- function xe(e) {
1038
- return e.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
1039
- }
1040
- function Me(e, n) {
1041
- return e.length <= n ? e : e.slice(0, n - 3) + "...";
1042
- }
1043
- function _e({ message: e, primaryColor: n }) {
1044
- const t = e.role === "user", a = t ? "user" : "assistant";
1045
- return /* @__PURE__ */ s("div", { className: `gcw-bubble-row gcw-bubble-row--${a}`, children: /* @__PURE__ */ b("div", { className: `gcw-bubble-wrapper gcw-bubble-wrapper--${a}`, children: [
1046
- /* @__PURE__ */ s(
1047
- "div",
1048
- {
1049
- className: `gcw-bubble gcw-bubble--${a}`,
1050
- style: t ? { backgroundColor: n } : void 0,
1051
- children: e.isStreaming && !e.content ? /* @__PURE__ */ s(ye, {}) : /* @__PURE__ */ s(be, { content: e.content, className: "gcw-content" })
1052
- }
1053
- ),
1054
- e.sources && e.sources.length > 0 && /* @__PURE__ */ s("div", { className: "gcw-sources", children: e.sources.slice(0, 3).map((r, o) => /* @__PURE__ */ b(
1055
- "a",
1056
- {
1057
- href: r.url,
1058
- target: "_blank",
1059
- rel: "noopener noreferrer",
1060
- className: "gcw-source",
1061
- children: [
1062
- /* @__PURE__ */ s(ke, { className: "gcw-source__icon" }),
1063
- Me(r.title, 25)
1064
- ]
1065
- },
1066
- o
1067
- )) }),
1068
- /* @__PURE__ */ s("span", { className: "gcw-bubble__timestamp", children: xe(e.timestamp) })
1069
- ] }) });
1070
- }
1071
- function Ce() {
1072
- return /* @__PURE__ */ s("div", { className: "gcw-typing", children: /* @__PURE__ */ s("div", { className: "gcw-typing__bubble", children: /* @__PURE__ */ b("div", { className: "gcw-typing__dots", children: [
1073
- /* @__PURE__ */ s("span", { className: "gcw-typing__dot" }),
1074
- /* @__PURE__ */ s("span", { className: "gcw-typing__dot" }),
1075
- /* @__PURE__ */ s("span", { className: "gcw-typing__dot" })
1076
- ] }) }) });
1077
- }
1078
- function Se({ messages: e, isLoading: n, primaryColor: t }) {
1079
- var r;
1080
- const a = R(null);
1081
- return L(() => {
1082
- var o;
1083
- (o = a.current) == null || o.scrollIntoView({ behavior: "smooth" });
1084
- }, [e, n]), e.length === 0 && !n ? /* @__PURE__ */ b("div", { className: "gcw-empty", children: [
1085
- /* @__PURE__ */ s("svg", { className: "gcw-empty__icon", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" }) }),
1086
- /* @__PURE__ */ s("p", { className: "gcw-empty__text", children: "Start a conversation by typing a message below" })
1087
- ] }) : /* @__PURE__ */ b("div", { className: "gcw-messages", children: [
1088
- e.map((o) => /* @__PURE__ */ s(
1089
- _e,
1090
- {
1091
- message: o,
1092
- primaryColor: t
1093
- },
1094
- o.id
1095
- )),
1096
- n && ((r = e[e.length - 1]) == null ? void 0 : r.role) !== "assistant" && /* @__PURE__ */ s(Ce, {}),
1097
- /* @__PURE__ */ s("div", { ref: a })
1098
- ] });
1099
- }
1100
- function Ie({ placeholder: e, isLoading: n, onSendMessage: t, primaryColor: a }) {
1101
- const [r, o] = I(""), i = R(null);
1102
- L(() => {
1103
- const l = i.current;
1104
- l && (l.style.height = "auto", l.style.height = `${Math.min(l.scrollHeight, 120)}px`);
1105
- }, [r]);
1106
- const d = () => {
1107
- r.trim() && !n && (t(r), o(""), i.current && (i.current.style.height = "auto"));
1108
- };
1109
- return /* @__PURE__ */ b("div", { className: "gcw-input-area", children: [
1110
- /* @__PURE__ */ b("div", { className: "gcw-input-row", children: [
1111
- /* @__PURE__ */ s(
1112
- "textarea",
1113
- {
1114
- ref: i,
1115
- value: r,
1116
- onChange: (l) => o(l.target.value),
1117
- onKeyDown: (l) => {
1118
- l.key === "Enter" && !l.shiftKey && (l.preventDefault(), d());
1119
- },
1120
- placeholder: e,
1121
- disabled: n,
1122
- rows: 1,
1123
- className: "gcw-input"
1124
- }
1125
- ),
1126
- /* @__PURE__ */ s(
1127
- "button",
1128
- {
1129
- onClick: d,
1130
- disabled: !r.trim() || n,
1131
- className: "gcw-send-btn",
1132
- style: { backgroundColor: a },
1133
- title: "Send message",
1134
- children: n ? /* @__PURE__ */ b("svg", { className: "gcw-send-btn__icon gcw-spin", fill: "none", viewBox: "0 0 24 24", children: [
1135
- /* @__PURE__ */ s("circle", { style: { opacity: 0.25 }, cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
1136
- /* @__PURE__ */ s("path", { style: { opacity: 0.75 }, fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
1137
- ] }) : /* @__PURE__ */ s("svg", { className: "gcw-send-btn__icon", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 19l9 2-9-18-9 18 9-2zm0 0v-8" }) })
1138
- }
1139
- )
1140
- ] }),
1141
- /* @__PURE__ */ s("p", { className: "gcw-input-hint", children: "Press Enter to send, Shift+Enter for new line" })
1142
- ] });
1143
- }
1144
- function Ne({ size: e, className: n = "gcw-w-4 gcw-h-4" }) {
1145
- switch (e) {
1146
- case "small":
1147
- return /* @__PURE__ */ s("svg", { className: n, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" }) });
1148
- case "medium":
1149
- return /* @__PURE__ */ s("svg", { className: n, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" }) });
1150
- case "large":
1151
- return /* @__PURE__ */ s("svg", { className: n, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" }) });
1152
- case "fullscreen":
1153
- return /* @__PURE__ */ s("svg", { className: n, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 9V4.5M9 9H4.5M9 9L3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5l5.25 5.25" }) });
1154
- }
1155
- }
1156
- function Le({ className: e = "gcw-w-4 gcw-h-4" }) {
1157
- return /* @__PURE__ */ s("svg", { className: e, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" }) });
1158
- }
1159
- function Ee({ className: e = "gcw-w-4 gcw-h-4" }) {
1160
- return /* @__PURE__ */ s("svg", { className: e, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) });
1161
- }
1162
- function Te({ className: e = "gcw-w-4 gcw-h-4" }) {
1163
- return /* @__PURE__ */ s("svg", { className: e, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) });
1164
- }
1165
- function $e({ className: e = "gcw-w-6 gcw-h-6" }) {
1166
- return /* @__PURE__ */ s("svg", { className: e, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" }) });
1167
- }
1168
- const Be = {
1169
- small: "gcw-window--small",
1170
- medium: "gcw-window--medium",
1171
- large: "gcw-window--large",
1172
- fullscreen: "gcw-window--fullscreen"
1173
- };
1174
- function je({
1175
- title: e,
1176
- subtitle: n,
1177
- placeholder: t,
1178
- messages: a,
1179
- isLoading: r,
1180
- onSendMessage: o,
1181
- onClose: i,
1182
- onClear: d,
1183
- onSizeChange: p,
1184
- onPopout: l,
1185
- primaryColor: g,
1186
- size: v
1187
- }) {
1188
- const E = () => {
1189
- const x = ["small", "medium", "large", "fullscreen"], f = (x.indexOf(v) + 1) % x.length;
1190
- p(x[f]);
1191
- };
1192
- return /* @__PURE__ */ b("div", { className: `gcw-window ${Be[v]}`, children: [
1193
- /* @__PURE__ */ b("div", { className: "gcw-header", style: { backgroundColor: g }, children: [
1194
- /* @__PURE__ */ b("div", { className: "gcw-header__left", children: [
1195
- /* @__PURE__ */ s("div", { className: "gcw-header__avatar", children: /* @__PURE__ */ s($e, { className: "gcw-header__avatar-icon" }) }),
1196
- /* @__PURE__ */ b("div", { className: "gcw-header__info", children: [
1197
- /* @__PURE__ */ s("h3", { className: "gcw-header__title", children: e }),
1198
- /* @__PURE__ */ s("p", { className: "gcw-header__subtitle", children: n })
1199
- ] })
1200
- ] }),
1201
- /* @__PURE__ */ b("div", { className: "gcw-header__actions", children: [
1202
- /* @__PURE__ */ s(
1203
- "button",
1204
- {
1205
- onClick: E,
1206
- className: "gcw-header__btn",
1207
- title: `Size: ${v} (click to cycle)`,
1208
- children: /* @__PURE__ */ s(Ne, { size: v, className: "gcw-header__btn-icon" })
1209
- }
1210
- ),
1211
- /* @__PURE__ */ s(
1212
- "button",
1213
- {
1214
- onClick: l,
1215
- className: "gcw-header__btn",
1216
- title: "Open in new window",
1217
- children: /* @__PURE__ */ s(Le, { className: "gcw-header__btn-icon" })
1218
- }
1219
- ),
1220
- /* @__PURE__ */ s(
1221
- "button",
1222
- {
1223
- onClick: d,
1224
- className: "gcw-header__btn",
1225
- title: "Clear conversation",
1226
- children: /* @__PURE__ */ s(Te, { className: "gcw-header__btn-icon" })
1227
- }
1228
- ),
1229
- /* @__PURE__ */ s(
1230
- "button",
1231
- {
1232
- onClick: i,
1233
- className: "gcw-header__btn",
1234
- title: "Close",
1235
- children: /* @__PURE__ */ s(Ee, { className: "gcw-header__btn-icon" })
1236
- }
1237
- )
1238
- ] })
1239
- ] }),
1240
- /* @__PURE__ */ s(
1241
- Se,
1242
- {
1243
- messages: a,
1244
- isLoading: r,
1245
- primaryColor: g
1246
- }
1247
- ),
1248
- /* @__PURE__ */ s(
1249
- Ie,
1250
- {
1251
- placeholder: t,
1252
- isLoading: r,
1253
- onSendMessage: o,
1254
- primaryColor: g
1255
- }
1256
- )
1257
- ] });
1258
- }
1259
- function De({ isOpen: e, onClick: n, primaryColor: t, hasUnread: a, isPoppedOut: r }) {
1260
- return /* @__PURE__ */ b(
1261
- "button",
1262
- {
1263
- onClick: n,
1264
- className: "gcw-toggle",
1265
- style: { backgroundColor: t },
1266
- title: r ? "Chat is open in another window" : e ? "Close chat" : "Open chat",
1267
- children: [
1268
- a && !e && /* @__PURE__ */ s("span", { className: "gcw-toggle__badge gcw-toggle__badge--unread", children: "!" }),
1269
- r && /* @__PURE__ */ s("span", { className: "gcw-toggle__badge gcw-toggle__badge--popout" }),
1270
- /* @__PURE__ */ s("div", { className: "gcw-toggle__icon", children: r ? /* @__PURE__ */ s("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" }) }) : e ? /* @__PURE__ */ s("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) : /* @__PURE__ */ s("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ s("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" }) }) })
1271
- ]
1272
- }
1273
- );
1274
- }
1275
- function re({
1276
- apiUrl: e,
1277
- tenantId: n,
1278
- title: t = "Chat Assistant",
1279
- subtitle: a = "How can I help you?",
1280
- placeholder: r = "Type a message...",
1281
- greeting: o,
1282
- primaryColor: i = "#3b82f6",
1283
- position: d = "bottom-right",
1284
- defaultOpen: p = !1,
1285
- defaultSize: l = "medium",
1286
- headers: g,
1287
- onOpen: v,
1288
- onClose: E,
1289
- onMessageSent: x,
1290
- onResponseReceived: m
1291
- }) {
1292
- const [f, B] = I(p), [O, X] = I(l), [q, T] = I(!1), { messages: c, isLoading: _, sendMessage: $, clearMessages: W, sessionId: P } = ge({
1293
- apiUrl: e,
1294
- tenantId: n,
1295
- headers: g
1296
- }), [u, y] = I(() => c.length > 0), [h, C] = I([]);
1297
- L(() => {
1298
- f && o && !u && c.length === 0 && y(!0);
1299
- }, [f, o, u, c.length]), L(() => {
1300
- c.length > 0 && y(!0);
1301
- }, [c.length]), L(() => {
1302
- u && o && c.length === 0 ? C([
1303
- {
1304
- id: "greeting",
1305
- role: "assistant",
1306
- content: o,
1307
- timestamp: /* @__PURE__ */ new Date()
1308
- }
1309
- ]) : o && c.length > 0 ? C([
1310
- {
1311
- id: "greeting",
1312
- role: "assistant",
1313
- content: o,
1314
- timestamp: /* @__PURE__ */ new Date()
1315
- },
1316
- ...c
1317
- ]) : C(c);
1318
- }, [c, u, o]);
1319
- const { isPoppedOut: k, openPopout: j, focusPopout: F } = fe(
1320
- {
1321
- apiUrl: e,
1322
- tenantId: n,
1323
- title: t,
1324
- subtitle: a,
1325
- placeholder: r,
1326
- primaryColor: i,
1327
- sessionId: P,
1328
- messages: h,
1329
- headers: g
1330
- },
1331
- O,
1332
- () => {
1333
- }
1334
- );
1335
- L(() => {
1336
- if (f || k) {
1337
- const w = [...c].reverse().find((M) => !M.isStreaming && M.content);
1338
- w && Z(w.id, n), T(!1);
1339
- } else {
1340
- const w = c.filter((N) => !N.isStreaming && N.content), M = he(n);
1341
- if (w.length === 0) {
1342
- T(!1);
1343
- return;
1344
- }
1345
- if (!M) {
1346
- T(w.some((N) => N.role === "assistant"));
1347
- return;
1348
- }
1349
- const H = w.findIndex((N) => N.id === M);
1350
- if (H === -1) {
1351
- T(w.some((N) => N.role === "assistant"));
1352
- return;
1353
- }
1354
- const ae = w.slice(H + 1).some((N) => N.role === "assistant");
1355
- T(ae);
1356
- }
1357
- }, [f, k, c, n]);
1358
- const S = () => {
1359
- if (k) {
1360
- F();
1361
- return;
1362
- }
1363
- const w = !f;
1364
- if (B(w), w) {
1365
- v == null || v();
1366
- const M = [...c].reverse().find((H) => !H.isStreaming && H.content);
1367
- M && Z(M.id, n), T(!1);
1368
- } else
1369
- E == null || E();
1370
- };
1371
- return /* @__PURE__ */ b("div", { className: `gcw-widget gcw-container ${d === "top-left" ? "gcw-container--top-left" : d === "top-right" ? "gcw-container--top-right" : d === "bottom-left" ? "gcw-container--bottom-left" : "gcw-container--bottom-right"}`, children: [
1372
- f && !k && /* @__PURE__ */ s(
1373
- je,
1374
- {
1375
- title: t,
1376
- subtitle: a,
1377
- placeholder: r,
1378
- messages: h,
1379
- isLoading: _,
1380
- onSendMessage: async (w) => {
1381
- x == null || x(w), await $(w);
1382
- const M = c[c.length - 1];
1383
- (M == null ? void 0 : M.role) === "assistant" && (m == null || m(M.content));
1384
- },
1385
- onClose: S,
1386
- onClear: () => {
1387
- W(), y(!1), C([]), T(!1), Z(null, n);
1388
- },
1389
- onSizeChange: (w) => {
1390
- X(w);
1391
- },
1392
- onPopout: () => {
1393
- B(!1), j();
1394
- },
1395
- primaryColor: i,
1396
- size: O
1397
- }
1398
- ),
1399
- /* @__PURE__ */ s(
1400
- De,
1401
- {
1402
- isOpen: f,
1403
- onClick: S,
1404
- primaryColor: i,
1405
- hasUnread: q && !f && !k,
1406
- isPoppedOut: k
1407
- }
1408
- )
1409
- ] });
1410
- }
1411
- function ee(e, n) {
1412
- let t;
1413
- if (n) {
1414
- const r = document.getElementById(n);
1415
- if (!r)
1416
- throw new Error(`Container with id "${n}" not found`);
1417
- t = r;
1418
- } else
1419
- t = document.createElement("div"), t.id = "geoapps-chat-widget-root", document.body.appendChild(t);
1420
- const a = ie.createRoot(t);
1421
- return a.render(
1422
- J.createElement(
1423
- J.StrictMode,
1424
- null,
1425
- J.createElement(re, e)
1426
- )
1427
- ), () => {
1428
- a.unmount(), !n && t.parentNode && t.parentNode.removeChild(t);
1429
- };
1430
- }
1431
- if (typeof window < "u") {
1432
- window.GeoAppsChatWidget = {
1433
- embed: ee,
1434
- ChatWidget: re
1435
- };
1436
- const e = document.currentScript;
1437
- if (e) {
1438
- const n = e.dataset.apiUrl;
1439
- n && (document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", () => {
1440
- ee({
1441
- apiUrl: n,
1442
- title: e.dataset.title,
1443
- subtitle: e.dataset.subtitle,
1444
- greeting: e.dataset.greeting,
1445
- placeholder: e.dataset.placeholder,
1446
- primaryColor: e.dataset.primaryColor,
1447
- position: e.dataset.position,
1448
- defaultOpen: e.dataset.defaultOpen === "true"
1449
- });
1450
- }) : ee({
1451
- apiUrl: n,
1452
- title: e.dataset.title,
1453
- subtitle: e.dataset.subtitle,
1454
- greeting: e.dataset.greeting,
1455
- placeholder: e.dataset.placeholder,
1456
- primaryColor: e.dataset.primaryColor,
1457
- position: e.dataset.position,
1458
- defaultOpen: e.dataset.defaultOpen === "true"
1459
- }));
1460
- }
1461
- }
1462
- export {
1463
- _e as ChatBubble,
1464
- re as ChatWidget,
1465
- je as ChatWindow,
1466
- Ie as MessageInput,
1467
- Se as MessageList,
1468
- De as ToggleButton,
1469
- ee as embedChatWidget,
1470
- ge as useChat
1471
- };