@object-ui/plugin-chatbot 3.1.4 → 3.3.0

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 (42) hide show
  1. package/.turbo/turbo-build.log +40 -9
  2. package/CHANGELOG.md +27 -0
  3. package/README.md +86 -4
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +11875 -2897
  6. package/dist/index.umd.cjs +71 -28
  7. package/dist/{src → packages/plugin-chatbot/src}/ChatbotEnhanced.d.ts +8 -0
  8. package/dist/packages/plugin-chatbot/src/ChatbotEnhanced.d.ts.map +1 -0
  9. package/dist/packages/plugin-chatbot/src/FloatingChatbot.d.ts +15 -0
  10. package/dist/packages/plugin-chatbot/src/FloatingChatbot.d.ts.map +1 -0
  11. package/dist/packages/plugin-chatbot/src/FloatingChatbotPanel.d.ts +29 -0
  12. package/dist/packages/plugin-chatbot/src/FloatingChatbotPanel.d.ts.map +1 -0
  13. package/dist/packages/plugin-chatbot/src/FloatingChatbotProvider.d.ts +37 -0
  14. package/dist/packages/plugin-chatbot/src/FloatingChatbotProvider.d.ts.map +1 -0
  15. package/dist/packages/plugin-chatbot/src/FloatingChatbotTrigger.d.ts +21 -0
  16. package/dist/packages/plugin-chatbot/src/FloatingChatbotTrigger.d.ts.map +1 -0
  17. package/dist/{src → packages/plugin-chatbot/src}/index.d.ts +10 -0
  18. package/dist/packages/plugin-chatbot/src/index.d.ts.map +1 -0
  19. package/dist/packages/plugin-chatbot/src/renderer.d.ts.map +1 -0
  20. package/dist/packages/plugin-chatbot/src/useObjectChat.d.ts +112 -0
  21. package/dist/packages/plugin-chatbot/src/useObjectChat.d.ts.map +1 -0
  22. package/dist/packages/plugin-chatbot/src/utils.d.ts.map +1 -0
  23. package/package.json +10 -8
  24. package/src/ChatbotEnhanced.tsx +55 -4
  25. package/src/FloatingChatbot.tsx +89 -0
  26. package/src/FloatingChatbotPanel.tsx +102 -0
  27. package/src/FloatingChatbotProvider.tsx +80 -0
  28. package/src/FloatingChatbotTrigger.tsx +55 -0
  29. package/src/__tests__/ChatbotEnhancedStreaming.test.tsx +159 -0
  30. package/src/__tests__/FloatingChatbotProvider.test.tsx +134 -0
  31. package/src/__tests__/FloatingChatbotWidgets.test.tsx +165 -0
  32. package/src/__tests__/useObjectChat.test.tsx +347 -0
  33. package/src/index.tsx +19 -0
  34. package/src/renderer.tsx +261 -92
  35. package/src/useObjectChat.ts +344 -0
  36. package/vite.config.ts +2 -1
  37. package/dist/src/ChatbotEnhanced.d.ts.map +0 -1
  38. package/dist/src/index.d.ts.map +0 -1
  39. package/dist/src/renderer.d.ts.map +0 -1
  40. package/dist/src/utils.d.ts.map +0 -1
  41. /package/dist/{src → packages/plugin-chatbot/src}/renderer.d.ts +0 -0
  42. /package/dist/{src → packages/plugin-chatbot/src}/utils.d.ts +0 -0
package/src/renderer.tsx CHANGED
@@ -10,65 +10,66 @@ import { ComponentRegistry } from '@object-ui/core';
10
10
  import type { ChatbotSchema, ChatMessage } from '@object-ui/types';
11
11
  import { Chatbot } from './index';
12
12
  import { ChatbotEnhanced } from './ChatbotEnhanced';
13
- import { generateUniqueId } from './utils';
14
- import { useState } from 'react';
13
+ import { FloatingChatbot } from './FloatingChatbot';
14
+ import { useObjectChat } from './useObjectChat';
15
15
 
16
16
  /**
17
17
  * Chatbot component for Object UI
18
18
  *
19
19
  * @remarks
20
- * This component supports an optional `onSend` callback in the schema:
20
+ * This component supports two modes:
21
+ *
22
+ * **API Mode** (when `api` is set):
23
+ * - Uses @ai-sdk/react for SSE streaming, tool-calling, and production-grade chat
24
+ * - Connects to service-ai backend (e.g., /api/v1/ai/chat)
25
+ * - Supports streaming, stop, reload, clear actions
26
+ * - Schema fields: api, conversationId, systemPrompt, model, streamingEnabled, headers, requestBody, maxToolRoundtrips
27
+ *
28
+ * **Legacy Mode** (when `api` is not set):
29
+ * - Local auto-response for demo/playground use
30
+ * - Schema fields: autoResponse, autoResponseText, autoResponseDelay
31
+ *
32
+ * Both modes support the `onSend` callback:
21
33
  * - Signature: `onSend(content: string, messages: ChatMessage[]): void`
22
- * - Parameters:
23
- * - content: The message text that was sent
24
- * - messages: Array of all messages including the newly added message
25
- * - Called when: User sends a message via the input field
26
- * - Use case: Connect to backend API or trigger custom actions on message send
27
34
  */
28
35
  ComponentRegistry.register('chatbot',
29
- ({ schema, className, ...props }: { schema: ChatbotSchema; className?: string; [key: string]: any }) => {
30
- // Initialize messages from schema or use empty array
31
- const [messages, setMessages] = useState<ChatMessage[]>(
32
- schema.messages?.map((msg: any, idx: number) => ({
33
- id: msg.id || `msg-${idx}`,
34
- role: msg.role || 'user',
35
- content: msg.content || '',
36
- timestamp: typeof msg.timestamp === 'string' ? msg.timestamp : (msg.timestamp instanceof Date ? msg.timestamp.toISOString() : undefined),
37
- avatar: msg.avatar,
38
- avatarFallback: msg.avatarFallback,
39
- })) || []
40
- );
36
+ ({ schema, className, ...props }: { schema: ChatbotSchema & {
37
+ showTimestamp?: boolean;
38
+ disabled?: boolean;
39
+ userAvatarUrl?: string;
40
+ userAvatarFallback?: string;
41
+ assistantAvatarUrl?: string;
42
+ assistantAvatarFallback?: string;
43
+ maxHeight?: string;
44
+ autoResponse?: boolean;
45
+ autoResponseText?: string;
46
+ autoResponseDelay?: number;
47
+ onSend?: (content: string, messages: ChatMessage[]) => void;
48
+ }; className?: string; [key: string]: any }) => {
49
+ const {
50
+ messages,
51
+ isLoading,
52
+ sendMessage,
53
+ } = useObjectChat({
54
+ api: schema.api,
55
+ initialMessages: schema.messages,
56
+ conversationId: schema.conversationId,
57
+ systemPrompt: schema.systemPrompt,
58
+ model: schema.model,
59
+ streamingEnabled: schema.streamingEnabled,
60
+ headers: schema.headers,
61
+ body: schema.requestBody,
62
+ maxToolRoundtrips: schema.maxToolRoundtrips,
63
+ onError: schema.onError,
64
+ showTimestamp: schema.showTimestamp,
65
+ autoResponse: schema.autoResponse,
66
+ autoResponseText: schema.autoResponseText,
67
+ autoResponseDelay: schema.autoResponseDelay,
68
+ onSend: schema.onSend,
69
+ });
41
70
 
42
- // Handle sending new messages
43
71
  const handleSendMessage = (content: string) => {
44
- // Create user message with robust ID generation
45
- const userMessage: ChatMessage = {
46
- id: generateUniqueId('msg'),
47
- role: 'user',
48
- content,
49
- timestamp: schema.showTimestamp ? new Date().toLocaleTimeString() : undefined,
50
- };
51
-
52
- const updatedMessages = [...messages, userMessage];
53
- setMessages(updatedMessages);
54
-
55
- // If onSend callback is provided in schema, call it with updated messages
56
- if (schema.onSend) {
57
- schema.onSend(content, updatedMessages);
58
- }
59
-
60
- // Auto-response feature for demo purposes
61
- if (schema.autoResponse) {
62
- setTimeout(() => {
63
- const assistantMessage: ChatMessage = {
64
- id: generateUniqueId('msg'),
65
- role: 'assistant',
66
- content: schema.autoResponseText || 'Thank you for your message!',
67
- timestamp: schema.showTimestamp ? new Date().toLocaleTimeString() : undefined,
68
- };
69
- setMessages((prev) => [...prev, assistantMessage]);
70
- }, schema.autoResponseDelay || 1000);
71
- }
72
+ sendMessage(content);
72
73
  };
73
74
 
74
75
  return (
@@ -76,7 +77,7 @@ ComponentRegistry.register('chatbot',
76
77
  messages={messages as any}
77
78
  placeholder={schema.placeholder}
78
79
  onSendMessage={handleSendMessage}
79
- disabled={schema.disabled}
80
+ disabled={schema.disabled || isLoading}
80
81
  showTimestamp={schema.showTimestamp}
81
82
  userAvatarUrl={schema.userAvatarUrl}
82
83
  userAvatarFallback={schema.userAvatarFallback}
@@ -148,12 +149,42 @@ ComponentRegistry.register('chatbot',
148
149
  label: 'Max Height',
149
150
  defaultValue: '500px'
150
151
  },
152
+ {
153
+ name: 'api',
154
+ type: 'string',
155
+ label: 'API Endpoint',
156
+ description: 'Backend SSE endpoint (e.g., /api/v1/ai/chat). When set, enables streaming AI mode.'
157
+ },
158
+ {
159
+ name: 'conversationId',
160
+ type: 'string',
161
+ label: 'Conversation ID',
162
+ description: 'Multi-turn conversation identifier'
163
+ },
164
+ {
165
+ name: 'systemPrompt',
166
+ type: 'string',
167
+ label: 'System Prompt',
168
+ description: 'System prompt to configure assistant behavior'
169
+ },
170
+ {
171
+ name: 'model',
172
+ type: 'string',
173
+ label: 'AI Model',
174
+ description: 'AI model identifier (e.g., gpt-4o)'
175
+ },
176
+ {
177
+ name: 'streamingEnabled',
178
+ type: 'boolean',
179
+ label: 'Enable Streaming',
180
+ defaultValue: true
181
+ },
151
182
  {
152
183
  name: 'autoResponse',
153
184
  type: 'boolean',
154
185
  label: 'Enable Auto Response (Demo)',
155
186
  defaultValue: false,
156
- description: 'Automatically send a response after user message (for demo purposes)'
187
+ description: 'Automatically send a response after user message (for demo purposes, ignored when API is set)'
157
188
  },
158
189
  {
159
190
  name: 'autoResponseText',
@@ -200,53 +231,53 @@ ComponentRegistry.register('chatbot-enhanced',
200
231
  ({ schema, className, ...props }: { schema: ChatbotSchema & {
201
232
  enableMarkdown?: boolean;
202
233
  enableFileUpload?: boolean;
234
+ showTimestamp?: boolean;
235
+ disabled?: boolean;
236
+ userAvatarUrl?: string;
237
+ userAvatarFallback?: string;
238
+ assistantAvatarUrl?: string;
239
+ assistantAvatarFallback?: string;
240
+ maxHeight?: string;
241
+ autoResponse?: boolean;
242
+ autoResponseText?: string;
243
+ autoResponseDelay?: number;
244
+ onSend?: (content: string, messages: ChatMessage[]) => void;
203
245
  onClear?: () => void;
204
246
  }; className?: string; [key: string]: any }) => {
205
- const [messages, setMessages] = useState<ChatMessage[]>(
206
- schema.messages?.map((msg: any, idx: number) => ({
207
- id: msg.id || `msg-${idx}`,
208
- role: msg.role || 'user',
209
- content: msg.content || '',
210
- timestamp: typeof msg.timestamp === 'string' ? msg.timestamp : (msg.timestamp instanceof Date ? msg.timestamp.toISOString() : undefined),
211
- avatar: msg.avatar,
212
- avatarFallback: msg.avatarFallback,
213
- })) || []
214
- );
215
-
216
- const handleSendMessage = (content: string, _files?: File[]) => {
217
- const userMessage: ChatMessage = {
218
- id: generateUniqueId('msg'),
219
- role: 'user',
220
- content,
221
- timestamp: schema.showTimestamp ? new Date().toLocaleTimeString() : undefined,
222
- };
223
-
224
- const updatedMessages = [...messages, userMessage];
225
- setMessages(updatedMessages);
226
-
227
- if (schema.onSend) {
228
- schema.onSend(content, updatedMessages);
229
- }
247
+ const {
248
+ messages,
249
+ isLoading,
250
+ error,
251
+ sendMessage,
252
+ stop,
253
+ reload,
254
+ clear,
255
+ isApiMode,
256
+ } = useObjectChat({
257
+ api: schema.api,
258
+ initialMessages: schema.messages,
259
+ conversationId: schema.conversationId,
260
+ systemPrompt: schema.systemPrompt,
261
+ model: schema.model,
262
+ streamingEnabled: schema.streamingEnabled,
263
+ headers: schema.headers,
264
+ body: schema.requestBody,
265
+ maxToolRoundtrips: schema.maxToolRoundtrips,
266
+ onError: schema.onError,
267
+ showTimestamp: schema.showTimestamp,
268
+ autoResponse: schema.autoResponse,
269
+ autoResponseText: schema.autoResponseText,
270
+ autoResponseDelay: schema.autoResponseDelay,
271
+ onSend: schema.onSend,
272
+ });
230
273
 
231
- // Auto-response with streaming simulation
232
- if (schema.autoResponse) {
233
- setTimeout(() => {
234
- const assistantMessage: ChatMessage = {
235
- id: generateUniqueId('msg'),
236
- role: 'assistant',
237
- content: schema.autoResponseText || 'Thank you for your message!',
238
- timestamp: schema.showTimestamp ? new Date().toLocaleTimeString() : undefined,
239
- };
240
- setMessages((prev) => [...prev, assistantMessage]);
241
- }, schema.autoResponseDelay || 1000);
242
- }
274
+ const handleSendMessage = (content: string, files?: File[]) => {
275
+ sendMessage(content, files);
243
276
  };
244
277
 
245
278
  const handleClear = () => {
246
- setMessages([]);
247
- if (schema.onClear) {
248
- schema.onClear();
249
- }
279
+ clear();
280
+ schema.onClear?.();
250
281
  };
251
282
 
252
283
  return (
@@ -255,7 +286,11 @@ ComponentRegistry.register('chatbot-enhanced',
255
286
  placeholder={schema.placeholder}
256
287
  onSendMessage={handleSendMessage}
257
288
  onClear={handleClear}
289
+ onStop={isApiMode && isLoading ? stop : undefined}
290
+ onReload={isApiMode ? reload : undefined}
258
291
  disabled={schema.disabled}
292
+ isLoading={isLoading}
293
+ error={error}
259
294
  showTimestamp={schema.showTimestamp}
260
295
  userAvatarUrl={schema.userAvatarUrl}
261
296
  userAvatarFallback={schema.userAvatarFallback}
@@ -284,6 +319,11 @@ ComponentRegistry.register('chatbot-enhanced',
284
319
  { name: 'assistantAvatarUrl', type: 'string', label: 'Assistant Avatar URL' },
285
320
  { name: 'assistantAvatarFallback', type: 'string', label: 'Assistant Avatar Fallback', defaultValue: 'AI' },
286
321
  { name: 'maxHeight', type: 'string', label: 'Max Height', defaultValue: '500px' },
322
+ { name: 'api', type: 'string', label: 'API Endpoint', description: 'Backend SSE endpoint for streaming AI mode' },
323
+ { name: 'conversationId', type: 'string', label: 'Conversation ID' },
324
+ { name: 'systemPrompt', type: 'string', label: 'System Prompt' },
325
+ { name: 'model', type: 'string', label: 'AI Model' },
326
+ { name: 'streamingEnabled', type: 'boolean', label: 'Enable Streaming', defaultValue: true },
287
327
  { name: 'autoResponse', type: 'boolean', label: 'Enable Auto Response (Demo)', defaultValue: false },
288
328
  { name: 'autoResponseText', type: 'string', label: 'Auto Response Text', defaultValue: 'Thank you for your message!' },
289
329
  { name: 'autoResponseDelay', type: 'number', label: 'Auto Response Delay (ms)', defaultValue: 1000 },
@@ -312,3 +352,132 @@ ComponentRegistry.register('chatbot-enhanced',
312
352
  }
313
353
  }
314
354
  );
355
+
356
+ // Register Floating Chatbot (FAB widget)
357
+ ComponentRegistry.register('chatbot-floating',
358
+ ({ schema, className, ...props }: { schema: ChatbotSchema & {
359
+ enableMarkdown?: boolean;
360
+ enableFileUpload?: boolean;
361
+ showTimestamp?: boolean;
362
+ disabled?: boolean;
363
+ userAvatarUrl?: string;
364
+ userAvatarFallback?: string;
365
+ assistantAvatarUrl?: string;
366
+ assistantAvatarFallback?: string;
367
+ autoResponse?: boolean;
368
+ autoResponseText?: string;
369
+ autoResponseDelay?: number;
370
+ onSend?: (content: string, messages: ChatMessage[]) => void;
371
+ onClear?: () => void;
372
+ }; className?: string; [key: string]: any }) => {
373
+ const {
374
+ messages,
375
+ isLoading,
376
+ error,
377
+ sendMessage,
378
+ stop,
379
+ reload,
380
+ clear,
381
+ isApiMode,
382
+ } = useObjectChat({
383
+ api: schema.api,
384
+ initialMessages: schema.messages,
385
+ conversationId: schema.conversationId,
386
+ systemPrompt: schema.systemPrompt,
387
+ model: schema.model,
388
+ streamingEnabled: schema.streamingEnabled,
389
+ headers: schema.headers,
390
+ body: schema.requestBody,
391
+ maxToolRoundtrips: schema.maxToolRoundtrips,
392
+ onError: schema.onError,
393
+ showTimestamp: schema.showTimestamp,
394
+ autoResponse: schema.autoResponse,
395
+ autoResponseText: schema.autoResponseText,
396
+ autoResponseDelay: schema.autoResponseDelay,
397
+ onSend: schema.onSend,
398
+ });
399
+
400
+ const handleSendMessage = (content: string, files?: File[]) => {
401
+ sendMessage(content, files);
402
+ };
403
+
404
+ const handleClear = () => {
405
+ clear();
406
+ schema.onClear?.();
407
+ };
408
+
409
+ return (
410
+ <FloatingChatbot
411
+ floatingConfig={schema.floatingConfig}
412
+ messages={messages as any}
413
+ placeholder={schema.placeholder}
414
+ onSendMessage={handleSendMessage}
415
+ onClear={handleClear}
416
+ onStop={isApiMode && isLoading ? stop : undefined}
417
+ onReload={isApiMode ? reload : undefined}
418
+ disabled={schema.disabled}
419
+ isLoading={isLoading}
420
+ error={error}
421
+ showTimestamp={schema.showTimestamp}
422
+ userAvatarUrl={schema.userAvatarUrl}
423
+ userAvatarFallback={schema.userAvatarFallback}
424
+ assistantAvatarUrl={schema.assistantAvatarUrl}
425
+ assistantAvatarFallback={schema.assistantAvatarFallback}
426
+ enableMarkdown={schema.enableMarkdown ?? true}
427
+ enableFileUpload={schema.enableFileUpload ?? false}
428
+ className={className}
429
+ {...props}
430
+ />
431
+ );
432
+ },
433
+ {
434
+ namespace: 'plugin-chatbot',
435
+ label: 'Chatbot (Floating)',
436
+ inputs: [
437
+ { name: 'displayMode', type: 'string', label: 'Display Mode', defaultValue: 'floating', description: 'Set to "floating" for FAB widget' },
438
+ { name: 'floatingConfig.position', type: 'string', label: 'FAB Position', defaultValue: 'bottom-right', description: 'bottom-right or bottom-left' },
439
+ { name: 'floatingConfig.defaultOpen', type: 'boolean', label: 'Default Open', defaultValue: false },
440
+ { name: 'floatingConfig.panelWidth', type: 'number', label: 'Panel Width', defaultValue: 400 },
441
+ { name: 'floatingConfig.panelHeight', type: 'number', label: 'Panel Height', defaultValue: 520 },
442
+ { name: 'floatingConfig.title', type: 'string', label: 'Panel Title', defaultValue: 'Chat' },
443
+ { name: 'floatingConfig.triggerSize', type: 'number', label: 'Trigger Size', defaultValue: 56 },
444
+ { name: 'messages', type: 'array', label: 'Initial Messages' },
445
+ { name: 'placeholder', type: 'string', label: 'Input Placeholder', defaultValue: 'Type your message...' },
446
+ { name: 'enableMarkdown', type: 'boolean', label: 'Enable Markdown', defaultValue: true },
447
+ { name: 'enableFileUpload', type: 'boolean', label: 'Enable File Upload', defaultValue: false },
448
+ { name: 'api', type: 'string', label: 'API Endpoint', description: 'Backend SSE endpoint for streaming AI mode' },
449
+ { name: 'conversationId', type: 'string', label: 'Conversation ID' },
450
+ { name: 'systemPrompt', type: 'string', label: 'System Prompt' },
451
+ { name: 'model', type: 'string', label: 'AI Model' },
452
+ { name: 'streamingEnabled', type: 'boolean', label: 'Enable Streaming', defaultValue: true },
453
+ { name: 'autoResponse', type: 'boolean', label: 'Enable Auto Response (Demo)', defaultValue: false },
454
+ { name: 'autoResponseText', type: 'string', label: 'Auto Response Text', defaultValue: 'Thank you for your message!' },
455
+ { name: 'autoResponseDelay', type: 'number', label: 'Auto Response Delay (ms)', defaultValue: 1000 },
456
+ { name: 'className', type: 'string', label: 'CSS Class' },
457
+ ],
458
+ defaultProps: {
459
+ displayMode: 'floating',
460
+ floatingConfig: {
461
+ position: 'bottom-right',
462
+ defaultOpen: false,
463
+ panelWidth: 400,
464
+ panelHeight: 520,
465
+ title: 'Chat',
466
+ triggerSize: 56,
467
+ },
468
+ messages: [
469
+ {
470
+ id: 'welcome',
471
+ role: 'assistant',
472
+ content: 'Hello! How can I help you today?',
473
+ }
474
+ ],
475
+ placeholder: 'Type your message...',
476
+ enableMarkdown: true,
477
+ enableFileUpload: false,
478
+ autoResponse: true,
479
+ autoResponseText: 'Thank you for your message! This is an automated response.',
480
+ autoResponseDelay: 1000,
481
+ }
482
+ }
483
+ );