@courseecho/ai-widget-react 1.0.22 → 1.0.24

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.
@@ -0,0 +1,389 @@
1
+ /**
2
+ * Canonical widget CSS — single source of truth for all framework packages.
3
+ *
4
+ * Isolation strategy:
5
+ * - React : injected into a Shadow DOM via ShadowWrapper (createPortal).
6
+ * :host { all: initial } resets inherited properties at the boundary.
7
+ * - jQuery : injected into attachShadow() root. Same :host reset applies.
8
+ * - Angular: ViewEncapsulation.ShadowDom — Angular handles the shadow root.
9
+ *
10
+ * NOTE: `@import` for Google Fonts does NOT work inside shadow-DOM <style> tags.
11
+ * Each package is responsible for loading the Inter font into document <head>
12
+ * separately (React: ensureFont(), jQuery: injectFontLink()).
13
+ */
14
+ export const WIDGET_CSS = `
15
+ /* ── Shadow-DOM host isolation ────────────────────────────────────────────── */
16
+ :host {
17
+ all: initial !important;
18
+ display: block !important;
19
+ }
20
+
21
+ /* Scoped box-model reset — only touches widget elements, not all of shadow DOM */
22
+ .aiwg-root *, .aiwg-root *::before, .aiwg-root *::after {
23
+ box-sizing: border-box;
24
+ margin: 0;
25
+ padding: 0;
26
+ }
27
+
28
+ /* ── Container ────────────────────────────────────────────────────────────── */
29
+ .aiwg-root {
30
+ --aiwg-primary: #6366f1;
31
+ --aiwg-primary-dark: #8b5cf6;
32
+ --aiwg-fg-color: #ffffff;
33
+ --aiwg-bg-color: #ffffff;
34
+ --aiwg-chat-bg: #f1f5f9;
35
+
36
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
37
+ font-size: 14px;
38
+ line-height: 1.5;
39
+ color: #1a1a2e;
40
+ display: flex;
41
+ flex-direction: column;
42
+ overflow: hidden;
43
+ border-radius: 16px;
44
+ background: var(--aiwg-bg-color);
45
+ position: fixed !important;
46
+ bottom: 24px;
47
+ right: 24px;
48
+ width: 380px !important;
49
+ max-width: calc(100vw - 48px) !important;
50
+ z-index: 99999;
51
+ transition: height 0.25s ease;
52
+ box-shadow: 0 24px 64px rgba(0,0,0,0.18);
53
+ }
54
+
55
+ /* ── Header ───────────────────────────────────────────────────────────────── */
56
+ .aiwg-header {
57
+ background: linear-gradient(135deg, var(--aiwg-primary) 0%, var(--aiwg-primary-dark) 100%);
58
+ padding: 16px 20px;
59
+ display: flex;
60
+ align-items: center;
61
+ gap: 12px;
62
+ flex-shrink: 0;
63
+ position: relative;
64
+ overflow: hidden;
65
+ }
66
+ .aiwg-header::before {
67
+ content: '';
68
+ position: absolute;
69
+ width: 140px; height: 140px;
70
+ background: rgba(255,255,255,0.08);
71
+ border-radius: 50%;
72
+ top: -50px; right: -30px;
73
+ }
74
+ .aiwg-avatar {
75
+ width: 40px; height: 40px;
76
+ background: rgba(255,255,255,0.25);
77
+ border-radius: 50%;
78
+ display: flex; align-items: center; justify-content: center;
79
+ font-size: 18px;
80
+ flex-shrink: 0;
81
+ border: 2px solid rgba(255,255,255,0.4);
82
+ }
83
+ .aiwg-header-info { flex: 1; }
84
+ .aiwg-title { color: #fff; font-weight: 600; font-size: 15px; }
85
+ .aiwg-subtitle {
86
+ color: rgba(255,255,255,0.75);
87
+ font-size: 12px;
88
+ display: flex; align-items: center; gap: 5px;
89
+ }
90
+ .aiwg-online-dot {
91
+ width: 7px; height: 7px;
92
+ background: #4ade80;
93
+ border-radius: 50%;
94
+ display: inline-block;
95
+ animation: aiwg-pulse 2s ease-in-out infinite;
96
+ }
97
+ @keyframes aiwg-pulse {
98
+ 0%, 100% { opacity: 1; transform: scale(1); }
99
+ 50% { opacity: 0.6; transform: scale(0.85); }
100
+ }
101
+ .aiwg-minimize-btn {
102
+ width: 36px; height: 36px;
103
+ background: rgba(255,255,255,0.18);
104
+ border: 1.5px solid rgba(255,255,255,0.3);
105
+ border-radius: 50%;
106
+ color: #fff; cursor: pointer;
107
+ display: flex; align-items: center; justify-content: center;
108
+ font-size: 18px; line-height: 1;
109
+ transition: background 0.15s, transform 0.15s;
110
+ flex-shrink: 0;
111
+ position: relative; z-index: 2;
112
+ }
113
+ .aiwg-minimize-btn * { cursor: pointer !important; pointer-events: none; }
114
+ .aiwg-minimize-btn:hover { background: rgba(255,255,255,0.32); transform: scale(1.08); }
115
+
116
+ /* ── Messages area ────────────────────────────────────────────────────────── */
117
+ .aiwg-messages {
118
+ flex: 1;
119
+ overflow-y: auto;
120
+ padding: 20px 16px 8px;
121
+ scroll-behavior: smooth;
122
+ display: flex;
123
+ flex-direction: column;
124
+ gap: 12px;
125
+ }
126
+ .aiwg-messages::-webkit-scrollbar { width: 4px; }
127
+ .aiwg-messages::-webkit-scrollbar-thumb { background: #e2e8f0; border-radius: 99px; }
128
+
129
+ /* ── Welcome card ─────────────────────────────────────────────────────────── */
130
+ .aiwg-welcome {
131
+ text-align: center;
132
+ padding: 24px 16px;
133
+ display: flex; flex-direction: column; align-items: center; gap: 8px;
134
+ }
135
+ .aiwg-welcome-icon { font-size: 36px; margin-bottom: 4px; }
136
+ .aiwg-welcome h4 { font-size: 15px; font-weight: 600; color: #1a1a2e; }
137
+ .aiwg-welcome p { font-size: 13px; color: #64748b; }
138
+
139
+ /* ── Message bubbles ──────────────────────────────────────────────────────── */
140
+ .aiwg-msg {
141
+ display: flex;
142
+ gap: 8px;
143
+ align-items: flex-end;
144
+ animation: aiwg-fade-up 0.2s ease both;
145
+ }
146
+ @keyframes aiwg-fade-up {
147
+ from { opacity: 0; transform: translateY(8px); }
148
+ to { opacity: 1; transform: translateY(0); }
149
+ }
150
+ .aiwg-msg--user { flex-direction: row-reverse; }
151
+ .aiwg-msg-avatar {
152
+ width: 28px; height: 28px; border-radius: 50%;
153
+ display: flex; align-items: center; justify-content: center;
154
+ font-size: 13px; flex-shrink: 0;
155
+ margin-bottom: 2px;
156
+ }
157
+ .aiwg-msg--bot .aiwg-msg-avatar { background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark)); color: var(--aiwg-fg-color); }
158
+ .aiwg-msg--user .aiwg-msg-avatar { background: #e2e8f0; color: #64748b; }
159
+ .aiwg-msg-body { max-width: 78%; }
160
+ .aiwg-msg-bubble {
161
+ padding: 10px 14px;
162
+ border-radius: 18px;
163
+ font-size: 14px;
164
+ line-height: 1.55;
165
+ word-break: break-word;
166
+ }
167
+ .aiwg-msg--bot .aiwg-msg-bubble { background: var(--aiwg-chat-bg); color: #1a1a2e; border-bottom-left-radius: 4px; }
168
+ .aiwg-msg--user .aiwg-msg-bubble { background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark)); color: var(--aiwg-fg-color); border-bottom-right-radius: 4px; }
169
+ .aiwg-msg-time { font-size: 10px; color: #94a3b8; margin-top: 4px; text-align: right; padding: 0 4px; }
170
+ .aiwg-msg--bot .aiwg-msg-time { text-align: left; }
171
+
172
+ /* ── Typing indicator ─────────────────────────────────────────────────────── */
173
+ .aiwg-typing { padding: 10px 14px; }
174
+ .aiwg-typing-dots { display: flex; gap: 4px; align-items: center; }
175
+ .aiwg-typing-dots span {
176
+ width: 7px; height: 7px;
177
+ background: #94a3b8; border-radius: 50%;
178
+ animation: aiwg-bounce 1.2s ease-in-out infinite;
179
+ }
180
+ .aiwg-typing-dots span:nth-child(2) { animation-delay: 0.2s; }
181
+ .aiwg-typing-dots span:nth-child(3) { animation-delay: 0.4s; }
182
+ @keyframes aiwg-bounce {
183
+ 0%, 80%, 100% { transform: translateY(0); }
184
+ 40% { transform: translateY(-6px); }
185
+ }
186
+
187
+ /* ── Quick-reply chips ────────────────────────────────────────────────────── */
188
+ .aiwg-chip-row {
189
+ padding: 6px 16px 2px;
190
+ display: flex;
191
+ flex-wrap: wrap;
192
+ gap: 6px;
193
+ flex-shrink: 0;
194
+ }
195
+ .aiwg-chip {
196
+ background: #f1f5f9;
197
+ border: 1px solid #e2e8f0;
198
+ border-radius: 99px;
199
+ padding: 4px 12px;
200
+ font-size: 12px;
201
+ color: #4f46e5;
202
+ cursor: pointer;
203
+ transition: all 0.15s;
204
+ white-space: nowrap;
205
+ font-weight: 500;
206
+ }
207
+ .aiwg-chip:hover { background: #ede9fe; border-color: #a5b4fc; }
208
+ .aiwg-chip-shimmer {
209
+ background: linear-gradient(90deg, #e2e8f0 25%, #f1f5f9 50%, #e2e8f0 75%);
210
+ background-size: 200% 100%;
211
+ border: 1px solid transparent;
212
+ border-radius: 99px;
213
+ padding: 4px 34px;
214
+ animation: aiwg-shimmer 1.4s ease-in-out infinite;
215
+ cursor: default;
216
+ pointer-events: none;
217
+ }
218
+ .aiwg-chip-ai-badge {
219
+ display: inline-block;
220
+ font-size: 9px; font-weight: 700;
221
+ background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark));
222
+ color: #fff;
223
+ padding: 1px 5px;
224
+ border-radius: 99px;
225
+ margin-left: 5px;
226
+ vertical-align: middle;
227
+ letter-spacing: 0.04em;
228
+ }
229
+ @keyframes aiwg-shimmer {
230
+ 0% { background-position: 200% 0; }
231
+ 100% { background-position: -200% 0; }
232
+ }
233
+
234
+ /* ── Input area ───────────────────────────────────────────────────────────── */
235
+ .aiwg-input-area {
236
+ padding: 12px 16px 16px;
237
+ flex-shrink: 0;
238
+ background: #fff;
239
+ border-top: 1px solid #f1f5f9;
240
+ position: relative;
241
+ }
242
+ .aiwg-input-row { display: flex; gap: 8px; align-items: flex-end; }
243
+ .aiwg-input {
244
+ flex: 1;
245
+ border: 1.5px solid #e2e8f0;
246
+ border-radius: 12px;
247
+ padding: 10px 14px;
248
+ font-size: 14px;
249
+ font-family: inherit;
250
+ outline: none;
251
+ resize: none;
252
+ background: #f8fafc;
253
+ color: #1a1a2e;
254
+ transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
255
+ line-height: 1.4;
256
+ max-height: 120px;
257
+ overflow-y: auto;
258
+ scrollbar-width: none;
259
+ -ms-overflow-style: none;
260
+ }
261
+ .aiwg-input::-webkit-scrollbar { display: none; }
262
+ .aiwg-input::placeholder { color: #94a3b8; }
263
+ .aiwg-input:focus {
264
+ border-color: var(--aiwg-primary-dark);
265
+ background: #fff;
266
+ box-shadow: 0 0 0 3px rgba(139,92,246,0.1);
267
+ }
268
+ .aiwg-send-btn {
269
+ width: 42px; height: 42px;
270
+ background: linear-gradient(135deg, var(--aiwg-primary), var(--aiwg-primary-dark));
271
+ border: none; border-radius: 12px;
272
+ color: var(--aiwg-fg-color); cursor: pointer;
273
+ display: flex; align-items: center; justify-content: center;
274
+ flex-shrink: 0;
275
+ transition: transform 0.15s, box-shadow 0.15s, opacity 0.15s;
276
+ box-shadow: 0 4px 12px rgba(99,102,241,0.35);
277
+ }
278
+ .aiwg-send-btn svg { width: 18px; height: 18px; }
279
+ .aiwg-send-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(99,102,241,0.4); }
280
+ .aiwg-send-btn:active { transform: translateY(0); }
281
+ .aiwg-send-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
282
+
283
+ /* ── Autocomplete suggestions dropdown ────────────────────────────────────── */
284
+ .aiwg-suggestions {
285
+ position: absolute;
286
+ bottom: calc(100% + 4px);
287
+ left: 16px; right: 16px;
288
+ background: #fff;
289
+ border: 1px solid #e2e8f0;
290
+ border-radius: 12px;
291
+ box-shadow: 0 8px 24px rgba(0,0,0,0.12);
292
+ overflow: hidden;
293
+ z-index: 100;
294
+ animation: aiwg-fade-up 0.15s ease both;
295
+ }
296
+ .aiwg-suggestions-header {
297
+ padding: 8px 14px 4px;
298
+ font-size: 11px; font-weight: 600;
299
+ color: #94a3b8;
300
+ text-transform: uppercase; letter-spacing: 0.06em;
301
+ border-bottom: 1px solid #f1f5f9;
302
+ }
303
+ .aiwg-suggestion-item {
304
+ display: flex; align-items: center; gap: 10px;
305
+ padding: 10px 14px;
306
+ cursor: pointer;
307
+ transition: background 0.1s;
308
+ border-bottom: 1px solid #f8fafc;
309
+ }
310
+ .aiwg-suggestion-item:last-child { border-bottom: none; }
311
+ .aiwg-suggestion-item:hover,
312
+ .aiwg-suggestion-item.aiwg-active { background: #f5f3ff; }
313
+ .aiwg-suggestion-icon { font-size: 16px; flex-shrink: 0; }
314
+ .aiwg-suggestion-main { flex: 1; min-width: 0; }
315
+ .aiwg-suggestion-text { font-size: 13px; font-weight: 500; color: #1a1a2e; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
316
+ .aiwg-suggestion-text mark { background: none; color: #6366f1; font-weight: 600; }
317
+ .aiwg-suggestion-desc { font-size: 11px; color: #94a3b8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
318
+ .aiwg-suggestion-badge { font-size: 10px; font-weight: 600; padding: 2px 8px; border-radius: 99px; background: #ede9fe; color: #6366f1; flex-shrink: 0; }
319
+ .aiwg-kbd-hint {
320
+ padding: 6px 14px;
321
+ font-size: 11px; color: #cbd5e1; background: #f8fafc;
322
+ display: flex; justify-content: flex-end; gap: 10px;
323
+ }
324
+ .aiwg-kbd-hint kbd { font-family: inherit; background: #e2e8f0; border-radius: 4px; padding: 1px 5px; font-size: 10px; color: #64748b; }
325
+
326
+ /* ── Error toast ──────────────────────────────────────────────────────────── */
327
+ .aiwg-error {
328
+ margin: 0 16px 8px;
329
+ padding: 8px 12px;
330
+ background: #fef2f2;
331
+ border: 1px solid #fecaca;
332
+ border-radius: 8px;
333
+ font-size: 12px;
334
+ color: #dc2626;
335
+ }
336
+
337
+ /* ── Minimized state ──────────────────────────────────────────────────────── */
338
+ .aiwg-root.aiwg-minimized .aiwg-messages,
339
+ .aiwg-root.aiwg-minimized .aiwg-chip-row,
340
+ .aiwg-root.aiwg-minimized .aiwg-error,
341
+ .aiwg-root.aiwg-minimized .aiwg-input-area,
342
+ .aiwg-root.aiwg-minimized .aiwg-powered { display: none !important; }
343
+ .aiwg-root.aiwg-minimized .aiwg-minimize-btn { display: none !important; }
344
+ .aiwg-root.aiwg-minimized { cursor: pointer; box-shadow: 0 8px 32px rgba(0,0,0,0.28); }
345
+ .aiwg-root.aiwg-minimized .aiwg-header { cursor: pointer !important; }
346
+
347
+ /* ── Powered-by footer ────────────────────────────────────────────────────── */
348
+ .aiwg-powered {
349
+ text-align: center;
350
+ padding: 6px 16px 10px;
351
+ font-size: 11px; color: #94a3b8;
352
+ flex-shrink: 0;
353
+ border-top: 1px solid #f1f5f9;
354
+ background: #fff;
355
+ }
356
+ .aiwg-powered a { color: #6366f1; text-decoration: none; font-weight: 500; }
357
+ .aiwg-powered a:hover { text-decoration: underline; }
358
+
359
+ /* ── Markdown rendering ───────────────────────────────────────────────────── */
360
+ .aiwg-msg-bubble strong { font-weight: 600; }
361
+ .aiwg-msg-bubble em { font-style: italic; }
362
+ .aiwg-msg-bubble code { background: rgba(0,0,0,0.08); padding: 1px 5px; border-radius: 4px; font-family: 'Fira Code','Consolas',monospace; font-size: 0.9em; }
363
+ .aiwg-msg--user .aiwg-msg-bubble code { background: rgba(255,255,255,0.2); }
364
+ .aiwg-msg-bubble ol, .aiwg-msg-bubble ul { padding-left: 18px; margin: 6px 0; display: flex; flex-direction: column; gap: 3px; }
365
+ .aiwg-msg-bubble ol { list-style: decimal; }
366
+ .aiwg-msg-bubble ul { list-style: disc; }
367
+ .aiwg-msg-bubble li { line-height: 1.5; }
368
+ .aiwg-msg-bubble h3, .aiwg-msg-bubble h4 { font-weight: 600; margin: 8px 0 4px; }
369
+ .aiwg-msg-bubble p { margin: 2px 0; }
370
+
371
+ /* ── Dark theme ───────────────────────────────────────────────────────────── */
372
+ .aiwg-root.aiwg-dark { background: #0f172a; color: #e2e8f0; }
373
+ .aiwg-dark .aiwg-msg--bot .aiwg-msg-bubble { background: #1e293b; color: #e2e8f0; }
374
+ .aiwg-dark .aiwg-input-area { background: #0f172a; border-top-color: #1e293b; }
375
+ .aiwg-dark .aiwg-input { background: #1e293b; border-color: #334155; color: #e2e8f0; }
376
+ .aiwg-dark .aiwg-input:focus { border-color: #8b5cf6; background: #1e293b; box-shadow: 0 0 0 3px rgba(139,92,246,0.2); }
377
+ .aiwg-dark .aiwg-suggestions { background: #1e293b; border-color: #334155; }
378
+ .aiwg-dark .aiwg-suggestions-header { color: #64748b; border-bottom-color: #334155; }
379
+ .aiwg-dark .aiwg-suggestion-item { border-bottom-color: #0f172a; }
380
+ .aiwg-dark .aiwg-suggestion-item:hover,
381
+ .aiwg-dark .aiwg-suggestion-item.aiwg-active { background: #2d1b69; }
382
+ .aiwg-dark .aiwg-suggestion-text { color: #e2e8f0; }
383
+ .aiwg-dark .aiwg-chip { background: #1e293b; border-color: #334155; }
384
+ .aiwg-dark .aiwg-chip:hover { background: #2d1b69; }
385
+ .aiwg-dark .aiwg-chip-shimmer { background: linear-gradient(90deg, #1e293b 25%, #334155 50%, #1e293b 75%); background-size: 200% 100%; }
386
+ .aiwg-dark .aiwg-kbd-hint { background: #1e293b; }
387
+ .aiwg-dark .aiwg-welcome h4 { color: #e2e8f0; }
388
+ .aiwg-dark .aiwg-powered { background: #0f172a; border-top-color: #1e293b; }
389
+ `;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * ShadowWrapper — renders children inside a Shadow DOM so the widget's CSS
3
+ * is completely isolated from the host application's styles.
4
+ *
5
+ * CSS is imported from @courseecho/ai-core-sdk (single source of truth).
6
+ * The :host { all: initial } rule in that CSS resets all inherited properties
7
+ * at the shadow boundary, preventing the host app from bleeding styles in.
8
+ */
9
+ import React from 'react';
10
+ export declare const ShadowWrapper: React.FC<{
11
+ children: React.ReactNode;
12
+ }>;
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * ShadowWrapper — renders children inside a Shadow DOM so the widget's CSS
4
+ * is completely isolated from the host application's styles.
5
+ *
6
+ * CSS is imported from @courseecho/ai-core-sdk (single source of truth).
7
+ * The :host { all: initial } rule in that CSS resets all inherited properties
8
+ * at the shadow boundary, preventing the host app from bleeding styles in.
9
+ */
10
+ import { useRef, useEffect, useState } from 'react';
11
+ import { createPortal } from 'react-dom';
12
+ import { WIDGET_CSS } from '@courseecho/ai-core-sdk';
13
+ // Inject Inter into document <head> once — fonts ARE shared across shadow DOM boundaries
14
+ function ensureFont() {
15
+ if (typeof document === 'undefined')
16
+ return;
17
+ if (document.getElementById('aiwg-inter-font'))
18
+ return;
19
+ const link = document.createElement('link');
20
+ link.id = 'aiwg-inter-font';
21
+ link.rel = 'stylesheet';
22
+ link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap';
23
+ document.head.appendChild(link);
24
+ }
25
+ export const ShadowWrapper = ({ children }) => {
26
+ const hostRef = useRef(null);
27
+ const [portalTarget, setPortalTarget] = useState(null);
28
+ useEffect(() => {
29
+ ensureFont();
30
+ const host = hostRef.current;
31
+ if (!host || host.shadowRoot)
32
+ return;
33
+ const shadow = host.attachShadow({ mode: 'open' });
34
+ // Inject widget CSS into the shadow root
35
+ const style = document.createElement('style');
36
+ style.textContent = WIDGET_CSS;
37
+ shadow.appendChild(style);
38
+ // Container where React portals the widget tree
39
+ const container = document.createElement('div');
40
+ shadow.appendChild(container);
41
+ setPortalTarget(container);
42
+ }, []);
43
+ return (_jsxs(_Fragment, { children: [_jsx("div", { ref: hostRef }), portalTarget && createPortal(children, portalTarget)] }));
44
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * AI Chat Widget - React Component (Modern Redesign)
3
+ * Usage: <AiChatWidget config={...} apiKey="..." />
4
+ */
5
+ import React from 'react';
6
+ import { AiContextConfig, AiMessage } from '@courseecho/ai-core-sdk';
7
+ export interface AiChatWidgetProps {
8
+ config: AiContextConfig;
9
+ apiKey?: string;
10
+ jwtToken?: string;
11
+ title?: string;
12
+ subtitle?: string;
13
+ placeholder?: string;
14
+ poweredBy?: string;
15
+ poweredByUrl?: string;
16
+ onError?: (error: string) => void;
17
+ onMessageReceived?: (message: AiMessage) => void;
18
+ className?: string;
19
+ /** Start widget collapsed to header-only. Default: false */
20
+ defaultMinimized?: boolean;
21
+ /** Full expanded height. Default: "580px" */
22
+ expandedHeight?: string;
23
+ }
24
+ export declare const AiChatWidget: React.FC<AiChatWidgetProps>;
25
+ export default AiChatWidget;