@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,597 @@
1
+ (function(f,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("react/jsx-runtime"),require("react"),require("react-dom/client")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","react-dom/client"],t):(f=typeof globalThis<"u"?globalThis:f||self,t(f.GeoAppsChatWidget={},f.React,f.React,f.ReactDOM))})(this,function(f,t,c,le){"use strict";const ce="gcw_chat_",de="gcw_popout_",ue="gcw_read_";let J=!1;function U(e){return`${ce}${e||"default"}`}function pe(e,n,s){if(!J)try{J=!0;const a={messages:e.map(r=>({id:r.id,role:r.role,content:r.content,timestamp:r.timestamp.toISOString(),sources:r.sources})),sessionId:n,lastUpdated:new Date().toISOString()};localStorage.setItem(U(s),JSON.stringify(a))}catch(a){console.warn("Failed to save chat state:",a)}finally{J=!1}}function ee(e){try{const n=localStorage.getItem(U(e));if(!n)return null;const s=JSON.parse(n),a=new Date(s.lastUpdated);return(Date.now()-a.getTime())/(1e3*60*60)>24?(te(e),null):{messages:s.messages.map(o=>({id:o.id,role:o.role,content:o.content,timestamp:new Date(o.timestamp),sources:o.sources})),sessionId:s.sessionId}}catch(n){return console.warn("Failed to load chat state:",n),null}}function te(e){try{localStorage.removeItem(U(e))}catch(n){console.warn("Failed to clear chat state:",n)}}function ge(e,n){const s=U(e),a=r=>{if(r.key===s){if(!r.newValue){n(null);return}try{const o=JSON.parse(r.newValue);n({messages:o.messages.map(i=>({id:i.id,role:i.role,content:i.content,timestamp:new Date(i.timestamp),sources:i.sources})),sessionId:o.sessionId})}catch{}}};return window.addEventListener("storage",a),()=>window.removeEventListener("storage",a)}function q(e){return`${de}${e||"default"}`}function F(e,n){try{e?localStorage.setItem(q(n),"true"):localStorage.removeItem(q(n))}catch(s){console.warn("Failed to save popout state:",s)}}function he(e){try{return localStorage.getItem(q(e))==="true"}catch{return!1}}function Y(e){return`${ue}${e||"default"}`}function X(e,n){try{e?localStorage.setItem(Y(n),e):localStorage.removeItem(Y(n))}catch(s){console.warn("Failed to save read state:",s)}}function me(e){try{return localStorage.getItem(Y(e))}catch{return null}}function se(e){const{apiUrl:n,tenantId:s,sessionId:a,headers:r={},onError:o,persist:i=!0}=e,u=c.useRef(!1),[g,l]=c.useState(()=>{if(i){const d=ee(s);if(d)return d.messages}return[]}),[w,y]=c.useState(!1),[L,M]=c.useState(null),[h,b]=c.useState(()=>{if(i){const d=ee(s);if(d)return d.sessionId}return a||null}),B=c.useRef(null);c.useEffect(()=>{u.current||i&&g.length>0&&pe(g,h,s)},[g,h,s,i]),c.useEffect(()=>i?ge(s,S=>{u.current=!0,S?(l(S.messages),b(S.sessionId)):(l([]),b(null)),setTimeout(()=>{u.current=!1},0)}):void 0,[s,i]);const P=()=>`msg_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,Z=c.useCallback(async d=>{var D;if(!d.trim()||w)return;B.current&&B.current.abort(),B.current=new AbortController;const S={id:P(),role:"user",content:d.trim(),timestamp:new Date};l(p=>[...p,S]),y(!0),M(null);const T=P(),z={id:T,role:"assistant",content:"",timestamp:new Date,isStreaming:!0};l(p=>[...p,z]);try{const p=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",...s&&{"X-Tenant-ID":s},...r},body:JSON.stringify({message:d.trim(),session_id:h}),signal:B.current.signal});if(!p.ok){const m=await p.json().catch(()=>({}));throw new Error(m.detail||`Request failed with status ${p.status}`)}const k=p.headers.get("content-type");if(k!=null&&k.includes("text/event-stream"))await R(p,T);else{const m=await p.json(),C=(D=m.message.citations)==null?void 0:D.map(x=>({title:x.title,url:x.url,relevance:x.relevance_score}));l(x=>x.map(j=>j.id===T?{...j,content:m.message.content,isStreaming:!1,sources:C}:j)),m.session_id&&b(m.session_id)}}catch(p){if(p.name==="AbortError")return;const k=p instanceof Error?p:new Error("An error occurred");M(k),o==null||o(k),l(m=>m.map(C=>C.id===T?{...C,content:"Sorry, an error occurred. Please try again.",isStreaming:!1}:C))}finally{y(!1),B.current=null}},[n,s,h,r,w,o]),R=async(d,S)=>{var p;const T=(p=d.body)==null?void 0:p.getReader();if(!T)throw new Error("No response body");const z=new TextDecoder;let D="";try{for(;;){const{done:k,value:m}=await T.read();if(k)break;const x=z.decode(m,{stream:!0}).split(`
2
+ `);for(const j of x)if(j.startsWith("data: ")){const G=j.slice(6);if(G==="[DONE]")continue;try{const I=JSON.parse(G);I.type==="content"&&I.content?(D+=I.content,l(H=>H.map($=>$.id===S?{...$,content:D}:$))):I.type==="sources"&&I.sources?l(H=>H.map($=>$.id===S?{...$,sources:I.sources}:$)):I.conversation_id&&b(I.conversation_id)}catch{}}}}finally{l(k=>k.map(m=>m.id===S?{...m,isStreaming:!1}:m))}},E=c.useCallback(()=>{l([]),b(null),M(null),i&&te(s)},[i,s]);return{messages:g,isLoading:w,error:L,sendMessage:Z,clearMessages:E,sessionId:h}}let O=null;function fe(e,n,s){const[a,r]=c.useState(()=>he(e.tenantId)),o=c.useRef(O);c.useEffect(()=>{if(a&&!o.current){const l=window.open("","GeoAppsChatPopout");l&&l.location.href!=="about:blank"&&!l.closed?(o.current=l,O=l):(r(!1),F(!1,e.tenantId))}},[a,e.tenantId]),c.useEffect(()=>{if(!o.current)return;const l=setInterval(()=>{var w;(w=o.current)!=null&&w.closed&&(r(!1),o.current=null,O=null,F(!1,e.tenantId))},500);return()=>clearInterval(l)},[e.tenantId,s]);const i=c.useCallback(()=>{if(o.current&&!o.current.closed){o.current.focus();return}const l={small:{width:380,height:520},medium:{width:440,height:660},large:{width:560,height:780},fullscreen:{width:700,height:800}},{width:w,height:y}=l[n],L=(window.screen.width-w)/2,M=(window.screen.height-y)/2,h=window.open("","GeoAppsChatPopout",`width=${w},height=${y},left=${L},top=${M},resizable=yes,scrollbars=no`);if(!h){console.error("Failed to open popup window. Check if popups are blocked.");return}h.__CHAT_CONFIG__=e,h.document.write(we(e)),h.document.close(),o.current=h,O=h,r(!0),F(!0,e.tenantId)},[e,n]),u=c.useCallback(()=>{o.current&&!o.current.closed&&o.current.close(),o.current=null,O=null,r(!1),F(!1,e.tenantId)},[e.tenantId]),g=c.useCallback(()=>{o.current&&!o.current.closed&&o.current.focus()},[]);return{isPoppedOut:a,openPopout:i,closePopout:u,focusPopout:g}}function we(e){const n=e.messages.map(s=>({...s,timestamp:s.timestamp.toISOString()}));return`
3
+ <!DOCTYPE html>
4
+ <html lang="en">
5
+ <head>
6
+ <meta charset="UTF-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+ <title>${V(e.title)}</title>
9
+ <style>
10
+ * { margin: 0; padding: 0; box-sizing: border-box; }
11
+ html, body, #chat-root { height: 100%; width: 100%; }
12
+ body {
13
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
14
+ background: #f5f5f5;
15
+ }
16
+
17
+ /* Modern chat widget styling */
18
+ .popout-container {
19
+ height: 100%;
20
+ display: flex;
21
+ flex-direction: column;
22
+ background: white;
23
+ border-radius: 0;
24
+ }
25
+ .popout-header {
26
+ padding: 0.75rem 1rem;
27
+ color: white;
28
+ display: flex;
29
+ align-items: center;
30
+ gap: 0.75rem;
31
+ flex-shrink: 0;
32
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
33
+ }
34
+ .popout-header-icon {
35
+ width: 2.5rem;
36
+ height: 2.5rem;
37
+ border-radius: 50%;
38
+ background: rgba(255,255,255,0.2);
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ flex-shrink: 0;
43
+ }
44
+ .popout-header-icon svg {
45
+ width: 1.25rem;
46
+ height: 1.25rem;
47
+ }
48
+ .popout-header-info {
49
+ flex: 1;
50
+ min-width: 0;
51
+ }
52
+ .popout-title {
53
+ font-weight: 600;
54
+ font-size: 0.875rem;
55
+ }
56
+ .popout-subtitle {
57
+ font-size: 0.75rem;
58
+ opacity: 0.9;
59
+ }
60
+ .popout-header-actions {
61
+ display: flex;
62
+ gap: 0.25rem;
63
+ }
64
+ .popout-header-btn {
65
+ width: 2rem;
66
+ height: 2rem;
67
+ border-radius: 50%;
68
+ border: none;
69
+ background: rgba(255,255,255,0.1);
70
+ color: white;
71
+ cursor: pointer;
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ transition: background 0.2s;
76
+ }
77
+ .popout-header-btn:hover {
78
+ background: rgba(255,255,255,0.25);
79
+ }
80
+ .popout-header-btn svg {
81
+ width: 1rem;
82
+ height: 1rem;
83
+ }
84
+ .popout-messages {
85
+ flex: 1;
86
+ overflow-y: auto;
87
+ padding: 1rem;
88
+ display: flex;
89
+ flex-direction: column;
90
+ gap: 1rem;
91
+ background: #fafafa;
92
+ }
93
+ .popout-messages::-webkit-scrollbar {
94
+ width: 6px;
95
+ }
96
+ .popout-messages::-webkit-scrollbar-track {
97
+ background: transparent;
98
+ }
99
+ .popout-messages::-webkit-scrollbar-thumb {
100
+ background: #d1d5db;
101
+ border-radius: 3px;
102
+ }
103
+ .popout-messages::-webkit-scrollbar-thumb:hover {
104
+ background: #9ca3af;
105
+ }
106
+ /* Empty state */
107
+ .popout-empty {
108
+ flex: 1;
109
+ display: flex;
110
+ flex-direction: column;
111
+ align-items: center;
112
+ justify-content: center;
113
+ padding: 2rem;
114
+ color: #9ca3af;
115
+ text-align: center;
116
+ }
117
+ .popout-empty svg {
118
+ width: 4rem;
119
+ height: 4rem;
120
+ margin-bottom: 1rem;
121
+ opacity: 0.5;
122
+ }
123
+ .popout-empty p {
124
+ font-size: 0.875rem;
125
+ }
126
+ .popout-message-wrapper {
127
+ display: flex;
128
+ flex-direction: column;
129
+ }
130
+ .popout-message-wrapper.user {
131
+ align-items: flex-end;
132
+ }
133
+ .popout-message-wrapper.assistant {
134
+ align-items: flex-start;
135
+ }
136
+ .popout-message {
137
+ max-width: 85%;
138
+ padding: 0.75rem 1rem;
139
+ border-radius: 1rem;
140
+ font-size: 0.875rem;
141
+ line-height: 1.5;
142
+ word-wrap: break-word;
143
+ }
144
+ .popout-message.user {
145
+ color: white;
146
+ border-bottom-right-radius: 0.25rem;
147
+ }
148
+ .popout-message.assistant {
149
+ background: #f1f5f9;
150
+ color: #1f2937;
151
+ border-bottom-left-radius: 0.25rem;
152
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
153
+ }
154
+ .popout-message p { margin-bottom: 0.5rem; }
155
+ .popout-message p:last-child { margin-bottom: 0; }
156
+ .popout-message strong { font-weight: 600; }
157
+ .popout-message em { font-style: italic; }
158
+ .popout-message code {
159
+ background: rgba(0,0,0,0.06);
160
+ padding: 0.125rem 0.375rem;
161
+ border-radius: 0.25rem;
162
+ font-size: 0.8125rem;
163
+ font-family: 'SF Mono', Monaco, Consolas, monospace;
164
+ }
165
+ .popout-message.user code {
166
+ background: rgba(255,255,255,0.2);
167
+ }
168
+ .popout-message pre {
169
+ background: #1f2937;
170
+ color: #f3f4f6;
171
+ padding: 0.75rem;
172
+ border-radius: 0.5rem;
173
+ font-size: 0.75rem;
174
+ overflow-x: auto;
175
+ margin: 0.5rem 0;
176
+ }
177
+ .popout-message pre code {
178
+ background: none;
179
+ padding: 0;
180
+ font-size: inherit;
181
+ }
182
+ .popout-message ul, .popout-message ol {
183
+ padding-left: 1.25rem;
184
+ margin: 0.5rem 0;
185
+ }
186
+ .popout-message li { margin: 0.25rem 0; }
187
+ .popout-message a {
188
+ color: #2563eb;
189
+ text-decoration: underline;
190
+ }
191
+ .popout-message.user a {
192
+ color: rgba(255,255,255,0.9);
193
+ }
194
+ .popout-input-container {
195
+ padding: 0.75rem 1rem 1rem;
196
+ border-top: 1px solid #e5e7eb;
197
+ flex-shrink: 0;
198
+ background: white;
199
+ }
200
+ .popout-input-wrapper {
201
+ display: flex;
202
+ gap: 0.5rem;
203
+ align-items: flex-end;
204
+ }
205
+ .popout-textarea {
206
+ flex: 1;
207
+ padding: 0.625rem 1rem;
208
+ border: 1px solid #e5e7eb;
209
+ border-radius: 1.25rem;
210
+ resize: none;
211
+ font-size: 0.875rem;
212
+ font-family: inherit;
213
+ min-height: 44px;
214
+ max-height: 120px;
215
+ outline: none;
216
+ transition: border-color 0.2s, box-shadow 0.2s;
217
+ }
218
+ .popout-textarea:focus {
219
+ border-color: ${e.primaryColor};
220
+ box-shadow: 0 0 0 3px ${e.primaryColor}15;
221
+ }
222
+ .popout-textarea::placeholder {
223
+ color: #9ca3af;
224
+ }
225
+ .popout-send-btn {
226
+ width: 2.5rem;
227
+ height: 2.5rem;
228
+ border-radius: 0.75rem;
229
+ border: none;
230
+ color: white;
231
+ cursor: pointer;
232
+ display: flex;
233
+ align-items: center;
234
+ justify-content: center;
235
+ flex-shrink: 0;
236
+ transition: opacity 0.2s, transform 0.1s;
237
+ }
238
+ .popout-send-btn:hover { opacity: 0.9; }
239
+ .popout-send-btn:active { transform: scale(0.95); }
240
+ .popout-send-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
241
+ .popout-send-btn svg { width: 1.25rem; height: 1.25rem; }
242
+ .popout-input-hint {
243
+ font-size: 0.625rem;
244
+ color: #9ca3af;
245
+ text-align: center;
246
+ margin-top: 0.5rem;
247
+ }
248
+ .popout-timestamp {
249
+ font-size: 0.625rem;
250
+ color: #9ca3af;
251
+ margin-top: 0.25rem;
252
+ padding: 0 0.25rem;
253
+ }
254
+ .popout-loading {
255
+ display: flex;
256
+ gap: 0.25rem;
257
+ padding: 0.25rem 0;
258
+ }
259
+ .popout-loading span {
260
+ width: 0.5rem;
261
+ height: 0.5rem;
262
+ background: #9ca3af;
263
+ border-radius: 50%;
264
+ animation: bounce 1.4s infinite ease-in-out both;
265
+ }
266
+ .popout-loading span:nth-child(1) { animation-delay: -0.32s; }
267
+ .popout-loading span:nth-child(2) { animation-delay: -0.16s; }
268
+ @keyframes bounce {
269
+ 0%, 80%, 100% { transform: scale(0); }
270
+ 40% { transform: scale(1); }
271
+ }
272
+ /* Typing indicator */
273
+ .typing-indicator {
274
+ display: flex;
275
+ align-items: flex-start;
276
+ }
277
+ .typing-bubble {
278
+ background: white;
279
+ padding: 0.75rem 1rem;
280
+ border-radius: 1rem;
281
+ border-bottom-left-radius: 0.25rem;
282
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
283
+ display: flex;
284
+ gap: 0.25rem;
285
+ }
286
+ .typing-bubble span {
287
+ width: 0.5rem;
288
+ height: 0.5rem;
289
+ background: #9ca3af;
290
+ border-radius: 50%;
291
+ animation: bounce 1.4s infinite ease-in-out both;
292
+ }
293
+ .typing-bubble span:nth-child(1) { animation-delay: -0.32s; }
294
+ .typing-bubble span:nth-child(2) { animation-delay: -0.16s; }
295
+ </style>
296
+ </head>
297
+ <body>
298
+ <div id="chat-root">
299
+ <div class="popout-container">
300
+ <div class="popout-header" style="background: ${e.primaryColor}">
301
+ <div class="popout-header-icon">
302
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
303
+ <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" />
304
+ </svg>
305
+ </div>
306
+ <div class="popout-header-info">
307
+ <div class="popout-title">${V(e.title)}</div>
308
+ <div class="popout-subtitle">${V(e.subtitle)}</div>
309
+ </div>
310
+ <div class="popout-header-actions">
311
+ <button class="popout-header-btn" id="clear-btn" title="Clear conversation">
312
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
313
+ <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" />
314
+ </svg>
315
+ </button>
316
+ </div>
317
+ </div>
318
+ <div class="popout-messages" id="messages-container"></div>
319
+ <div class="popout-input-container">
320
+ <div class="popout-input-wrapper">
321
+ <textarea
322
+ class="popout-textarea"
323
+ id="message-input"
324
+ placeholder="${V(e.placeholder)}"
325
+ rows="1"
326
+ ></textarea>
327
+ <button
328
+ class="popout-send-btn"
329
+ id="send-btn"
330
+ style="background: ${e.primaryColor}"
331
+ >
332
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
333
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
334
+ </svg>
335
+ </button>
336
+ </div>
337
+ <p class="popout-input-hint">Press Enter to send, Shift+Enter for new line</p>
338
+ </div>
339
+ </div>
340
+ </div>
341
+ <script>
342
+ (function() {
343
+ const config = ${JSON.stringify({apiUrl:e.apiUrl,tenantId:e.tenantId,sessionId:e.sessionId,primaryColor:e.primaryColor,headers:e.headers||{}})};
344
+
345
+ const STORAGE_KEY = 'gcw_chat_' + (config.tenantId || 'default');
346
+
347
+ let messages = ${JSON.stringify(n)}.map(m => ({
348
+ ...m,
349
+ timestamp: new Date(m.timestamp)
350
+ }));
351
+ let sessionId = config.sessionId;
352
+ let isLoading = false;
353
+
354
+ const messagesContainer = document.getElementById('messages-container');
355
+ const messageInput = document.getElementById('message-input');
356
+ const sendBtn = document.getElementById('send-btn');
357
+
358
+ // Save state to localStorage for sync with main window
359
+ function saveState() {
360
+ try {
361
+ const state = {
362
+ messages: messages.map(m => ({
363
+ id: m.id,
364
+ role: m.role,
365
+ content: m.content,
366
+ timestamp: m.timestamp.toISOString(),
367
+ sources: m.sources
368
+ })),
369
+ sessionId: sessionId,
370
+ lastUpdated: new Date().toISOString()
371
+ };
372
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
373
+ } catch (e) {
374
+ console.warn('Failed to save state:', e);
375
+ }
376
+ }
377
+
378
+ function escapeHtml(text) {
379
+ const div = document.createElement('div');
380
+ div.textContent = text;
381
+ return div.innerHTML;
382
+ }
383
+
384
+ function parseMarkdown(text) {
385
+ if (!text) return '';
386
+
387
+ // Code blocks
388
+ text = text.replace(/\`\`\`([\\s\\S]*?)\`\`\`/g, '<pre><code>$1</code></pre>');
389
+
390
+ // Inline code
391
+ text = text.replace(/\`([^\`]+)\`/g, '<code>$1</code>');
392
+
393
+ // Bold
394
+ text = text.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');
395
+
396
+ // Italic
397
+ text = text.replace(/\\*([^*]+?)\\*/g, '<em>$1</em>');
398
+
399
+ // Links
400
+ text = text.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
401
+
402
+ // Lists
403
+ const lines = text.split('\\n');
404
+ let result = [];
405
+ let inList = false;
406
+ let listType = null;
407
+
408
+ for (const line of lines) {
409
+ const ulMatch = line.match(/^[\\s]*[-*•]\\s+(.+)/);
410
+ const olMatch = line.match(/^[\\s]*(\\d+)[.)]\\s+(.+)/);
411
+
412
+ if (ulMatch) {
413
+ if (!inList || listType !== 'ul') {
414
+ if (inList) result.push('</' + listType + '>');
415
+ result.push('<ul>');
416
+ inList = true;
417
+ listType = 'ul';
418
+ }
419
+ result.push('<li>' + ulMatch[1] + '</li>');
420
+ } else if (olMatch) {
421
+ if (!inList || listType !== 'ol') {
422
+ if (inList) result.push('</' + listType + '>');
423
+ result.push('<ol>');
424
+ inList = true;
425
+ listType = 'ol';
426
+ }
427
+ result.push('<li>' + olMatch[2] + '</li>');
428
+ } else {
429
+ if (inList) {
430
+ result.push('</' + listType + '>');
431
+ inList = false;
432
+ listType = null;
433
+ }
434
+ if (line.trim()) {
435
+ result.push('<p>' + line + '</p>');
436
+ }
437
+ }
438
+ }
439
+ if (inList) result.push('</' + listType + '>');
440
+
441
+ return result.join('');
442
+ }
443
+
444
+ function formatTime(date) {
445
+ return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
446
+ }
447
+
448
+ function renderMessages() {
449
+ // Show empty state if no messages
450
+ if (messages.length === 0) {
451
+ messagesContainer.innerHTML = \`
452
+ <div class="popout-empty">
453
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
454
+ <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" />
455
+ </svg>
456
+ <p>Start a conversation by typing a message below</p>
457
+ </div>
458
+ \`;
459
+ return;
460
+ }
461
+
462
+ messagesContainer.innerHTML = messages.map(msg => {
463
+ const isUser = msg.role === 'user';
464
+ const content = msg.isStreaming && !msg.content
465
+ ? '<div class="popout-loading"><span></span><span></span><span></span></div>'
466
+ : parseMarkdown(escapeHtml(msg.content));
467
+
468
+ return \`
469
+ <div class="popout-message-wrapper \${msg.role}">
470
+ <div class="popout-message \${msg.role}" \${isUser ? 'style="background: ' + config.primaryColor + '"' : ''}>
471
+ \${content}
472
+ </div>
473
+ <div class="popout-timestamp">\${formatTime(msg.timestamp)}</div>
474
+ </div>
475
+ \`;
476
+ }).join('');
477
+
478
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
479
+ }
480
+
481
+ async function sendMessage(content) {
482
+ if (!content.trim() || isLoading) return;
483
+
484
+ const userMsg = {
485
+ id: 'msg_' + Date.now(),
486
+ role: 'user',
487
+ content: content.trim(),
488
+ timestamp: new Date()
489
+ };
490
+ messages.push(userMsg);
491
+
492
+ const assistantMsgId = 'msg_' + Date.now() + '_assistant';
493
+ const assistantMsg = {
494
+ id: assistantMsgId,
495
+ role: 'assistant',
496
+ content: '',
497
+ timestamp: new Date(),
498
+ isStreaming: true
499
+ };
500
+ messages.push(assistantMsg);
501
+
502
+ isLoading = true;
503
+ renderMessages();
504
+ messageInput.value = '';
505
+ sendBtn.disabled = true;
506
+
507
+ try {
508
+ const response = await fetch(config.apiUrl, {
509
+ method: 'POST',
510
+ headers: {
511
+ 'Content-Type': 'application/json',
512
+ ...(config.tenantId && { 'X-Tenant-ID': config.tenantId }),
513
+ ...config.headers
514
+ },
515
+ body: JSON.stringify({
516
+ message: content.trim(),
517
+ session_id: sessionId
518
+ })
519
+ });
520
+
521
+ if (!response.ok) throw new Error('Request failed');
522
+
523
+ const data = await response.json();
524
+
525
+ const msgIndex = messages.findIndex(m => m.id === assistantMsgId);
526
+ if (msgIndex !== -1) {
527
+ messages[msgIndex] = {
528
+ ...messages[msgIndex],
529
+ content: data.message.content,
530
+ isStreaming: false
531
+ };
532
+ }
533
+
534
+ if (data.session_id) {
535
+ sessionId = data.session_id;
536
+ }
537
+
538
+ // Save to localStorage after successful response
539
+ saveState();
540
+ } catch (err) {
541
+ const msgIndex = messages.findIndex(m => m.id === assistantMsgId);
542
+ if (msgIndex !== -1) {
543
+ messages[msgIndex] = {
544
+ ...messages[msgIndex],
545
+ content: 'Sorry, an error occurred. Please try again.',
546
+ isStreaming: false
547
+ };
548
+ }
549
+ } finally {
550
+ isLoading = false;
551
+ sendBtn.disabled = false;
552
+ renderMessages();
553
+ saveState(); // Always save state after message exchange
554
+ }
555
+ }
556
+
557
+ // Auto-resize textarea
558
+ messageInput.addEventListener('input', function() {
559
+ this.style.height = 'auto';
560
+ this.style.height = Math.min(this.scrollHeight, 120) + 'px';
561
+ });
562
+
563
+ // Send on Enter (Shift+Enter for newline)
564
+ messageInput.addEventListener('keydown', function(e) {
565
+ if (e.key === 'Enter' && !e.shiftKey) {
566
+ e.preventDefault();
567
+ sendMessage(this.value);
568
+ }
569
+ });
570
+
571
+ sendBtn.addEventListener('click', function() {
572
+ sendMessage(messageInput.value);
573
+ });
574
+
575
+ // Clear conversation button
576
+ const clearBtn = document.getElementById('clear-btn');
577
+ if (clearBtn) {
578
+ clearBtn.addEventListener('click', function() {
579
+ if (confirm('Are you sure you want to clear the conversation?')) {
580
+ messages = [];
581
+ sessionId = null;
582
+ saveState();
583
+ renderMessages();
584
+ }
585
+ });
586
+ }
587
+
588
+ // Initial render
589
+ renderMessages();
590
+ messageInput.focus();
591
+ })();
592
+ <\/script>
593
+ </body>
594
+ </html>
595
+ `}function V(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}function be({content:e,className:n}){if(!e)return null;const s=ve(e);return t.jsx("div",{className:n,children:s.map((a,r)=>t.jsx(c.Fragment,{children:a},r))})}function ve(e){const n=e.split(`
596
+ `),s=[];let a=null;const r=()=>{a&&(a.type==="ul"?s.push(t.jsx("ul",{className:"gcw-md-list gcw-md-list--ul",children:a.items.map((o,i)=>t.jsx("li",{className:"gcw-md-list__item",children:o},i))},`list-${s.length}`)):s.push(t.jsx("ol",{className:"gcw-md-list gcw-md-list--ol",children:a.items.map((o,i)=>t.jsx("li",{className:"gcw-md-list__item",children:o},i))},`list-${s.length}`)),a=null)};for(let o=0;o<n.length;o++){const i=n[o];if(i.startsWith("#### ")){r(),s.push(t.jsx("h5",{className:"gcw-md-heading gcw-md-heading--h5",children:W(i.slice(5))},o));continue}if(i.startsWith("### ")){r(),s.push(t.jsx("h4",{className:"gcw-md-heading gcw-md-heading--h4",children:W(i.slice(4))},o));continue}if(i.startsWith("## ")){r(),s.push(t.jsx("h3",{className:"gcw-md-heading gcw-md-heading--h3",children:W(i.slice(3))},o));continue}if(i.startsWith("# ")){r(),s.push(t.jsx("h2",{className:"gcw-md-heading gcw-md-heading--h2",children:W(i.slice(2))},o));continue}const u=i.match(/^[\s]*[-*•]\s+(.+)/);if(u){(!a||a.type!=="ul")&&(r(),a={type:"ul",items:[]}),a.items.push(W(u[1]));continue}const g=i.match(/^[\s]*(\d+)[.)]\s+(.+)/);if(g){(!a||a.type!=="ol")&&(r(),a={type:"ol",items:[]}),a.items.push(W(g[2]));continue}if(i.startsWith("```")){r();const l=[];for(o++;o<n.length&&!n[o].startsWith("```");)l.push(n[o]),o++;s.push(t.jsx("pre",{className:"gcw-md-code-block",children:t.jsx("code",{children:l.join(`
597
+ `)})},`code-${s.length}`));continue}if(i.trim()===""){r();continue}r(),s.push(t.jsx("p",{className:"gcw-md-paragraph",children:W(i)},o))}return r(),s}function W(e){if(!e)return null;const n=[];let s=e,a=0;for(;s.length>0;){const r=s.match(/^\*\*(.+?)\*\*/);if(r){n.push(t.jsx("strong",{className:"gcw-md-bold",children:r[1]},a++)),s=s.slice(r[0].length);continue}const o=s.match(/^\*([^*]+?)\*/);if(o){n.push(t.jsx("em",{className:"gcw-md-italic",children:o[1]},a++)),s=s.slice(o[0].length);continue}const i=s.match(/^`([^`]+)`/);if(i){n.push(t.jsx("code",{className:"gcw-md-code",children:i[1]},a++)),s=s.slice(i[0].length);continue}const u=s.match(/^\[([^\]]+)\]\(([^)]+)\)/);if(u){n.push(t.jsx("a",{href:u[2],target:"_blank",rel:"noopener noreferrer",className:"gcw-md-link",children:u[1]},a++)),s=s.slice(u[0].length);continue}const g=s.search(/[\*`\[]/);if(g===-1){n.push(s);break}else g===0?(n.push(s[0]),s=s.slice(1)):(n.push(s.slice(0,g)),s=s.slice(g))}return n.length===1?n[0]:n}function ye(){return t.jsxs("div",{className:"gcw-typing__dots",children:[t.jsx("span",{className:"gcw-typing__dot"}),t.jsx("span",{className:"gcw-typing__dot"}),t.jsx("span",{className:"gcw-typing__dot"})]})}function ke({className:e}){return t.jsx("svg",{className:e,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("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"})})}function xe(e){return e.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}function Me(e,n){return e.length<=n?e:e.slice(0,n-3)+"..."}function ne({message:e,primaryColor:n}){const s=e.role==="user",a=s?"user":"assistant";return t.jsx("div",{className:`gcw-bubble-row gcw-bubble-row--${a}`,children:t.jsxs("div",{className:`gcw-bubble-wrapper gcw-bubble-wrapper--${a}`,children:[t.jsx("div",{className:`gcw-bubble gcw-bubble--${a}`,style:s?{backgroundColor:n}:void 0,children:e.isStreaming&&!e.content?t.jsx(ye,{}):t.jsx(be,{content:e.content,className:"gcw-content"})}),e.sources&&e.sources.length>0&&t.jsx("div",{className:"gcw-sources",children:e.sources.slice(0,3).map((r,o)=>t.jsxs("a",{href:r.url,target:"_blank",rel:"noopener noreferrer",className:"gcw-source",children:[t.jsx(ke,{className:"gcw-source__icon"}),Me(r.title,25)]},o))}),t.jsx("span",{className:"gcw-bubble__timestamp",children:xe(e.timestamp)})]})})}function _e(){return t.jsx("div",{className:"gcw-typing",children:t.jsx("div",{className:"gcw-typing__bubble",children:t.jsxs("div",{className:"gcw-typing__dots",children:[t.jsx("span",{className:"gcw-typing__dot"}),t.jsx("span",{className:"gcw-typing__dot"}),t.jsx("span",{className:"gcw-typing__dot"})]})})})}function oe({messages:e,isLoading:n,primaryColor:s}){var r;const a=c.useRef(null);return c.useEffect(()=>{var o;(o=a.current)==null||o.scrollIntoView({behavior:"smooth"})},[e,n]),e.length===0&&!n?t.jsxs("div",{className:"gcw-empty",children:[t.jsx("svg",{className:"gcw-empty__icon",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("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"})}),t.jsx("p",{className:"gcw-empty__text",children:"Start a conversation by typing a message below"})]}):t.jsxs("div",{className:"gcw-messages",children:[e.map(o=>t.jsx(ne,{message:o,primaryColor:s},o.id)),n&&((r=e[e.length-1])==null?void 0:r.role)!=="assistant"&&t.jsx(_e,{}),t.jsx("div",{ref:a})]})}function re({placeholder:e,isLoading:n,onSendMessage:s,primaryColor:a}){const[r,o]=c.useState(""),i=c.useRef(null);c.useEffect(()=>{const l=i.current;l&&(l.style.height="auto",l.style.height=`${Math.min(l.scrollHeight,120)}px`)},[r]);const u=()=>{r.trim()&&!n&&(s(r),o(""),i.current&&(i.current.style.height="auto"))},g=l=>{l.key==="Enter"&&!l.shiftKey&&(l.preventDefault(),u())};return t.jsxs("div",{className:"gcw-input-area",children:[t.jsxs("div",{className:"gcw-input-row",children:[t.jsx("textarea",{ref:i,value:r,onChange:l=>o(l.target.value),onKeyDown:g,placeholder:e,disabled:n,rows:1,className:"gcw-input"}),t.jsx("button",{onClick:u,disabled:!r.trim()||n,className:"gcw-send-btn",style:{backgroundColor:a},title:"Send message",children:n?t.jsxs("svg",{className:"gcw-send-btn__icon gcw-spin",fill:"none",viewBox:"0 0 24 24",children:[t.jsx("circle",{style:{opacity:.25},cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),t.jsx("path",{style:{opacity:.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"})]}):t.jsx("svg",{className:"gcw-send-btn__icon",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 19l9 2-9-18-9 18 9-2zm0 0v-8"})})})]}),t.jsx("p",{className:"gcw-input-hint",children:"Press Enter to send, Shift+Enter for new line"})]})}function Se({size:e,className:n="gcw-w-4 gcw-h-4"}){switch(e){case"small":return t.jsx("svg",{className:n,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("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"})});case"medium":return t.jsx("svg",{className:n,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("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"})});case"large":return t.jsx("svg",{className:n,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("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"})});case"fullscreen":return t.jsx("svg",{className:n,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("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"})})}}function Ce({className:e="gcw-w-4 gcw-h-4"}){return t.jsx("svg",{className:e,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("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"})})}function Ie({className:e="gcw-w-4 gcw-h-4"}){return t.jsx("svg",{className:e,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})}function Ne({className:e="gcw-w-4 gcw-h-4"}){return t.jsx("svg",{className:e,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("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"})})}function Le({className:e="gcw-w-6 gcw-h-6"}){return t.jsx("svg",{className:e,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("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"})})}const Ee={small:"gcw-window--small",medium:"gcw-window--medium",large:"gcw-window--large",fullscreen:"gcw-window--fullscreen"};function ae({title:e,subtitle:n,placeholder:s,messages:a,isLoading:r,onSendMessage:o,onClose:i,onClear:u,onSizeChange:g,onPopout:l,primaryColor:w,size:y}){const L=()=>{const M=["small","medium","large","fullscreen"],b=(M.indexOf(y)+1)%M.length;g(M[b])};return t.jsxs("div",{className:`gcw-window ${Ee[y]}`,children:[t.jsxs("div",{className:"gcw-header",style:{backgroundColor:w},children:[t.jsxs("div",{className:"gcw-header__left",children:[t.jsx("div",{className:"gcw-header__avatar",children:t.jsx(Le,{className:"gcw-header__avatar-icon"})}),t.jsxs("div",{className:"gcw-header__info",children:[t.jsx("h3",{className:"gcw-header__title",children:e}),t.jsx("p",{className:"gcw-header__subtitle",children:n})]})]}),t.jsxs("div",{className:"gcw-header__actions",children:[t.jsx("button",{onClick:L,className:"gcw-header__btn",title:`Size: ${y} (click to cycle)`,children:t.jsx(Se,{size:y,className:"gcw-header__btn-icon"})}),t.jsx("button",{onClick:l,className:"gcw-header__btn",title:"Open in new window",children:t.jsx(Ce,{className:"gcw-header__btn-icon"})}),t.jsx("button",{onClick:u,className:"gcw-header__btn",title:"Clear conversation",children:t.jsx(Ne,{className:"gcw-header__btn-icon"})}),t.jsx("button",{onClick:i,className:"gcw-header__btn",title:"Close",children:t.jsx(Ie,{className:"gcw-header__btn-icon"})})]})]}),t.jsx(oe,{messages:a,isLoading:r,primaryColor:w}),t.jsx(re,{placeholder:s,isLoading:r,onSendMessage:o,primaryColor:w})]})}function ie({isOpen:e,onClick:n,primaryColor:s,hasUnread:a,isPoppedOut:r}){return t.jsxs("button",{onClick:n,className:"gcw-toggle",style:{backgroundColor:s},title:r?"Chat is open in another window":e?"Close chat":"Open chat",children:[a&&!e&&t.jsx("span",{className:"gcw-toggle__badge gcw-toggle__badge--unread",children:"!"}),r&&t.jsx("span",{className:"gcw-toggle__badge gcw-toggle__badge--popout"}),t.jsx("div",{className:"gcw-toggle__icon",children:r?t.jsx("svg",{fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("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?t.jsx("svg",{fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})}):t.jsx("svg",{fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:t.jsx("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"})})})]})}function Q({apiUrl:e,tenantId:n,title:s="Chat Assistant",subtitle:a="How can I help you?",placeholder:r="Type a message...",greeting:o,primaryColor:i="#3b82f6",position:u="bottom-right",defaultOpen:g=!1,defaultSize:l="medium",headers:w,onOpen:y,onClose:L,onMessageSent:M,onResponseReceived:h}){const[b,B]=c.useState(g),[P,Z]=c.useState(l),[R,E]=c.useState(!1),{messages:d,isLoading:S,sendMessage:T,clearMessages:z,sessionId:D}=se({apiUrl:e,tenantId:n,headers:w}),[p,k]=c.useState(()=>d.length>0),[m,C]=c.useState([]);c.useEffect(()=>{b&&o&&!p&&d.length===0&&k(!0)},[b,o,p,d.length]),c.useEffect(()=>{d.length>0&&k(!0)},[d.length]),c.useEffect(()=>{p&&o&&d.length===0?C([{id:"greeting",role:"assistant",content:o,timestamp:new Date}]):o&&d.length>0?C([{id:"greeting",role:"assistant",content:o,timestamp:new Date},...d]):C(d)},[d,p,o]);const{isPoppedOut:x,openPopout:j,focusPopout:G}=fe({apiUrl:e,tenantId:n,title:s,subtitle:a,placeholder:r,primaryColor:i,sessionId:D,messages:m,headers:w},P,()=>{});c.useEffect(()=>{if(b||x){const v=[...d].reverse().find(_=>!_.isStreaming&&_.content);v&&X(v.id,n),E(!1)}else{const v=d.filter(N=>!N.isStreaming&&N.content),_=me(n);if(v.length===0){E(!1);return}if(!_){E(v.some(N=>N.role==="assistant"));return}const A=v.findIndex(N=>N.id===_);if(A===-1){E(v.some(N=>N.role==="assistant"));return}const je=v.slice(A+1).some(N=>N.role==="assistant");E(je)}},[b,x,d,n]);const I=()=>{if(x){G();return}const v=!b;if(B(v),v){y==null||y();const _=[...d].reverse().find(A=>!A.isStreaming&&A.content);_&&X(_.id,n),E(!1)}else L==null||L()},H=async v=>{M==null||M(v),await T(v);const _=d[d.length-1];(_==null?void 0:_.role)==="assistant"&&(h==null||h(_.content))},$=()=>{z(),k(!1),C([]),E(!1),X(null,n)},Te=v=>{Z(v)},$e=()=>{B(!1),j()},Be=u==="top-left"?"gcw-container--top-left":u==="top-right"?"gcw-container--top-right":u==="bottom-left"?"gcw-container--bottom-left":"gcw-container--bottom-right";return t.jsxs("div",{className:`gcw-widget gcw-container ${Be}`,children:[b&&!x&&t.jsx(ae,{title:s,subtitle:a,placeholder:r,messages:m,isLoading:S,onSendMessage:H,onClose:I,onClear:$,onSizeChange:Te,onPopout:$e,primaryColor:i,size:P}),t.jsx(ie,{isOpen:b,onClick:I,primaryColor:i,hasUnread:R&&!b&&!x,isPoppedOut:x})]})}function K(e,n){let s;if(n){const r=document.getElementById(n);if(!r)throw new Error(`Container with id "${n}" not found`);s=r}else s=document.createElement("div"),s.id="geoapps-chat-widget-root",document.body.appendChild(s);const a=le.createRoot(s);return a.render(c.createElement(c.StrictMode,null,c.createElement(Q,e))),()=>{a.unmount(),!n&&s.parentNode&&s.parentNode.removeChild(s)}}if(typeof window<"u"){window.GeoAppsChatWidget={embed:K,ChatWidget:Q};const e=document.currentScript;if(e){const n=e.dataset.apiUrl;n&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{K({apiUrl:n,title:e.dataset.title,subtitle:e.dataset.subtitle,greeting:e.dataset.greeting,placeholder:e.dataset.placeholder,primaryColor:e.dataset.primaryColor,position:e.dataset.position,defaultOpen:e.dataset.defaultOpen==="true"})}):K({apiUrl:n,title:e.dataset.title,subtitle:e.dataset.subtitle,greeting:e.dataset.greeting,placeholder:e.dataset.placeholder,primaryColor:e.dataset.primaryColor,position:e.dataset.position,defaultOpen:e.dataset.defaultOpen==="true"}))}}f.ChatBubble=ne,f.ChatWidget=Q,f.ChatWindow=ae,f.MessageInput=re,f.MessageList=oe,f.ToggleButton=ie,f.embedChatWidget=K,f.useChat=se,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})});
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import { Message, ChatConfig } from '../types';
2
+
3
+ interface UseChatOptions extends ChatConfig {
4
+ onError?: (error: Error) => void;
5
+ /** Enable localStorage persistence (default: true) */
6
+ persist?: boolean;
7
+ }
8
+ interface UseChatReturn {
9
+ messages: Message[];
10
+ isLoading: boolean;
11
+ error: Error | null;
12
+ sendMessage: (content: string) => Promise<void>;
13
+ clearMessages: () => void;
14
+ sessionId: string | null;
15
+ }
16
+ export declare function useChat(options: UseChatOptions): UseChatReturn;
17
+ export {};
@@ -0,0 +1,9 @@
1
+ export { ChatWidget } from './components/ChatWidget';
2
+ export { ChatWindow } from './components/ChatWindow';
3
+ export { MessageList } from './components/MessageList';
4
+ export { MessageInput } from './components/MessageInput';
5
+ export { ChatBubble } from './components/ChatBubble';
6
+ export { ToggleButton } from './components/ToggleButton';
7
+ export { useChat } from './hooks/useChat';
8
+ export type { Message, ChatConfig, ChatWidgetProps } from './types';
9
+ export { embedChatWidget } from './embed';
package/dist/main.d.ts ADDED
@@ -0,0 +1 @@
1
+