@cas0570/chat-widget 0.0.1

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 (37) hide show
  1. package/README.md +367 -0
  2. package/dist/components/ChatBubble.d.ts +8 -0
  3. package/dist/components/ChatWidget.d.ts +3 -0
  4. package/dist/components/ChatWindow.d.ts +18 -0
  5. package/dist/components/Icons.d.ts +26 -0
  6. package/dist/components/MarkdownRenderer.d.ts +6 -0
  7. package/dist/components/MessageInput.d.ts +8 -0
  8. package/dist/components/MessageList.d.ts +9 -0
  9. package/dist/components/PopoutWindow.d.ts +25 -0
  10. package/dist/components/ToggleButton.d.ts +9 -0
  11. package/dist/components/TypingIndicator.d.ts +1 -0
  12. package/dist/components/__tests__/ChatBubble.test.d.ts +1 -0
  13. package/dist/components/__tests__/ChatWidget.test.d.ts +1 -0
  14. package/dist/components/__tests__/ChatWindow.test.d.ts +1 -0
  15. package/dist/components/__tests__/Icons.test.d.ts +1 -0
  16. package/dist/components/__tests__/MarkdownRenderer.test.d.ts +1 -0
  17. package/dist/components/__tests__/MessageInput.test.d.ts +1 -0
  18. package/dist/components/__tests__/MessageList.test.d.ts +1 -0
  19. package/dist/components/__tests__/PopoutWindow.test.d.ts +1 -0
  20. package/dist/components/__tests__/ToggleButton.test.d.ts +1 -0
  21. package/dist/components/__tests__/TypingIndicator.test.d.ts +1 -0
  22. package/dist/components/__tests__/index.test.d.ts +1 -0
  23. package/dist/components/index.d.ts +9 -0
  24. package/dist/embed.d.ts +17 -0
  25. package/dist/geoapps-chat-widget.js +1471 -0
  26. package/dist/geoapps-chat-widget.umd.cjs +597 -0
  27. package/dist/hooks/__tests__/useChat.test.d.ts +1 -0
  28. package/dist/hooks/useChat.d.ts +17 -0
  29. package/dist/index.d.ts +9 -0
  30. package/dist/main.d.ts +1 -0
  31. package/dist/style.css +1 -0
  32. package/dist/test/setup.d.ts +1 -0
  33. package/dist/types.d.ts +70 -0
  34. package/dist/utils/__tests__/chatPersistence.test.d.ts +1 -0
  35. package/dist/utils/chatPersistence.d.ts +44 -0
  36. package/dist/utils/index.d.ts +1 -0
  37. package/package.json +80 -0
@@ -0,0 +1,1471 @@
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
+ };