@chatwidgetai/chat-widget 0.1.0 → 0.1.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.
- package/dist/ai-chat-widget.umd.js +144 -219
- package/dist/ai-chat-widget.umd.js.map +1 -1
- package/dist/api/client.d.ts +9 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/components/ChatWidget.d.ts.map +1 -1
- package/dist/components/ChatWindow.d.ts +0 -2
- package/dist/components/ChatWindow.d.ts.map +1 -1
- package/dist/hooks/useChat.d.ts +0 -10
- package/dist/hooks/useChat.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +145 -220
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +145 -219
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/utils/actionExecutor.d.ts +0 -18
- package/dist/utils/actionExecutor.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -7,6 +7,65 @@ var react = require('react');
|
|
|
7
7
|
* API Client for Widget Communication
|
|
8
8
|
* Handles all HTTP requests to the widget API
|
|
9
9
|
*/
|
|
10
|
+
class ApiError extends Error {
|
|
11
|
+
constructor(message, status, options) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'ApiError';
|
|
14
|
+
this.status = status;
|
|
15
|
+
this.details = options?.details;
|
|
16
|
+
this.retryAfterMs = options?.retryAfterMs;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function parseRetryAfter(headerValue) {
|
|
20
|
+
if (!headerValue)
|
|
21
|
+
return undefined;
|
|
22
|
+
const seconds = Number(headerValue);
|
|
23
|
+
if (!Number.isNaN(seconds) && seconds >= 0) {
|
|
24
|
+
return seconds * 1000;
|
|
25
|
+
}
|
|
26
|
+
const date = Date.parse(headerValue);
|
|
27
|
+
if (!Number.isNaN(date)) {
|
|
28
|
+
return Math.max(0, date - Date.now());
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
async function buildApiError(response, defaultMessage) {
|
|
33
|
+
let parsedBody = null;
|
|
34
|
+
let message = defaultMessage;
|
|
35
|
+
try {
|
|
36
|
+
const raw = await response.text();
|
|
37
|
+
if (raw) {
|
|
38
|
+
try {
|
|
39
|
+
parsedBody = JSON.parse(raw);
|
|
40
|
+
if (typeof parsedBody === 'object' && parsedBody !== null) {
|
|
41
|
+
const body = parsedBody;
|
|
42
|
+
message = typeof body.error === 'string'
|
|
43
|
+
? body.error
|
|
44
|
+
: typeof body.message === 'string'
|
|
45
|
+
? body.message
|
|
46
|
+
: defaultMessage;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
message = raw;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
message = raw;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// ignore body parsing errors
|
|
59
|
+
}
|
|
60
|
+
let retryAfterMs = parseRetryAfter(response.headers.get('retry-after'));
|
|
61
|
+
if (retryAfterMs === undefined) {
|
|
62
|
+
retryAfterMs = parseRetryAfter(response.headers.get('x-ratelimit-reset'));
|
|
63
|
+
}
|
|
64
|
+
return new ApiError(message || defaultMessage, response.status, {
|
|
65
|
+
details: parsedBody,
|
|
66
|
+
retryAfterMs,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
10
69
|
class WidgetApiClient {
|
|
11
70
|
constructor(config) {
|
|
12
71
|
this.config = config;
|
|
@@ -23,23 +82,24 @@ class WidgetApiClient {
|
|
|
23
82
|
},
|
|
24
83
|
});
|
|
25
84
|
if (!response.ok) {
|
|
26
|
-
throw
|
|
85
|
+
throw await buildApiError(response, 'Failed to fetch config');
|
|
27
86
|
}
|
|
28
87
|
const json = await response.json();
|
|
29
88
|
return json;
|
|
30
89
|
}
|
|
31
90
|
async getOrCreateConversation(conversationId) {
|
|
32
|
-
const
|
|
91
|
+
const baseUrl = `${this.config.apiUrl}/api/widget/${this.config.widgetId}/conversation`;
|
|
92
|
+
const query = conversationId ? `?conversationId=${encodeURIComponent(conversationId)}` : '';
|
|
93
|
+
const response = await fetch(`${baseUrl}${query}`, {
|
|
33
94
|
method: 'GET',
|
|
34
95
|
headers: {
|
|
35
96
|
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
36
|
-
}
|
|
97
|
+
},
|
|
37
98
|
});
|
|
38
99
|
if (!response.ok) {
|
|
39
|
-
|
|
40
|
-
throw new Error(error.error || 'Failed to upload file');
|
|
100
|
+
throw await buildApiError(response, 'Failed to load conversation');
|
|
41
101
|
}
|
|
42
|
-
return
|
|
102
|
+
return response.json();
|
|
43
103
|
}
|
|
44
104
|
/**
|
|
45
105
|
* Upload a file
|
|
@@ -56,8 +116,7 @@ class WidgetApiClient {
|
|
|
56
116
|
body: formData,
|
|
57
117
|
});
|
|
58
118
|
if (!response.ok) {
|
|
59
|
-
|
|
60
|
-
throw new Error(error.error || 'Failed to upload file');
|
|
119
|
+
throw await buildApiError(response, 'Failed to upload file');
|
|
61
120
|
}
|
|
62
121
|
const result = await response.json();
|
|
63
122
|
return result.file;
|
|
@@ -80,8 +139,7 @@ class WidgetApiClient {
|
|
|
80
139
|
}),
|
|
81
140
|
});
|
|
82
141
|
if (!response.ok) {
|
|
83
|
-
|
|
84
|
-
throw new Error(error.error || 'Failed to send message');
|
|
142
|
+
throw await buildApiError(response, 'Failed to send message');
|
|
85
143
|
}
|
|
86
144
|
return response.json();
|
|
87
145
|
}
|
|
@@ -103,8 +161,7 @@ class WidgetApiClient {
|
|
|
103
161
|
}),
|
|
104
162
|
});
|
|
105
163
|
if (!response.ok) {
|
|
106
|
-
|
|
107
|
-
throw new Error(error.error || `Agent request failed with status ${response.status}`);
|
|
164
|
+
throw await buildApiError(response, `Agent request failed with status ${response.status}`);
|
|
108
165
|
}
|
|
109
166
|
const data = await response.json();
|
|
110
167
|
// Check if response indicates an error
|
|
@@ -116,7 +173,7 @@ class WidgetApiClient {
|
|
|
116
173
|
catch (error) {
|
|
117
174
|
// Enhance error messages
|
|
118
175
|
if (error instanceof TypeError && error.message.includes('fetch')) {
|
|
119
|
-
throw new
|
|
176
|
+
throw new ApiError('Network error: Unable to reach the server', 0);
|
|
120
177
|
}
|
|
121
178
|
throw error;
|
|
122
179
|
}
|
|
@@ -138,13 +195,13 @@ class WidgetApiClient {
|
|
|
138
195
|
}),
|
|
139
196
|
});
|
|
140
197
|
if (!response.ok) {
|
|
141
|
-
const
|
|
198
|
+
const apiError = await buildApiError(response, 'Failed to submit feedback');
|
|
142
199
|
console.error('Feedback submission failed:', {
|
|
143
200
|
status: response.status,
|
|
144
|
-
error:
|
|
201
|
+
error: apiError.details,
|
|
145
202
|
payload: { session_id: sessionId, message_id: messageId, feedback }
|
|
146
203
|
});
|
|
147
|
-
throw
|
|
204
|
+
throw apiError;
|
|
148
205
|
}
|
|
149
206
|
}
|
|
150
207
|
/**
|
|
@@ -252,111 +309,70 @@ function isStorageAvailable() {
|
|
|
252
309
|
}
|
|
253
310
|
|
|
254
311
|
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*/
|
|
258
|
-
/**
|
|
259
|
-
* Execute a client-side action
|
|
312
|
+
* useChat Hook
|
|
313
|
+
* Main state management for chat functionality
|
|
260
314
|
*/
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
case
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
315
|
+
function deriveErrorInfo(error) {
|
|
316
|
+
if (error instanceof ApiError) {
|
|
317
|
+
const retryAfterSeconds = typeof error.retryAfterMs === 'number'
|
|
318
|
+
? Math.max(1, Math.ceil(error.retryAfterMs / 1000))
|
|
319
|
+
: undefined;
|
|
320
|
+
const lowerMessage = (error.message || '').toLowerCase();
|
|
321
|
+
let message;
|
|
322
|
+
switch (error.status) {
|
|
323
|
+
case 429: {
|
|
324
|
+
const isPerUser = lowerMessage.includes('user');
|
|
325
|
+
const base = isPerUser
|
|
326
|
+
? 'You have reached the per-user rate limit.'
|
|
327
|
+
: 'This widget has received too many requests.';
|
|
328
|
+
if (retryAfterSeconds) {
|
|
329
|
+
message = `${base} Please wait ${retryAfterSeconds} second${retryAfterSeconds === 1 ? '' : 's'} before trying again.`;
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
message = `${base} Please wait a moment and try again.`;
|
|
333
|
+
}
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
case 401:
|
|
337
|
+
message = 'Authentication failed. Please refresh the page or verify your API key.';
|
|
338
|
+
break;
|
|
339
|
+
case 403:
|
|
340
|
+
message = 'Access to this widget is restricted. Please contact the site owner if you believe this is an error.';
|
|
341
|
+
break;
|
|
342
|
+
case 404:
|
|
343
|
+
message = 'We could not find this widget. It may have been removed.';
|
|
344
|
+
break;
|
|
273
345
|
default:
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
346
|
+
if (error.status >= 500) {
|
|
347
|
+
message = 'The server encountered an error. Please try again shortly.';
|
|
348
|
+
}
|
|
349
|
+
else if (error.status > 0) {
|
|
350
|
+
message = error.message || 'Something went wrong. Please try again.';
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
message = error.message || 'Unable to connect to the server. Please check your internet connection.';
|
|
354
|
+
}
|
|
279
355
|
}
|
|
356
|
+
return { message, retryAfterSeconds, status: error.status };
|
|
280
357
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
return {
|
|
296
|
-
}
|
|
297
|
-
try {
|
|
298
|
-
// Validate URL
|
|
299
|
-
new URL(url);
|
|
300
|
-
// Redirect
|
|
301
|
-
window.location.href = url;
|
|
302
|
-
return { success: true, result: { redirected_to: url } };
|
|
303
|
-
}
|
|
304
|
-
catch (error) {
|
|
305
|
-
return { success: false, error: 'Invalid URL' };
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Show a modal
|
|
310
|
-
*/
|
|
311
|
-
function executeShowModal(params) {
|
|
312
|
-
const { title, content } = params;
|
|
313
|
-
if (!title || !content) {
|
|
314
|
-
return { success: false, error: 'Title and content are required' };
|
|
315
|
-
}
|
|
316
|
-
// Use browser's alert as a simple modal
|
|
317
|
-
// In a real implementation, you'd use a proper modal component
|
|
318
|
-
alert(`${title}\n\n${content}`);
|
|
319
|
-
return { success: true, result: { shown: true } };
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Copy text to clipboard
|
|
323
|
-
*/
|
|
324
|
-
async function executeCopyToClipboard(params) {
|
|
325
|
-
const text = params.text;
|
|
326
|
-
if (!text) {
|
|
327
|
-
return { success: false, error: 'Text parameter is required' };
|
|
328
|
-
}
|
|
329
|
-
try {
|
|
330
|
-
await navigator.clipboard.writeText(text);
|
|
331
|
-
return { success: true, result: { copied: text } };
|
|
332
|
-
}
|
|
333
|
-
catch (error) {
|
|
334
|
-
return {
|
|
335
|
-
success: false,
|
|
336
|
-
error: 'Failed to copy to clipboard. Please check browser permissions.'
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Open chat with a specific message
|
|
342
|
-
*/
|
|
343
|
-
function executeOpenChat(params) {
|
|
344
|
-
const message = params.message;
|
|
345
|
-
if (!message) {
|
|
346
|
-
return { success: false, error: 'Message parameter is required' };
|
|
358
|
+
if (error instanceof Error) {
|
|
359
|
+
const lower = error.message.toLowerCase();
|
|
360
|
+
if (lower.includes('network')) {
|
|
361
|
+
return { message: 'Unable to connect to the server. Please check your internet connection.' };
|
|
362
|
+
}
|
|
363
|
+
if (lower.includes('timeout')) {
|
|
364
|
+
return { message: 'The request timed out. Please try again.' };
|
|
365
|
+
}
|
|
366
|
+
if (lower.includes('unauthorized') || lower.includes('401')) {
|
|
367
|
+
return { message: 'Authentication failed. Please refresh the page or verify your API key.' };
|
|
368
|
+
}
|
|
369
|
+
if (lower.includes('internal server error') || lower.includes('500')) {
|
|
370
|
+
return { message: 'The server encountered an error. Please try again shortly.' };
|
|
371
|
+
}
|
|
372
|
+
return { message: error.message || 'Something went wrong. Please try again.' };
|
|
347
373
|
}
|
|
348
|
-
|
|
349
|
-
const event = new CustomEvent('widget:open-chat', {
|
|
350
|
-
detail: { message }
|
|
351
|
-
});
|
|
352
|
-
window.dispatchEvent(event);
|
|
353
|
-
return { success: true, result: { message } };
|
|
374
|
+
return { message: 'Something went wrong. Please try again.' };
|
|
354
375
|
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* useChat Hook
|
|
358
|
-
* Main state management for chat functionality
|
|
359
|
-
*/
|
|
360
376
|
function useChat(options) {
|
|
361
377
|
const { widgetId, apiKey, apiUrl, onMessage, onError, } = options;
|
|
362
378
|
const [state, setState] = react.useState({
|
|
@@ -368,7 +384,6 @@ function useChat(options) {
|
|
|
368
384
|
conversationId: '', // Will be set after loading conversation
|
|
369
385
|
config: null,
|
|
370
386
|
});
|
|
371
|
-
const [pendingAction, setPendingAction] = react.useState(null);
|
|
372
387
|
const apiClient = react.useRef(new WidgetApiClient({ widgetId, apiKey, apiUrl }));
|
|
373
388
|
// Load configuration and conversation on mount
|
|
374
389
|
react.useEffect(() => {
|
|
@@ -393,8 +408,9 @@ function useChat(options) {
|
|
|
393
408
|
}));
|
|
394
409
|
}
|
|
395
410
|
catch (error) {
|
|
396
|
-
const
|
|
397
|
-
|
|
411
|
+
const errorInfo = deriveErrorInfo(error);
|
|
412
|
+
const err = error instanceof Error ? error : new Error(errorInfo.message);
|
|
413
|
+
setState(prev => ({ ...prev, error: errorInfo.message }));
|
|
398
414
|
onError?.(err);
|
|
399
415
|
}
|
|
400
416
|
};
|
|
@@ -449,7 +465,6 @@ function useChat(options) {
|
|
|
449
465
|
}
|
|
450
466
|
// Determine if widget has actions (use agent endpoint)
|
|
451
467
|
const useAgent = state.config?.behavior.agentic || (state.config?.actions && state.config.actions.length > 0);
|
|
452
|
-
console.log(useAgent);
|
|
453
468
|
let response;
|
|
454
469
|
if (useAgent) {
|
|
455
470
|
// Use agent endpoint - returns ConversationMessage[]
|
|
@@ -518,26 +533,9 @@ function useChat(options) {
|
|
|
518
533
|
}
|
|
519
534
|
}
|
|
520
535
|
catch (error) {
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
let userMessage = err.message;
|
|
524
|
-
// Handle specific error types
|
|
525
|
-
if (err.message.includes('Network') || err.message.includes('fetch')) {
|
|
526
|
-
userMessage = 'Unable to connect to the server. Please check your internet connection.';
|
|
527
|
-
}
|
|
528
|
-
else if (err.message.includes('401') || err.message.includes('Unauthorized')) {
|
|
529
|
-
userMessage = 'Authentication failed. Please refresh the page.';
|
|
530
|
-
}
|
|
531
|
-
else if (err.message.includes('500') || err.message.includes('Internal Server Error')) {
|
|
532
|
-
userMessage = 'The server encountered an error. Please try again later.';
|
|
533
|
-
}
|
|
534
|
-
else if (err.message.includes('timeout')) {
|
|
535
|
-
userMessage = 'Request timed out. Please try again.';
|
|
536
|
-
}
|
|
537
|
-
// Use fallback message if configured, otherwise use error message
|
|
536
|
+
const errorInfo = deriveErrorInfo(error);
|
|
537
|
+
const err = error instanceof Error ? error : new Error(errorInfo.message);
|
|
538
538
|
const fallbackMessage = state.config?.behavior?.fallbackMessage;
|
|
539
|
-
const errorMessage = fallbackMessage || userMessage;
|
|
540
|
-
// If fallback message is configured, add it as an assistant message
|
|
541
539
|
if (fallbackMessage) {
|
|
542
540
|
const fallbackAssistantMessage = {
|
|
543
541
|
id: generateMessageId(),
|
|
@@ -553,17 +551,16 @@ function useChat(options) {
|
|
|
553
551
|
messages: [...prev.messages, fallbackAssistantMessage],
|
|
554
552
|
isLoading: false,
|
|
555
553
|
isTyping: false,
|
|
556
|
-
error:
|
|
554
|
+
error: errorInfo.message,
|
|
557
555
|
}));
|
|
558
556
|
onMessage?.(fallbackAssistantMessage);
|
|
559
557
|
}
|
|
560
558
|
else {
|
|
561
|
-
// Show error message as assistant message for better UX
|
|
562
559
|
const errorAssistantMessage = {
|
|
563
560
|
id: generateMessageId(),
|
|
564
561
|
message: {
|
|
565
562
|
type: 'ai',
|
|
566
|
-
content: `⚠️ ${
|
|
563
|
+
content: `⚠️ ${errorInfo.message}`,
|
|
567
564
|
},
|
|
568
565
|
timestamp: new Date().toISOString(),
|
|
569
566
|
sources: [],
|
|
@@ -573,7 +570,7 @@ function useChat(options) {
|
|
|
573
570
|
messages: [...prev.messages, errorAssistantMessage],
|
|
574
571
|
isLoading: false,
|
|
575
572
|
isTyping: false,
|
|
576
|
-
error:
|
|
573
|
+
error: errorInfo.message,
|
|
577
574
|
}));
|
|
578
575
|
onMessage?.(errorAssistantMessage);
|
|
579
576
|
}
|
|
@@ -611,81 +608,12 @@ function useChat(options) {
|
|
|
611
608
|
}));
|
|
612
609
|
}
|
|
613
610
|
catch (error) {
|
|
614
|
-
const
|
|
611
|
+
const errorInfo = deriveErrorInfo(error);
|
|
612
|
+
const err = error instanceof Error ? error : new Error(errorInfo.message);
|
|
613
|
+
setState(prev => ({ ...prev, error: errorInfo.message }));
|
|
615
614
|
onError?.(err);
|
|
616
615
|
}
|
|
617
616
|
}, [state.conversationId, onError]);
|
|
618
|
-
/**
|
|
619
|
-
* Approve and execute pending action
|
|
620
|
-
*/
|
|
621
|
-
const approveAction = react.useCallback(async () => {
|
|
622
|
-
if (!pendingAction)
|
|
623
|
-
return;
|
|
624
|
-
setState(prev => ({ ...prev, isLoading: true, isTyping: true }));
|
|
625
|
-
try {
|
|
626
|
-
// Execute the client action
|
|
627
|
-
const actionResult = await executeClientAction(pendingAction.action);
|
|
628
|
-
// Update the pending action message to show execution result
|
|
629
|
-
const executionMessage = actionResult.success
|
|
630
|
-
? `✓ Action executed successfully`
|
|
631
|
-
: `✗ Action failed: ${actionResult.error}`;
|
|
632
|
-
setState(prev => ({
|
|
633
|
-
...prev,
|
|
634
|
-
messages: prev.messages.map(msg => msg.id === pendingAction.messageId
|
|
635
|
-
? { ...msg, message: { ...msg.message, content: `${msg.message.content}\n\n${executionMessage}` } }
|
|
636
|
-
: msg),
|
|
637
|
-
}));
|
|
638
|
-
// Clear pending action
|
|
639
|
-
setPendingAction(null);
|
|
640
|
-
// TODO: Implement continueAgent in API client if actions are needed
|
|
641
|
-
// For now, just show success message
|
|
642
|
-
const assistantMessage = {
|
|
643
|
-
id: generateMessageId(),
|
|
644
|
-
message: {
|
|
645
|
-
type: 'ai',
|
|
646
|
-
content: 'Action completed successfully.',
|
|
647
|
-
},
|
|
648
|
-
timestamp: new Date().toISOString(),
|
|
649
|
-
sources: [],
|
|
650
|
-
};
|
|
651
|
-
setState(prev => ({
|
|
652
|
-
...prev,
|
|
653
|
-
messages: [...prev.messages, assistantMessage],
|
|
654
|
-
isLoading: false,
|
|
655
|
-
isTyping: false,
|
|
656
|
-
}));
|
|
657
|
-
onMessage?.(assistantMessage);
|
|
658
|
-
}
|
|
659
|
-
catch (error) {
|
|
660
|
-
const err = error instanceof Error ? error : new Error('Failed to execute action');
|
|
661
|
-
setState(prev => ({
|
|
662
|
-
...prev,
|
|
663
|
-
isLoading: false,
|
|
664
|
-
isTyping: false,
|
|
665
|
-
error: err.message,
|
|
666
|
-
messages: prev.messages.map(msg => msg.id === pendingAction.messageId
|
|
667
|
-
? { ...msg, message: { ...msg.message, content: `${msg.message.content}\n\n✗ Failed to execute action` } }
|
|
668
|
-
: msg),
|
|
669
|
-
}));
|
|
670
|
-
setPendingAction(null);
|
|
671
|
-
onError?.(err);
|
|
672
|
-
}
|
|
673
|
-
}, [pendingAction, state.conversationId, onMessage, onError]);
|
|
674
|
-
/**
|
|
675
|
-
* Reject pending action
|
|
676
|
-
*/
|
|
677
|
-
const rejectAction = react.useCallback(() => {
|
|
678
|
-
if (!pendingAction)
|
|
679
|
-
return;
|
|
680
|
-
// Update message to show rejection
|
|
681
|
-
setState(prev => ({
|
|
682
|
-
...prev,
|
|
683
|
-
messages: prev.messages.map(msg => msg.id === pendingAction.messageId
|
|
684
|
-
? { ...msg, message: { ...msg.message, content: `${msg.message.content}\n\n✗ Action cancelled by user` } }
|
|
685
|
-
: msg),
|
|
686
|
-
}));
|
|
687
|
-
setPendingAction(null);
|
|
688
|
-
}, [pendingAction]);
|
|
689
617
|
return {
|
|
690
618
|
messages: state.messages,
|
|
691
619
|
isLoading: state.isLoading,
|
|
@@ -693,10 +621,7 @@ function useChat(options) {
|
|
|
693
621
|
error: state.error,
|
|
694
622
|
config: state.config,
|
|
695
623
|
conversationId: state.conversationId,
|
|
696
|
-
pendingAction,
|
|
697
624
|
sendMessage,
|
|
698
|
-
approveAction,
|
|
699
|
-
rejectAction,
|
|
700
625
|
clearMessages,
|
|
701
626
|
submitFeedback,
|
|
702
627
|
};
|
|
@@ -21611,7 +21536,7 @@ styleInject(css_248z);
|
|
|
21611
21536
|
|
|
21612
21537
|
const ChatWidget = ({ widgetId, apiKey, apiUrl = window.location.origin, position = 'bottom-right', theme: themeOverride, primaryColor, onOpen, onClose, onMessage, onError, }) => {
|
|
21613
21538
|
const [isOpen, setIsOpen] = react.useState(false);
|
|
21614
|
-
const { messages, isLoading, isTyping, error, config, sendMessage,
|
|
21539
|
+
const { messages, isLoading, isTyping, error, config, sendMessage, submitFeedback, } = useChat({
|
|
21615
21540
|
widgetId,
|
|
21616
21541
|
apiKey,
|
|
21617
21542
|
apiUrl,
|
|
@@ -21661,12 +21586,13 @@ const ChatWidget = ({ widgetId, apiKey, apiUrl = window.location.origin, positio
|
|
|
21661
21586
|
const handleFeedback = async (messageId, feedback) => {
|
|
21662
21587
|
await submitFeedback(messageId, feedback);
|
|
21663
21588
|
};
|
|
21664
|
-
return (jsxRuntime.jsx("div", { className: `ai-chat-widget ${effectiveTheme}`, style: customStyles, children: jsxRuntime.jsx("div", { className: `ai-chat-widget-container ${effectivePosition}`, children: isOpen ? (jsxRuntime.jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, error: error, config: config, onSendMessage: sendMessage,
|
|
21589
|
+
return (jsxRuntime.jsx("div", { className: `ai-chat-widget ${effectiveTheme}`, style: customStyles, children: jsxRuntime.jsx("div", { className: `ai-chat-widget-container ${effectivePosition}`, children: isOpen ? (jsxRuntime.jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, error: error, config: config, onSendMessage: sendMessage, onClose: handleToggle, onFeedback: handleFeedback })) : (jsxRuntime.jsx("button", { className: "ai-chat-button", onClick: handleToggle, "aria-label": "Open chat", style: {
|
|
21665
21590
|
width: config?.appearance.buttonSize || 60,
|
|
21666
21591
|
height: config?.appearance.buttonSize || 60,
|
|
21667
21592
|
}, children: config?.appearance.buttonIcon ? (jsxRuntime.jsx("span", { className: "ai-chat-button-icon", children: config.appearance.buttonIcon })) : (jsxRuntime.jsx("svg", { className: "ai-chat-button-svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) })) })) }) }));
|
|
21668
21593
|
};
|
|
21669
21594
|
|
|
21595
|
+
exports.ApiError = ApiError;
|
|
21670
21596
|
exports.ChatWidget = ChatWidget;
|
|
21671
21597
|
exports.useChat = useChat;
|
|
21672
21598
|
//# sourceMappingURL=index.js.map
|