@devicai/ui 0.1.0 → 0.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.
- package/dist/cjs/api/client.js +46 -13
- package/dist/cjs/api/client.js.map +1 -1
- package/dist/cjs/components/AICommandBar/AICommandBar.js +314 -0
- package/dist/cjs/components/AICommandBar/AICommandBar.js.map +1 -0
- package/dist/cjs/components/AICommandBar/useAICommandBar.js +595 -0
- package/dist/cjs/components/AICommandBar/useAICommandBar.js.map +1 -0
- package/dist/cjs/components/ChatDrawer/ChatDrawer.js +200 -22
- package/dist/cjs/components/ChatDrawer/ChatDrawer.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ChatInput.js +12 -12
- package/dist/cjs/components/ChatDrawer/ChatInput.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ChatMessages.js +137 -29
- package/dist/cjs/components/ChatDrawer/ChatMessages.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ConversationSelector.js +93 -0
- package/dist/cjs/components/ChatDrawer/ConversationSelector.js.map +1 -0
- package/dist/cjs/components/ChatDrawer/ErrorBoundary.js +25 -0
- package/dist/cjs/components/ChatDrawer/ErrorBoundary.js.map +1 -0
- package/dist/cjs/components/Feedback/FeedbackModal.js +87 -0
- package/dist/cjs/components/Feedback/FeedbackModal.js.map +1 -0
- package/dist/cjs/components/Feedback/MessageActions.js +74 -0
- package/dist/cjs/components/Feedback/MessageActions.js.map +1 -0
- package/dist/cjs/hooks/useDevicChat.js +54 -27
- package/dist/cjs/hooks/useDevicChat.js.map +1 -1
- package/dist/cjs/hooks/useModelInterface.js +6 -6
- package/dist/cjs/hooks/usePolling.js +64 -30
- package/dist/cjs/hooks/usePolling.js.map +1 -1
- package/dist/cjs/index.js +11 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/provider/DevicContext.js +4 -4
- package/dist/cjs/provider/DevicProvider.js +2 -2
- package/dist/cjs/styles.css +1 -1
- package/dist/esm/api/client.d.ts +19 -3
- package/dist/esm/api/client.js +46 -13
- package/dist/esm/api/client.js.map +1 -1
- package/dist/esm/api/types.d.ts +40 -0
- package/dist/esm/components/AICommandBar/AICommandBar.d.ts +22 -0
- package/dist/esm/components/AICommandBar/AICommandBar.js +312 -0
- package/dist/esm/components/AICommandBar/AICommandBar.js.map +1 -0
- package/dist/esm/components/AICommandBar/AICommandBar.types.d.ts +321 -0
- package/dist/esm/components/AICommandBar/index.d.ts +3 -0
- package/dist/esm/components/AICommandBar/useAICommandBar.d.ts +57 -0
- package/dist/esm/components/AICommandBar/useAICommandBar.js +592 -0
- package/dist/esm/components/AICommandBar/useAICommandBar.js.map +1 -0
- package/dist/esm/components/AutocompleteInput/AutocompleteInput.d.ts +4 -0
- package/dist/esm/components/AutocompleteInput/AutocompleteInput.types.d.ts +50 -0
- package/dist/esm/components/AutocompleteInput/index.d.ts +4 -0
- package/dist/esm/components/AutocompleteInput/useAutocomplete.d.ts +29 -0
- package/dist/esm/components/ChatDrawer/ChatDrawer.d.ts +4 -2
- package/dist/esm/components/ChatDrawer/ChatDrawer.js +191 -13
- package/dist/esm/components/ChatDrawer/ChatDrawer.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ChatDrawer.types.d.ts +155 -5
- package/dist/esm/components/ChatDrawer/ChatInput.d.ts +2 -1
- package/dist/esm/components/ChatDrawer/ChatInput.js +2 -2
- package/dist/esm/components/ChatDrawer/ChatInput.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ChatMessages.d.ts +2 -4
- package/dist/esm/components/ChatDrawer/ChatMessages.js +136 -28
- package/dist/esm/components/ChatDrawer/ChatMessages.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ConversationSelector.d.ts +2 -0
- package/dist/esm/components/ChatDrawer/ConversationSelector.js +91 -0
- package/dist/esm/components/ChatDrawer/ConversationSelector.js.map +1 -0
- package/dist/esm/components/ChatDrawer/ErrorBoundary.d.ts +16 -0
- package/dist/esm/components/ChatDrawer/ErrorBoundary.js +23 -0
- package/dist/esm/components/ChatDrawer/ErrorBoundary.js.map +1 -0
- package/dist/esm/components/ChatDrawer/index.d.ts +2 -1
- package/dist/esm/components/Feedback/Feedback.types.d.ts +50 -0
- package/dist/esm/components/Feedback/FeedbackModal.d.ts +5 -0
- package/dist/esm/components/Feedback/FeedbackModal.js +85 -0
- package/dist/esm/components/Feedback/FeedbackModal.js.map +1 -0
- package/dist/esm/components/Feedback/MessageActions.d.ts +5 -0
- package/dist/esm/components/Feedback/MessageActions.js +72 -0
- package/dist/esm/components/Feedback/MessageActions.js.map +1 -0
- package/dist/esm/components/Feedback/index.d.ts +3 -0
- package/dist/esm/hooks/useDevicChat.js +37 -10
- package/dist/esm/hooks/useDevicChat.js.map +1 -1
- package/dist/esm/hooks/usePolling.js +46 -12
- package/dist/esm/hooks/usePolling.js.map +1 -1
- package/dist/esm/index.d.ts +7 -3
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/styles.css +1 -1
- package/package.json +10 -4
|
@@ -18,10 +18,10 @@ export interface ChatDrawerOptions {
|
|
|
18
18
|
*/
|
|
19
19
|
position?: 'left' | 'right';
|
|
20
20
|
/**
|
|
21
|
-
* Drawer width
|
|
22
|
-
* @default
|
|
21
|
+
* Drawer width as pixels (number) or CSS string (e.g. '50%', '400px')
|
|
22
|
+
* @default '100%'
|
|
23
23
|
*/
|
|
24
|
-
width?: number;
|
|
24
|
+
width?: number | string;
|
|
25
25
|
/**
|
|
26
26
|
* Whether drawer starts open
|
|
27
27
|
* @default false
|
|
@@ -60,9 +60,14 @@ export interface ChatDrawerOptions {
|
|
|
60
60
|
*/
|
|
61
61
|
inputPlaceholder?: string;
|
|
62
62
|
/**
|
|
63
|
-
* Title displayed in header
|
|
63
|
+
* Title displayed in header. Can be a string or React node.
|
|
64
64
|
*/
|
|
65
|
-
title?: string;
|
|
65
|
+
title?: string | React.ReactNode;
|
|
66
|
+
/**
|
|
67
|
+
* Show the assistant's image (from its imgUrl) as an avatar next to the title
|
|
68
|
+
* @default false
|
|
69
|
+
*/
|
|
70
|
+
showAvatar?: boolean;
|
|
66
71
|
/**
|
|
67
72
|
* Show tool execution timeline
|
|
68
73
|
* @default true
|
|
@@ -73,11 +78,120 @@ export interface ChatDrawerOptions {
|
|
|
73
78
|
* @default 1000
|
|
74
79
|
*/
|
|
75
80
|
zIndex?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Border radius for the drawer container
|
|
83
|
+
* @default 0
|
|
84
|
+
*/
|
|
85
|
+
borderRadius?: number | string;
|
|
86
|
+
/**
|
|
87
|
+
* Enable a draggable resize handle on the lateral border
|
|
88
|
+
* @default false
|
|
89
|
+
*/
|
|
90
|
+
resizable?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Minimum width when resizable (px)
|
|
93
|
+
* @default 300
|
|
94
|
+
*/
|
|
95
|
+
minWidth?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Maximum width when resizable (px)
|
|
98
|
+
* @default 800
|
|
99
|
+
*/
|
|
100
|
+
maxWidth?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Additional inline styles applied to the drawer container
|
|
103
|
+
*/
|
|
104
|
+
style?: React.CSSProperties;
|
|
105
|
+
/**
|
|
106
|
+
* Font family override
|
|
107
|
+
*/
|
|
108
|
+
fontFamily?: string;
|
|
109
|
+
/**
|
|
110
|
+
* Drawer background color
|
|
111
|
+
*/
|
|
112
|
+
backgroundColor?: string;
|
|
113
|
+
/**
|
|
114
|
+
* Text color
|
|
115
|
+
*/
|
|
116
|
+
textColor?: string;
|
|
117
|
+
/**
|
|
118
|
+
* Secondary background color (inputs, selector trigger, etc.)
|
|
119
|
+
*/
|
|
120
|
+
secondaryBackgroundColor?: string;
|
|
121
|
+
/**
|
|
122
|
+
* Drawer border color
|
|
123
|
+
*/
|
|
124
|
+
borderColor?: string;
|
|
125
|
+
/**
|
|
126
|
+
* User bubble background color
|
|
127
|
+
*/
|
|
128
|
+
userBubbleColor?: string;
|
|
129
|
+
/**
|
|
130
|
+
* Assistant bubble background color
|
|
131
|
+
*/
|
|
132
|
+
assistantBubbleColor?: string;
|
|
133
|
+
/**
|
|
134
|
+
* User bubble text color
|
|
135
|
+
*/
|
|
136
|
+
userBubbleTextColor?: string;
|
|
137
|
+
/**
|
|
138
|
+
* Assistant bubble text color
|
|
139
|
+
*/
|
|
140
|
+
assistantBubbleTextColor?: string;
|
|
141
|
+
/**
|
|
142
|
+
* Send button background color
|
|
143
|
+
*/
|
|
144
|
+
sendButtonColor?: string;
|
|
145
|
+
/**
|
|
146
|
+
* Custom loading indicator to replace the default 3-dot spinner
|
|
147
|
+
*/
|
|
148
|
+
loadingIndicator?: React.ReactNode;
|
|
149
|
+
/**
|
|
150
|
+
* Custom send button content. The click handler is managed by an overlay,
|
|
151
|
+
* so the node doesn't need to handle click events.
|
|
152
|
+
*/
|
|
153
|
+
sendButtonContent?: React.ReactNode;
|
|
154
|
+
/**
|
|
155
|
+
* Custom renderers for tool calls by tool name.
|
|
156
|
+
* When a completed tool has a matching renderer, it replaces the default summary line.
|
|
157
|
+
*/
|
|
158
|
+
toolRenderers?: Record<string, (input: any, output: any) => React.ReactNode>;
|
|
159
|
+
/**
|
|
160
|
+
* Custom icons for completed tool calls by tool name.
|
|
161
|
+
* Replaces the default check icon.
|
|
162
|
+
*/
|
|
163
|
+
toolIcons?: Record<string, React.ReactNode>;
|
|
164
|
+
/**
|
|
165
|
+
* Show feedback buttons (thumbs up/down) on assistant messages
|
|
166
|
+
* @default true
|
|
167
|
+
*/
|
|
168
|
+
showFeedback?: boolean;
|
|
76
169
|
}
|
|
77
170
|
/**
|
|
78
171
|
* Props for the ChatDrawer component
|
|
79
172
|
*/
|
|
173
|
+
/**
|
|
174
|
+
* Props for ConversationSelector component
|
|
175
|
+
*/
|
|
176
|
+
export interface ConversationSelectorProps {
|
|
177
|
+
assistantId: string;
|
|
178
|
+
currentChatUid: string | null;
|
|
179
|
+
onSelect: (chatUid: string) => void;
|
|
180
|
+
onNewChat: () => void;
|
|
181
|
+
apiKey?: string;
|
|
182
|
+
baseUrl?: string;
|
|
183
|
+
tenantId?: string;
|
|
184
|
+
}
|
|
80
185
|
export interface ChatDrawerProps {
|
|
186
|
+
/**
|
|
187
|
+
* Display mode: 'drawer' (default overlay) or 'inline' (embedded, always visible)
|
|
188
|
+
* @default 'drawer'
|
|
189
|
+
*/
|
|
190
|
+
mode?: 'drawer' | 'inline';
|
|
191
|
+
/**
|
|
192
|
+
* Callback when active conversation changes
|
|
193
|
+
*/
|
|
194
|
+
onConversationChange?: (chatUid: string) => void;
|
|
81
195
|
/**
|
|
82
196
|
* Assistant identifier
|
|
83
197
|
*/
|
|
@@ -156,11 +270,21 @@ export interface ChatDrawerProps {
|
|
|
156
270
|
*/
|
|
157
271
|
export interface ChatMessagesProps {
|
|
158
272
|
messages: ChatMessage[];
|
|
273
|
+
allMessages: ChatMessage[];
|
|
159
274
|
isLoading: boolean;
|
|
160
275
|
welcomeMessage?: string;
|
|
161
276
|
suggestedMessages?: string[];
|
|
162
277
|
onSuggestedClick?: (message: string) => void;
|
|
163
278
|
showToolTimeline?: boolean;
|
|
279
|
+
toolRenderers?: Record<string, (input: any, output: any) => React.ReactNode>;
|
|
280
|
+
toolIcons?: Record<string, React.ReactNode>;
|
|
281
|
+
loadingIndicator?: React.ReactNode;
|
|
282
|
+
/** Show feedback buttons on assistant messages */
|
|
283
|
+
showFeedback?: boolean;
|
|
284
|
+
/** Map of message ID to feedback state */
|
|
285
|
+
feedbackMap?: Map<string, 'positive' | 'negative'>;
|
|
286
|
+
/** Callback when feedback is submitted */
|
|
287
|
+
onFeedback?: (messageId: string, positive: boolean, comment?: string) => void;
|
|
164
288
|
}
|
|
165
289
|
/**
|
|
166
290
|
* Props for ChatInput component
|
|
@@ -172,6 +296,7 @@ export interface ChatInputProps {
|
|
|
172
296
|
enableFileUploads?: boolean;
|
|
173
297
|
allowedFileTypes?: AllowedFileTypes;
|
|
174
298
|
maxFileSize?: number;
|
|
299
|
+
sendButtonContent?: React.ReactNode;
|
|
175
300
|
}
|
|
176
301
|
/**
|
|
177
302
|
* Props for ToolTimeline component
|
|
@@ -195,3 +320,28 @@ export interface TriggerPosition {
|
|
|
195
320
|
left?: number;
|
|
196
321
|
top?: number;
|
|
197
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Handle for programmatically controlling the ChatDrawer
|
|
325
|
+
*/
|
|
326
|
+
export interface ChatDrawerHandle {
|
|
327
|
+
/**
|
|
328
|
+
* Open the drawer
|
|
329
|
+
*/
|
|
330
|
+
open: () => void;
|
|
331
|
+
/**
|
|
332
|
+
* Close the drawer
|
|
333
|
+
*/
|
|
334
|
+
close: () => void;
|
|
335
|
+
/**
|
|
336
|
+
* Toggle the drawer visibility
|
|
337
|
+
*/
|
|
338
|
+
toggle: () => void;
|
|
339
|
+
/**
|
|
340
|
+
* Set the chat UID to load a specific conversation
|
|
341
|
+
*/
|
|
342
|
+
setChatUid: (chatUid: string) => void;
|
|
343
|
+
/**
|
|
344
|
+
* Send a message in the drawer
|
|
345
|
+
*/
|
|
346
|
+
sendMessage: (message: string) => void;
|
|
347
|
+
}
|
|
@@ -2,4 +2,5 @@ import type { ChatInputProps } from './ChatDrawer.types';
|
|
|
2
2
|
/**
|
|
3
3
|
* Chat input component with file upload support
|
|
4
4
|
*/
|
|
5
|
-
export declare function ChatInput({ onSend, disabled, placeholder, enableFileUploads, allowedFileTypes, maxFileSize,
|
|
5
|
+
export declare function ChatInput({ onSend, disabled, placeholder, enableFileUploads, allowedFileTypes, maxFileSize, // 10MB
|
|
6
|
+
sendButtonContent, }: ChatInputProps): JSX.Element;
|
|
@@ -17,7 +17,7 @@ const FILE_TYPE_ACCEPT = {
|
|
|
17
17
|
* Chat input component with file upload support
|
|
18
18
|
*/
|
|
19
19
|
function ChatInput({ onSend, disabled = false, placeholder = 'Type a message...', enableFileUploads = false, allowedFileTypes = { images: true, documents: true }, maxFileSize = 10 * 1024 * 1024, // 10MB
|
|
20
|
-
}) {
|
|
20
|
+
sendButtonContent, }) {
|
|
21
21
|
const [message, setMessage] = useState('');
|
|
22
22
|
const [files, setFiles] = useState([]);
|
|
23
23
|
const textareaRef = useRef(null);
|
|
@@ -84,7 +84,7 @@ function ChatInput({ onSend, disabled = false, placeholder = 'Type a message...'
|
|
|
84
84
|
return (jsxs("div", { className: "devic-input-area", children: [files.length > 0 && (jsx("div", { className: "devic-file-preview", children: files.map((file, idx) => (jsxs("div", { className: "devic-file-preview-item", children: [jsx(FileIcon, {}), jsx("span", { children: file.name }), jsx("button", { className: "devic-file-remove", onClick: () => removeFile(idx), type: "button", children: "\u00D7" })] }, idx))) })), jsxs("div", { className: "devic-input-wrapper", children: [enableFileUploads && (jsxs(Fragment, { children: [jsx("input", { ref: fileInputRef, type: "file", accept: acceptedTypes, multiple: true, onChange: handleFileSelect, style: { display: 'none' } }), jsx("button", { className: "devic-input-btn", onClick: () => fileInputRef.current?.click(), disabled: disabled, type: "button", title: "Attach file", children: jsx(AttachIcon, {}) })] })), jsx("textarea", { ref: textareaRef, className: "devic-input", value: message, onChange: (e) => {
|
|
85
85
|
setMessage(e.target.value);
|
|
86
86
|
handleInput();
|
|
87
|
-
}, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled, rows: 1 }), jsx("button", { className: "devic-input-btn devic-send-btn", onClick: handleSend, disabled: disabled || (!message.trim() && files.length === 0), type: "button", title: "Send message", children: jsx(SendIcon, {}) })] })] }));
|
|
87
|
+
}, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled, rows: 1 }), sendButtonContent ? (jsxs("div", { className: "devic-send-btn-wrapper", children: [jsx("div", { className: "devic-send-btn-custom", "aria-hidden": "true", children: sendButtonContent }), jsx("button", { className: "devic-send-btn-overlay", onClick: handleSend, disabled: disabled || (!message.trim() && files.length === 0), type: "button", title: "Send message" })] })) : (jsx("button", { className: "devic-input-btn devic-send-btn", onClick: handleSend, disabled: disabled || (!message.trim() && files.length === 0), type: "button", title: "Send message", children: jsx(SendIcon, {}) }))] })] }));
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
90
|
* Get file type category from MIME type
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInput.js","sources":["../../../../src/components/ChatDrawer/ChatInput.tsx"],"sourcesContent":["import React, { useState, useRef, useCallback } from 'react';\nimport type { ChatInputProps } from './ChatDrawer.types';\nimport type { ChatFile } from '../../api/types';\n\nconst FILE_TYPE_ACCEPT: Record<string, string[]> = {\n images: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],\n documents: [\n 'application/pdf',\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'text/plain',\n 'text/csv',\n ],\n audio: ['audio/mpeg', 'audio/wav', 'audio/ogg'],\n video: ['video/mp4', 'video/webm', 'video/ogg'],\n};\n\n/**\n * Chat input component with file upload support\n */\nexport function ChatInput({\n onSend,\n disabled = false,\n placeholder = 'Type a message...',\n enableFileUploads = false,\n allowedFileTypes = { images: true, documents: true },\n maxFileSize = 10 * 1024 * 1024, // 10MB\n}: ChatInputProps): JSX.Element {\n const [message, setMessage] = useState('');\n const [files, setFiles] = useState<File[]>([]);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Calculate accepted file types\n const acceptedTypes = Object.entries(allowedFileTypes)\n .filter(([, enabled]) => enabled)\n .flatMap(([type]) => FILE_TYPE_ACCEPT[type] || [])\n .join(',');\n\n // Auto-resize textarea\n const handleInput = useCallback(() => {\n const textarea = textareaRef.current;\n if (textarea) {\n textarea.style.height = 'auto';\n textarea.style.height = `${Math.min(textarea.scrollHeight, 120)}px`;\n }\n }, []);\n\n // Handle send\n const handleSend = useCallback(() => {\n const trimmedMessage = message.trim();\n if (!trimmedMessage && files.length === 0) return;\n\n // Convert File objects to ChatFile format\n const chatFiles: ChatFile[] = files.map((file) => ({\n name: file.name,\n fileType: getFileType(file.type),\n }));\n\n onSend(trimmedMessage, chatFiles.length > 0 ? chatFiles : undefined);\n setMessage('');\n setFiles([]);\n\n // Reset textarea height\n if (textareaRef.current) {\n textareaRef.current.style.height = 'auto';\n }\n }, [message, files, onSend]);\n\n // Handle key press\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n },\n [handleSend]\n );\n\n // Handle file selection\n const handleFileSelect = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const selectedFiles = Array.from(e.target.files || []);\n\n // Filter valid files\n const validFiles = selectedFiles.filter((file) => {\n if (file.size > maxFileSize) {\n console.warn(`File ${file.name} exceeds maximum size`);\n return false;\n }\n return true;\n });\n\n setFiles((prev) => [...prev, ...validFiles]);\n\n // Reset input\n if (fileInputRef.current) {\n fileInputRef.current.value = '';\n }\n },\n [maxFileSize]\n );\n\n // Remove file\n const removeFile = useCallback((index: number) => {\n setFiles((prev) => prev.filter((_, i) => i !== index));\n }, []);\n\n return (\n <div className=\"devic-input-area\">\n {files.length > 0 && (\n <div className=\"devic-file-preview\">\n {files.map((file, idx) => (\n <div key={idx} className=\"devic-file-preview-item\">\n <FileIcon />\n <span>{file.name}</span>\n <button\n className=\"devic-file-remove\"\n onClick={() => removeFile(idx)}\n type=\"button\"\n >\n ×\n </button>\n </div>\n ))}\n </div>\n )}\n\n <div className=\"devic-input-wrapper\">\n {enableFileUploads && (\n <>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={acceptedTypes}\n multiple\n onChange={handleFileSelect}\n style={{ display: 'none' }}\n />\n <button\n className=\"devic-input-btn\"\n onClick={() => fileInputRef.current?.click()}\n disabled={disabled}\n type=\"button\"\n title=\"Attach file\"\n >\n <AttachIcon />\n </button>\n </>\n )}\n\n <textarea\n ref={textareaRef}\n className=\"devic-input\"\n value={message}\n onChange={(e) => {\n setMessage(e.target.value);\n handleInput();\n }}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n rows={1}\n />\n\n <button\n className=\"devic-input-btn devic-send-btn\"\n onClick={handleSend}\n disabled={disabled || (!message.trim() && files.length === 0)}\n type=\"button\"\n title=\"Send message\"\n >\n <SendIcon />\n </button>\n </div>\n </div>\n );\n}\n\n/**\n * Get file type category from MIME type\n */\nfunction getFileType(\n mimeType: string\n): 'image' | 'document' | 'audio' | 'video' | 'other' {\n if (mimeType.startsWith('image/')) return 'image';\n if (mimeType.startsWith('audio/')) return 'audio';\n if (mimeType.startsWith('video/')) return 'video';\n if (\n mimeType.startsWith('application/pdf') ||\n mimeType.startsWith('application/msword') ||\n mimeType.startsWith('text/')\n ) {\n return 'document';\n }\n return 'other';\n}\n\n/**\n * Attach icon\n */\nfunction AttachIcon(): JSX.Element {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\" />\n </svg>\n );\n}\n\n/**\n * Send icon\n */\nfunction SendIcon(): JSX.Element {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" />\n </svg>\n );\n}\n\n/**\n * File icon\n */\nfunction FileIcon(): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <polyline points=\"14,2 14,8 20,8\" />\n </svg>\n );\n}\n"],"names":["_jsxs","_jsx","_Fragment"],"mappings":";;;AAIA,MAAM,gBAAgB,GAA6B;IACjD,MAAM,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,CAAC;AAC9D,IAAA,SAAS,EAAE;QACT,iBAAiB;QACjB,oBAAoB;QACpB,yEAAyE;QACzE,YAAY;QACZ,UAAU;AACX,KAAA;AACD,IAAA,KAAK,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,CAAC;AAC/C,IAAA,KAAK,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,WAAW,CAAC;CAChD;AAED;;AAEG;SACa,SAAS,CAAC,EACxB,MAAM,EACN,QAAQ,GAAG,KAAK,EAChB,WAAW,GAAG,mBAAmB,EACjC,iBAAiB,GAAG,KAAK,EACzB,gBAAgB,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EACpD,WAAW,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI;AACf,EAAA,EAAA;IACf,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;IAC1C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAS,EAAE,CAAC;AAC9C,IAAA,MAAM,WAAW,GAAG,MAAM,CAAsB,IAAI,CAAC;AACrD,IAAA,MAAM,YAAY,GAAG,MAAM,CAAmB,IAAI,CAAC;;AAGnD,IAAA,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB;SAClD,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,OAAO;AAC/B,SAAA,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE;SAChD,IAAI,CAAC,GAAG,CAAC;;AAGZ,IAAA,MAAM,WAAW,GAAG,WAAW,CAAC,MAAK;AACnC,QAAA,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO;QACpC,IAAI,QAAQ,EAAE;AACZ,YAAA,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM;AAC9B,YAAA,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,EAAE,GAAG,CAAC,IAAI;QACrE;IACF,CAAC,EAAE,EAAE,CAAC;;AAGN,IAAA,MAAM,UAAU,GAAG,WAAW,CAAC,MAAK;AAClC,QAAA,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,EAAE;AACrC,QAAA,IAAI,CAAC,cAAc,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE;;QAG3C,MAAM,SAAS,GAAe,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM;YACjD,IAAI,EAAE,IAAI,CAAC,IAAI;AACf,YAAA,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC,SAAA,CAAC,CAAC;AAEH,QAAA,MAAM,CAAC,cAAc,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,GAAG,SAAS,CAAC;QACpE,UAAU,CAAC,EAAE,CAAC;QACd,QAAQ,CAAC,EAAE,CAAC;;AAGZ,QAAA,IAAI,WAAW,CAAC,OAAO,EAAE;YACvB,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM;QAC3C;IACF,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;;AAG5B,IAAA,MAAM,aAAa,GAAG,WAAW,CAC/B,CAAC,CAAsB,KAAI;QACzB,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE;YACpC,CAAC,CAAC,cAAc,EAAE;AAClB,YAAA,UAAU,EAAE;QACd;AACF,IAAA,CAAC,EACD,CAAC,UAAU,CAAC,CACb;;AAGD,IAAA,MAAM,gBAAgB,GAAG,WAAW,CAClC,CAAC,CAAsC,KAAI;AACzC,QAAA,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;;QAGtD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,KAAI;AAC/C,YAAA,IAAI,IAAI,CAAC,IAAI,GAAG,WAAW,EAAE;gBAC3B,OAAO,CAAC,IAAI,CAAC,CAAA,KAAA,EAAQ,IAAI,CAAC,IAAI,CAAA,qBAAA,CAAuB,CAAC;AACtD,gBAAA,OAAO,KAAK;YACd;AACA,YAAA,OAAO,IAAI;AACb,QAAA,CAAC,CAAC;AAEF,QAAA,QAAQ,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,UAAU,CAAC,CAAC;;AAG5C,QAAA,IAAI,YAAY,CAAC,OAAO,EAAE;AACxB,YAAA,YAAY,CAAC,OAAO,CAAC,KAAK,GAAG,EAAE;QACjC;AACF,IAAA,CAAC,EACD,CAAC,WAAW,CAAC,CACd;;AAGD,IAAA,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,KAAa,KAAI;QAC/C,QAAQ,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,CAAC;IACxD,CAAC,EAAE,EAAE,CAAC;AAEN,IAAA,QACEA,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,kBAAkB,aAC9B,KAAK,CAAC,MAAM,GAAG,CAAC,KACfC,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,oBAAoB,EAAA,QAAA,EAChC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,MACnBD,cAAe,SAAS,EAAC,yBAAyB,EAAA,QAAA,EAAA,CAChDC,GAAA,CAAC,QAAQ,EAAA,EAAA,CAAG,EACZA,GAAA,CAAA,MAAA,EAAA,EAAA,QAAA,EAAO,IAAI,CAAC,IAAI,EAAA,CAAQ,EACxBA,GAAA,CAAA,QAAA,EAAA,EACE,SAAS,EAAC,mBAAmB,EAC7B,OAAO,EAAE,MAAM,UAAU,CAAC,GAAG,CAAC,EAC9B,IAAI,EAAC,QAAQ,EAAA,QAAA,EAAA,QAAA,EAAA,CAGN,CAAA,EAAA,EATD,GAAG,CAUP,CACP,CAAC,EAAA,CACE,CACP,EAEDD,cAAK,SAAS,EAAC,qBAAqB,EAAA,QAAA,EAAA,CACjC,iBAAiB,KAChBA,IAAA,CAAAE,QAAA,EAAA,EAAA,QAAA,EAAA,CACED,GAAA,CAAA,OAAA,EAAA,EACE,GAAG,EAAE,YAAY,EACjB,IAAI,EAAC,MAAM,EACX,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAA,IAAA,EACR,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAA,CAC1B,EACFA,gBACE,SAAS,EAAC,iBAAiB,EAC3B,OAAO,EAAE,MAAM,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,EAC5C,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAC,QAAQ,EACb,KAAK,EAAC,aAAa,EAAA,QAAA,EAEnBA,IAAC,UAAU,EAAA,EAAA,CAAG,GACP,CAAA,EAAA,CACR,CACJ,EAEDA,GAAA,CAAA,UAAA,EAAA,EACE,GAAG,EAAE,WAAW,EAChB,SAAS,EAAC,aAAa,EACvB,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,CAAC,CAAC,KAAI;AACd,4BAAA,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;AAC1B,4BAAA,WAAW,EAAE;AACf,wBAAA,CAAC,EACD,SAAS,EAAE,aAAa,EACxB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,EAAA,CACP,EAEFA,GAAA,CAAA,QAAA,EAAA,EACE,SAAS,EAAC,gCAAgC,EAC1C,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,QAAQ,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,EAC7D,IAAI,EAAC,QAAQ,EACb,KAAK,EAAC,cAAc,EAAA,QAAA,EAEpBA,GAAA,CAAC,QAAQ,EAAA,EAAA,CAAG,EAAA,CACL,CAAA,EAAA,CACL,CAAA,EAAA,CACF;AAEV;AAEA;;AAEG;AACH,SAAS,WAAW,CAClB,QAAgB,EAAA;AAEhB,IAAA,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC;AAAE,QAAA,OAAO,OAAO;AACjD,IAAA,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC;AAAE,QAAA,OAAO,OAAO;AACjD,IAAA,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC;AAAE,QAAA,OAAO,OAAO;AACjD,IAAA,IACE,QAAQ,CAAC,UAAU,CAAC,iBAAiB,CAAC;AACtC,QAAA,QAAQ,CAAC,UAAU,CAAC,oBAAoB,CAAC;AACzC,QAAA,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,EAC5B;AACA,QAAA,OAAO,UAAU;IACnB;AACA,IAAA,OAAO,OAAO;AAChB;AAEA;;AAEG;AACH,SAAS,UAAU,GAAA;AACjB,IAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EAAA,QAAA,EAEtBA,cAAM,CAAC,EAAC,mHAAmH,EAAA,CAAG,EAAA,CAC1H;AAEV;AAEA;;AAEG;AACH,SAAS,QAAQ,GAAA;IACf,QACEA,GAAA,CAAA,KAAA,EAAA,EACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,cAAc,EAAA,QAAA,EAEnBA,GAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,uCAAuC,EAAA,CAAG,EAAA,CAC9C;AAEV;AAEA;;AAEG;AACH,SAAS,QAAQ,GAAA;IACf,QACED,cACE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EAAA,QAAA,EAAA,CAEtBC,GAAA,CAAA,MAAA,EAAA,EAAM,CAAC,EAAC,4DAA4D,EAAA,CAAG,EACvEA,GAAA,CAAA,UAAA,EAAA,EAAU,MAAM,EAAC,gBAAgB,EAAA,CAAG,CAAA,EAAA,CAChC;AAEV;;;;"}
|
|
1
|
+
{"version":3,"file":"ChatInput.js","sources":["../../../../src/components/ChatDrawer/ChatInput.tsx"],"sourcesContent":["import React, { useState, useRef, useCallback } from 'react';\nimport type { ChatInputProps } from './ChatDrawer.types';\nimport type { ChatFile } from '../../api/types';\n\nconst FILE_TYPE_ACCEPT: Record<string, string[]> = {\n images: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],\n documents: [\n 'application/pdf',\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'text/plain',\n 'text/csv',\n ],\n audio: ['audio/mpeg', 'audio/wav', 'audio/ogg'],\n video: ['video/mp4', 'video/webm', 'video/ogg'],\n};\n\n/**\n * Chat input component with file upload support\n */\nexport function ChatInput({\n onSend,\n disabled = false,\n placeholder = 'Type a message...',\n enableFileUploads = false,\n allowedFileTypes = { images: true, documents: true },\n maxFileSize = 10 * 1024 * 1024, // 10MB\n sendButtonContent,\n}: ChatInputProps): JSX.Element {\n const [message, setMessage] = useState('');\n const [files, setFiles] = useState<File[]>([]);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Calculate accepted file types\n const acceptedTypes = Object.entries(allowedFileTypes)\n .filter(([, enabled]) => enabled)\n .flatMap(([type]) => FILE_TYPE_ACCEPT[type] || [])\n .join(',');\n\n // Auto-resize textarea\n const handleInput = useCallback(() => {\n const textarea = textareaRef.current;\n if (textarea) {\n textarea.style.height = 'auto';\n textarea.style.height = `${Math.min(textarea.scrollHeight, 120)}px`;\n }\n }, []);\n\n // Handle send\n const handleSend = useCallback(() => {\n const trimmedMessage = message.trim();\n if (!trimmedMessage && files.length === 0) return;\n\n // Convert File objects to ChatFile format\n const chatFiles: ChatFile[] = files.map((file) => ({\n name: file.name,\n fileType: getFileType(file.type),\n }));\n\n onSend(trimmedMessage, chatFiles.length > 0 ? chatFiles : undefined);\n setMessage('');\n setFiles([]);\n\n // Reset textarea height\n if (textareaRef.current) {\n textareaRef.current.style.height = 'auto';\n }\n }, [message, files, onSend]);\n\n // Handle key press\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n },\n [handleSend]\n );\n\n // Handle file selection\n const handleFileSelect = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const selectedFiles = Array.from(e.target.files || []);\n\n // Filter valid files\n const validFiles = selectedFiles.filter((file) => {\n if (file.size > maxFileSize) {\n console.warn(`File ${file.name} exceeds maximum size`);\n return false;\n }\n return true;\n });\n\n setFiles((prev) => [...prev, ...validFiles]);\n\n // Reset input\n if (fileInputRef.current) {\n fileInputRef.current.value = '';\n }\n },\n [maxFileSize]\n );\n\n // Remove file\n const removeFile = useCallback((index: number) => {\n setFiles((prev) => prev.filter((_, i) => i !== index));\n }, []);\n\n return (\n <div className=\"devic-input-area\">\n {files.length > 0 && (\n <div className=\"devic-file-preview\">\n {files.map((file, idx) => (\n <div key={idx} className=\"devic-file-preview-item\">\n <FileIcon />\n <span>{file.name}</span>\n <button\n className=\"devic-file-remove\"\n onClick={() => removeFile(idx)}\n type=\"button\"\n >\n ×\n </button>\n </div>\n ))}\n </div>\n )}\n\n <div className=\"devic-input-wrapper\">\n {enableFileUploads && (\n <>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={acceptedTypes}\n multiple\n onChange={handleFileSelect}\n style={{ display: 'none' }}\n />\n <button\n className=\"devic-input-btn\"\n onClick={() => fileInputRef.current?.click()}\n disabled={disabled}\n type=\"button\"\n title=\"Attach file\"\n >\n <AttachIcon />\n </button>\n </>\n )}\n\n <textarea\n ref={textareaRef}\n className=\"devic-input\"\n value={message}\n onChange={(e) => {\n setMessage(e.target.value);\n handleInput();\n }}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n rows={1}\n />\n\n {sendButtonContent ? (\n <div className=\"devic-send-btn-wrapper\">\n <div className=\"devic-send-btn-custom\" aria-hidden=\"true\">\n {sendButtonContent}\n </div>\n <button\n className=\"devic-send-btn-overlay\"\n onClick={handleSend}\n disabled={disabled || (!message.trim() && files.length === 0)}\n type=\"button\"\n title=\"Send message\"\n />\n </div>\n ) : (\n <button\n className=\"devic-input-btn devic-send-btn\"\n onClick={handleSend}\n disabled={disabled || (!message.trim() && files.length === 0)}\n type=\"button\"\n title=\"Send message\"\n >\n <SendIcon />\n </button>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Get file type category from MIME type\n */\nfunction getFileType(\n mimeType: string\n): 'image' | 'document' | 'audio' | 'video' | 'other' {\n if (mimeType.startsWith('image/')) return 'image';\n if (mimeType.startsWith('audio/')) return 'audio';\n if (mimeType.startsWith('video/')) return 'video';\n if (\n mimeType.startsWith('application/pdf') ||\n mimeType.startsWith('application/msword') ||\n mimeType.startsWith('text/')\n ) {\n return 'document';\n }\n return 'other';\n}\n\n/**\n * Attach icon\n */\nfunction AttachIcon(): JSX.Element {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\" />\n </svg>\n );\n}\n\n/**\n * Send icon\n */\nfunction SendIcon(): JSX.Element {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" />\n </svg>\n );\n}\n\n/**\n * File icon\n */\nfunction FileIcon(): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <polyline points=\"14,2 14,8 20,8\" />\n </svg>\n );\n}\n"],"names":["_jsxs","_jsx","_Fragment"],"mappings":"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"}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import type { ChatMessagesProps } from './ChatDrawer.types';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
5
|
-
export declare function ChatMessages({ messages, isLoading, welcomeMessage, suggestedMessages, onSuggestedClick, showToolTimeline, }: ChatMessagesProps): JSX.Element;
|
|
2
|
+
import '../Feedback/Feedback.css';
|
|
3
|
+
export declare function ChatMessages({ messages, allMessages, isLoading, welcomeMessage, suggestedMessages, onSuggestedClick, toolRenderers, toolIcons, loadingIndicator, showFeedback, feedbackMap, onFeedback, }: ChatMessagesProps): JSX.Element;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { useRef, useEffect } from 'react';
|
|
3
|
-
import
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import React, { useRef, useEffect, useState } from 'react';
|
|
3
|
+
import Markdown from 'markdown-to-jsx';
|
|
4
|
+
import { MessageActions } from '../Feedback/MessageActions.js';
|
|
4
5
|
|
|
6
|
+
console.log('[devic-ui] ChatMessages: DEV-BUILD-005');
|
|
5
7
|
/**
|
|
6
8
|
* Format timestamp to readable time
|
|
7
9
|
*/
|
|
@@ -11,41 +13,147 @@ function formatTime(timestamp) {
|
|
|
11
13
|
minute: '2-digit',
|
|
12
14
|
});
|
|
13
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Groups consecutive tool-call assistant messages (no text content)
|
|
18
|
+
* into { toolMessages, isActive } groups, interleaved with regular messages.
|
|
19
|
+
*/
|
|
20
|
+
function groupMessages(messages, isLoading) {
|
|
21
|
+
const result = [];
|
|
22
|
+
let currentToolGroup = [];
|
|
23
|
+
const flushToolGroup = (isActive) => {
|
|
24
|
+
if (currentToolGroup.length > 0) {
|
|
25
|
+
result.push({ type: 'toolGroup', toolMessages: [...currentToolGroup], isActive });
|
|
26
|
+
currentToolGroup = [];
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
for (let i = 0; i < messages.length; i++) {
|
|
30
|
+
const msg = messages[i];
|
|
31
|
+
// Skip developer and tool response messages
|
|
32
|
+
if (msg.role === 'developer' || msg.role === 'tool') {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const hasToolCalls = msg.tool_calls && msg.tool_calls.length > 0;
|
|
36
|
+
const hasText = !!msg.content?.message;
|
|
37
|
+
const hasFiles = msg.content?.files && msg.content.files.length > 0;
|
|
38
|
+
if (hasToolCalls) {
|
|
39
|
+
// If message has both text and tool_calls, show text first
|
|
40
|
+
if (hasText || hasFiles) {
|
|
41
|
+
// Flush any prior tool group before inserting the text message
|
|
42
|
+
const remainingMeaningful = messages.slice(i + 1).some((m) => (m.role === 'assistant' && m.content?.message) || m.role === 'user');
|
|
43
|
+
flushToolGroup(isLoading && !remainingMeaningful);
|
|
44
|
+
result.push({ type: 'message', message: msg });
|
|
45
|
+
}
|
|
46
|
+
// Always accumulate the tool call
|
|
47
|
+
currentToolGroup.push(msg);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// Regular message → flush any accumulated tool group first
|
|
51
|
+
const remainingMeaningful = messages.slice(i).some((m) => (m.role === 'assistant' && m.content?.message) || m.role === 'user');
|
|
52
|
+
flushToolGroup(isLoading && !remainingMeaningful);
|
|
53
|
+
if (hasText || hasFiles) {
|
|
54
|
+
result.push({ type: 'message', message: msg });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Flush remaining tool group (is active if still loading)
|
|
59
|
+
flushToolGroup(isLoading);
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Collapsible tool actions group
|
|
64
|
+
*/
|
|
65
|
+
function ToolGroup({ toolMessages, isActive, allMessages, toolRenderers, toolIcons, }) {
|
|
66
|
+
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
67
|
+
const shouldCollapse = toolMessages.length > 3 && !isActive;
|
|
68
|
+
// Auto-collapse when transitioning from active to completed
|
|
69
|
+
const wasActiveRef = useRef(isActive);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (wasActiveRef.current && !isActive && toolMessages.length > 3) {
|
|
72
|
+
setIsCollapsed(true);
|
|
73
|
+
}
|
|
74
|
+
wasActiveRef.current = isActive;
|
|
75
|
+
}, [isActive, toolMessages.length]);
|
|
76
|
+
const lastIndex = toolMessages.length - 1;
|
|
77
|
+
const renderToolItem = (msg, opts) => {
|
|
78
|
+
const toolCall = msg.tool_calls?.[0];
|
|
79
|
+
const toolName = toolCall?.function?.name;
|
|
80
|
+
const summaryText = msg.summary || toolName || (opts.active ? 'Processing...' : 'Completed');
|
|
81
|
+
// Custom renderer for completed tools
|
|
82
|
+
if (!opts.active && toolName && toolRenderers?.[toolName] && allMessages) {
|
|
83
|
+
const toolResponse = allMessages.find((m) => m.role === 'tool' && m.tool_call_id === toolCall.id);
|
|
84
|
+
let input = {};
|
|
85
|
+
try {
|
|
86
|
+
input = JSON.parse(toolCall.function.arguments);
|
|
87
|
+
}
|
|
88
|
+
catch { }
|
|
89
|
+
const output = toolResponse?.content?.data ?? toolResponse?.content?.message;
|
|
90
|
+
return toolRenderers[toolName](input, output);
|
|
91
|
+
}
|
|
92
|
+
const icon = opts.showSpinner
|
|
93
|
+
? jsx(SpinnerIcon, {})
|
|
94
|
+
: (toolName && toolIcons?.[toolName]) || jsx(ToolDoneIcon, {});
|
|
95
|
+
return (jsxs(Fragment, { children: [jsx("span", { className: "devic-tool-activity-icon", children: icon }), jsx("span", { className: `devic-tool-activity-text ${opts.active ? 'devic-glow-text' : ''}`, children: summaryText })] }));
|
|
96
|
+
};
|
|
97
|
+
// If active, show all items; last one gets the glow treatment
|
|
98
|
+
if (isActive) {
|
|
99
|
+
return (jsx("div", { className: "devic-tool-group", children: toolMessages.map((msg, idx) => {
|
|
100
|
+
const isLast = idx === lastIndex;
|
|
101
|
+
return (jsx("div", { className: `devic-tool-activity ${isLast ? 'devic-tool-activity--active' : ''}`, children: renderToolItem(msg, { active: isLast, showSpinner: isLast }) }, msg.uid));
|
|
102
|
+
}) }));
|
|
103
|
+
}
|
|
104
|
+
// Completed: collapse if > 3 actions
|
|
105
|
+
if (shouldCollapse && isCollapsed) {
|
|
106
|
+
return (jsx("div", { className: "devic-tool-group", children: jsxs("button", { className: "devic-tool-collapse-btn", onClick: () => setIsCollapsed(false), type: "button", children: [jsx(ToolDoneIcon, {}), jsxs("span", { children: [toolMessages.length, " actions"] }), jsx(ChevronDownIcon, {})] }) }));
|
|
107
|
+
}
|
|
108
|
+
return (jsxs("div", { className: "devic-tool-group", children: [shouldCollapse && (jsxs("button", { className: "devic-tool-collapse-btn", onClick: () => setIsCollapsed(true), type: "button", children: [jsxs("span", { children: [toolMessages.length, " actions"] }), jsx(ChevronUpIcon, {})] })), jsx("div", { className: "devic-tool-group-items", "data-expanded": "true", children: toolMessages.map((msg) => (jsx("div", { className: "devic-tool-activity", children: renderToolItem(msg, {}) }, msg.uid))) })] }));
|
|
109
|
+
}
|
|
14
110
|
/**
|
|
15
111
|
* Messages list component
|
|
16
112
|
*/
|
|
17
|
-
|
|
113
|
+
const markdownOverrides = {
|
|
114
|
+
table: {
|
|
115
|
+
component: ({ children, ...props }) => React.createElement('div', { className: 'markdown-table' }, React.createElement('table', props, children)),
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
function ChatMessages({ messages, allMessages, isLoading, welcomeMessage, suggestedMessages, onSuggestedClick, toolRenderers, toolIcons, loadingIndicator, showFeedback = true, feedbackMap, onFeedback, }) {
|
|
18
119
|
const containerRef = useRef(null);
|
|
19
|
-
const
|
|
120
|
+
const prevLengthRef = useRef(messages.length);
|
|
20
121
|
// Auto-scroll to bottom when new messages arrive
|
|
21
122
|
useEffect(() => {
|
|
22
|
-
if (
|
|
123
|
+
if (containerRef.current) {
|
|
23
124
|
containerRef.current.scrollTop = containerRef.current.scrollHeight;
|
|
24
125
|
}
|
|
25
|
-
|
|
26
|
-
}, [messages.length]);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
status: 'completed',
|
|
35
|
-
timestamp: m.timestamp,
|
|
36
|
-
})))
|
|
37
|
-
: [];
|
|
38
|
-
return (jsxs("div", { className: "devic-messages-container", ref: containerRef, children: [messages.length === 0 && (welcomeMessage || suggestedMessages?.length) && (jsxs("div", { className: "devic-welcome", children: [welcomeMessage && (jsx("p", { className: "devic-welcome-text", children: welcomeMessage })), suggestedMessages && suggestedMessages.length > 0 && (jsx("div", { className: "devic-suggested-messages", children: suggestedMessages.map((msg, idx) => (jsx("button", { className: "devic-suggested-btn", onClick: () => onSuggestedClick?.(msg), children: msg }, idx))) }))] })), messages.map((message) => {
|
|
39
|
-
// Skip tool messages in main display (shown in timeline)
|
|
40
|
-
if (message.role === 'tool' && showToolTimeline) {
|
|
41
|
-
return null;
|
|
126
|
+
prevLengthRef.current = messages.length;
|
|
127
|
+
}, [messages.length, isLoading]);
|
|
128
|
+
const grouped = groupMessages(messages, isLoading);
|
|
129
|
+
// Show loading dots only if there's no active tool group at the end
|
|
130
|
+
const lastGroup = grouped[grouped.length - 1];
|
|
131
|
+
const showLoadingDots = isLoading && !(lastGroup?.type === 'toolGroup' && lastGroup.isActive);
|
|
132
|
+
return (jsxs("div", { className: "devic-messages-container", ref: containerRef, children: [messages.length === 0 && !isLoading && (welcomeMessage || suggestedMessages?.length) && (jsxs("div", { className: "devic-welcome", children: [welcomeMessage && (jsx("p", { className: "devic-welcome-text", children: welcomeMessage })), suggestedMessages && suggestedMessages.length > 0 && (jsx("div", { className: "devic-suggested-messages", children: suggestedMessages.map((msg, idx) => (jsx("button", { className: "devic-suggested-btn", onClick: () => onSuggestedClick?.(msg), children: msg }, idx))) }))] })), grouped.map((item) => {
|
|
133
|
+
if (item.type === 'toolGroup') {
|
|
134
|
+
return (jsx(ToolGroup, { toolMessages: item.toolMessages, isActive: item.isActive, allMessages: allMessages, toolRenderers: toolRenderers, toolIcons: toolIcons }, `tg-${item.toolMessages[0].uid}`));
|
|
42
135
|
}
|
|
43
|
-
|
|
44
|
-
|
|
136
|
+
const message = item.message;
|
|
137
|
+
const messageText = message.content?.message;
|
|
138
|
+
const hasFiles = message.content?.files && message.content.files.length > 0;
|
|
139
|
+
const isAssistant = message.role === 'assistant';
|
|
140
|
+
const currentFeedback = feedbackMap?.get(message.uid) || 'none';
|
|
141
|
+
return (jsxs("div", { className: "devic-message", "data-role": message.role, children: [jsxs("div", { className: "devic-message-bubble", children: [messageText && isAssistant ? (jsx(Markdown, { options: { overrides: markdownOverrides }, children: messageText })) : (messageText), hasFiles && (jsx("div", { className: "devic-message-files", children: message.content.files.map((file, fileIdx) => (jsxs("div", { className: "devic-message-file", children: [jsx(FileIcon, {}), jsx("span", { children: file.name })] }, fileIdx))) }))] }), jsxs("div", { className: "devic-message-footer", children: [jsx("span", { className: "devic-message-time", children: formatTime(message.timestamp) }), isAssistant && showFeedback && onFeedback && (jsx(MessageActions, { messageId: message.uid, messageContent: messageText, currentFeedback: currentFeedback, onFeedback: onFeedback, showCopy: true, showFeedback: true }))] })] }, message.uid));
|
|
142
|
+
}), showLoadingDots && (loadingIndicator ? (jsx("div", { className: "devic-loading", children: loadingIndicator })) : (jsxs("div", { className: "devic-loading", children: [jsx("span", { className: "devic-loading-dot" }), jsx("span", { className: "devic-loading-dot" }), jsx("span", { className: "devic-loading-dot" })] })))] }));
|
|
143
|
+
}
|
|
144
|
+
/* ── Icons ── */
|
|
145
|
+
function SpinnerIcon() {
|
|
146
|
+
return (jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", className: "devic-spinner", children: jsx("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }));
|
|
147
|
+
}
|
|
148
|
+
function ToolDoneIcon() {
|
|
149
|
+
return (jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "20,6 9,17 4,12" }) }));
|
|
150
|
+
}
|
|
151
|
+
function ChevronDownIcon() {
|
|
152
|
+
return (jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "6,9 12,15 18,9" }) }));
|
|
153
|
+
}
|
|
154
|
+
function ChevronUpIcon() {
|
|
155
|
+
return (jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "6,15 12,9 18,15" }) }));
|
|
45
156
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Simple file icon
|
|
48
|
-
*/
|
|
49
157
|
function FileIcon() {
|
|
50
158
|
return (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), jsx("polyline", { points: "14,2 14,8 20,8" })] }));
|
|
51
159
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatMessages.js","sources":["../../../../src/components/ChatDrawer/ChatMessages.tsx"],"sourcesContent":["import React, { useEffect, useRef } from 'react';\nimport type { ChatMessagesProps } from './ChatDrawer.types';\nimport { ToolTimeline } from './ToolTimeline';\n\n/**\n * Format timestamp to readable time\n */\nfunction formatTime(timestamp: number): string {\n return new Date(timestamp).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\n/**\n * Messages list component\n */\nexport function ChatMessages({\n messages,\n isLoading,\n welcomeMessage,\n suggestedMessages,\n onSuggestedClick,\n showToolTimeline = true,\n}: ChatMessagesProps): JSX.Element {\n const containerRef = useRef<HTMLDivElement>(null);\n const lastMessageCountRef = useRef(messages.length);\n\n // Auto-scroll to bottom when new messages arrive\n useEffect(() => {\n if (messages.length > lastMessageCountRef.current && containerRef.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight;\n }\n lastMessageCountRef.current = messages.length;\n }, [messages.length]);\n\n // Extract tool calls for timeline\n const toolCalls = showToolTimeline\n ? messages\n .filter((m) => m.tool_calls?.length)\n .flatMap((m) =>\n m.tool_calls!.map((tc) => ({\n id: tc.id,\n name: tc.function.name,\n status: 'completed' as const,\n timestamp: m.timestamp,\n }))\n )\n : [];\n\n return (\n <div className=\"devic-messages-container\" ref={containerRef}>\n {messages.length === 0 && (welcomeMessage || suggestedMessages?.length) && (\n <div className=\"devic-welcome\">\n {welcomeMessage && (\n <p className=\"devic-welcome-text\">{welcomeMessage}</p>\n )}\n {suggestedMessages && suggestedMessages.length > 0 && (\n <div className=\"devic-suggested-messages\">\n {suggestedMessages.map((msg, idx) => (\n <button\n key={idx}\n className=\"devic-suggested-btn\"\n onClick={() => onSuggestedClick?.(msg)}\n >\n {msg}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n\n {messages.map((message) => {\n // Skip tool messages in main display (shown in timeline)\n if (message.role === 'tool' && showToolTimeline) {\n return null;\n }\n\n return (\n <div\n key={message.uid}\n className=\"devic-message\"\n data-role={message.role}\n >\n <div className=\"devic-message-bubble\">\n {message.content.message}\n {message.content.files && message.content.files.length > 0 && (\n <div className=\"devic-message-files\">\n {message.content.files.map((file, idx) => (\n <div key={idx} className=\"devic-message-file\">\n <FileIcon />\n <span>{file.name}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n <span className=\"devic-message-time\">\n {formatTime(message.timestamp)}\n </span>\n </div>\n );\n })}\n\n {showToolTimeline && toolCalls.length > 0 && (\n <ToolTimeline toolCalls={toolCalls} />\n )}\n\n {isLoading && (\n <div className=\"devic-loading\">\n <span className=\"devic-loading-dot\"></span>\n <span className=\"devic-loading-dot\"></span>\n <span className=\"devic-loading-dot\"></span>\n </div>\n )}\n </div>\n );\n}\n\n/**\n * Simple file icon\n */\nfunction FileIcon(): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <polyline points=\"14,2 14,8 20,8\" />\n </svg>\n );\n}\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatMessages.js","sources":["../../../../src/components/ChatDrawer/ChatMessages.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef } from 'react';\nimport Markdown from 'markdown-to-jsx';\nimport { MessageActions } from '../Feedback';\nimport type { ChatMessagesProps } from './ChatDrawer.types';\nimport type { ChatMessage } from '../../api/types';\nimport type { FeedbackState } from '../Feedback';\nimport '../Feedback/Feedback.css';\n\nconsole.log('[devic-ui] ChatMessages: DEV-BUILD-005');\n\n/**\n * Format timestamp to readable time\n */\nfunction formatTime(timestamp: number): string {\n return new Date(timestamp).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\n/**\n * Groups consecutive tool-call assistant messages (no text content)\n * into { toolMessages, isActive } groups, interleaved with regular messages.\n */\nfunction groupMessages(\n messages: ChatMessage[],\n isLoading: boolean\n): Array<\n | { type: 'message'; message: ChatMessage }\n | { type: 'toolGroup'; toolMessages: ChatMessage[]; isActive: boolean }\n> {\n const result: Array<\n | { type: 'message'; message: ChatMessage }\n | { type: 'toolGroup'; toolMessages: ChatMessage[]; isActive: boolean }\n > = [];\n\n let currentToolGroup: ChatMessage[] = [];\n\n const flushToolGroup = (isActive: boolean) => {\n if (currentToolGroup.length > 0) {\n result.push({ type: 'toolGroup', toolMessages: [...currentToolGroup], isActive });\n currentToolGroup = [];\n }\n };\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n\n // Skip developer and tool response messages\n if (msg.role === 'developer' || msg.role === 'tool') {\n continue;\n }\n\n const hasToolCalls = msg.tool_calls && msg.tool_calls.length > 0;\n const hasText = !!msg.content?.message;\n const hasFiles = msg.content?.files && msg.content.files.length > 0;\n\n if (hasToolCalls) {\n // If message has both text and tool_calls, show text first\n if (hasText || hasFiles) {\n // Flush any prior tool group before inserting the text message\n const remainingMeaningful = messages.slice(i + 1).some(\n (m) => (m.role === 'assistant' && m.content?.message) || m.role === 'user'\n );\n flushToolGroup(isLoading && !remainingMeaningful);\n result.push({ type: 'message', message: msg });\n }\n // Always accumulate the tool call\n currentToolGroup.push(msg);\n } else {\n // Regular message → flush any accumulated tool group first\n const remainingMeaningful = messages.slice(i).some(\n (m) => (m.role === 'assistant' && m.content?.message) || m.role === 'user'\n );\n flushToolGroup(isLoading && !remainingMeaningful);\n\n if (hasText || hasFiles) {\n result.push({ type: 'message', message: msg });\n }\n }\n }\n\n // Flush remaining tool group (is active if still loading)\n flushToolGroup(isLoading);\n\n return result;\n}\n\n/**\n * Collapsible tool actions group\n */\nfunction ToolGroup({\n toolMessages,\n isActive,\n allMessages,\n toolRenderers,\n toolIcons,\n}: {\n toolMessages: ChatMessage[];\n isActive: boolean;\n allMessages?: ChatMessage[];\n toolRenderers?: Record<string, (input: any, output: any) => React.ReactNode>;\n toolIcons?: Record<string, React.ReactNode>;\n}): JSX.Element {\n const [isCollapsed, setIsCollapsed] = useState(false);\n const shouldCollapse = toolMessages.length > 3 && !isActive;\n\n // Auto-collapse when transitioning from active to completed\n const wasActiveRef = useRef(isActive);\n useEffect(() => {\n if (wasActiveRef.current && !isActive && toolMessages.length > 3) {\n setIsCollapsed(true);\n }\n wasActiveRef.current = isActive;\n }, [isActive, toolMessages.length]);\n\n const lastIndex = toolMessages.length - 1;\n\n const renderToolItem = (msg: ChatMessage, opts: { active?: boolean; showSpinner?: boolean }) => {\n const toolCall = msg.tool_calls?.[0];\n const toolName = toolCall?.function?.name;\n const summaryText = msg.summary || toolName || (opts.active ? 'Processing...' : 'Completed');\n\n // Custom renderer for completed tools\n if (!opts.active && toolName && toolRenderers?.[toolName] && allMessages) {\n const toolResponse = allMessages.find(\n (m) => m.role === 'tool' && m.tool_call_id === toolCall!.id\n );\n let input: any = {};\n try { input = JSON.parse(toolCall!.function.arguments); } catch {}\n const output = toolResponse?.content?.data ?? toolResponse?.content?.message;\n return toolRenderers[toolName](input, output);\n }\n\n const icon = opts.showSpinner\n ? <SpinnerIcon />\n : (toolName && toolIcons?.[toolName]) || <ToolDoneIcon />;\n\n return (\n <>\n <span className=\"devic-tool-activity-icon\">{icon}</span>\n <span className={`devic-tool-activity-text ${opts.active ? 'devic-glow-text' : ''}`}>\n {summaryText}\n </span>\n </>\n );\n };\n\n // If active, show all items; last one gets the glow treatment\n if (isActive) {\n return (\n <div className=\"devic-tool-group\">\n {toolMessages.map((msg, idx) => {\n const isLast = idx === lastIndex;\n return (\n <div\n key={msg.uid}\n className={`devic-tool-activity ${isLast ? 'devic-tool-activity--active' : ''}`}\n >\n {renderToolItem(msg, { active: isLast, showSpinner: isLast })}\n </div>\n );\n })}\n </div>\n );\n }\n\n // Completed: collapse if > 3 actions\n if (shouldCollapse && isCollapsed) {\n return (\n <div className=\"devic-tool-group\">\n <button\n className=\"devic-tool-collapse-btn\"\n onClick={() => setIsCollapsed(false)}\n type=\"button\"\n >\n <ToolDoneIcon />\n <span>{toolMessages.length} actions</span>\n <ChevronDownIcon />\n </button>\n </div>\n );\n }\n\n return (\n <div className=\"devic-tool-group\">\n {shouldCollapse && (\n <button\n className=\"devic-tool-collapse-btn\"\n onClick={() => setIsCollapsed(true)}\n type=\"button\"\n >\n <span>{toolMessages.length} actions</span>\n <ChevronUpIcon />\n </button>\n )}\n <div className=\"devic-tool-group-items\" data-expanded=\"true\">\n {toolMessages.map((msg) => (\n <div key={msg.uid} className=\"devic-tool-activity\">\n {renderToolItem(msg, {})}\n </div>\n ))}\n </div>\n </div>\n );\n}\n\n/**\n * Messages list component\n */\nconst markdownOverrides = {\n table: {\n component: ({ children, ...props }: any) =>\n React.createElement('div', { className: 'markdown-table' },\n React.createElement('table', props, children)\n ),\n },\n};\n\nexport function ChatMessages({\n messages,\n allMessages,\n isLoading,\n welcomeMessage,\n suggestedMessages,\n onSuggestedClick,\n toolRenderers,\n toolIcons,\n loadingIndicator,\n showFeedback = true,\n feedbackMap,\n onFeedback,\n}: ChatMessagesProps): JSX.Element {\n const containerRef = useRef<HTMLDivElement>(null);\n const prevLengthRef = useRef(messages.length);\n\n // Auto-scroll to bottom when new messages arrive\n useEffect(() => {\n if (containerRef.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight;\n }\n prevLengthRef.current = messages.length;\n }, [messages.length, isLoading]);\n\n const grouped = groupMessages(messages, isLoading);\n\n // Show loading dots only if there's no active tool group at the end\n const lastGroup = grouped[grouped.length - 1];\n const showLoadingDots = isLoading && !(lastGroup?.type === 'toolGroup' && lastGroup.isActive);\n\n return (\n <div className=\"devic-messages-container\" ref={containerRef}>\n {messages.length === 0 && !isLoading && (welcomeMessage || suggestedMessages?.length) && (\n <div className=\"devic-welcome\">\n {welcomeMessage && (\n <p className=\"devic-welcome-text\">{welcomeMessage}</p>\n )}\n {suggestedMessages && suggestedMessages.length > 0 && (\n <div className=\"devic-suggested-messages\">\n {suggestedMessages.map((msg, idx) => (\n <button\n key={idx}\n className=\"devic-suggested-btn\"\n onClick={() => onSuggestedClick?.(msg)}\n >\n {msg}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n\n {grouped.map((item) => {\n if (item.type === 'toolGroup') {\n return (\n <ToolGroup\n key={`tg-${item.toolMessages[0].uid}`}\n toolMessages={item.toolMessages}\n isActive={item.isActive}\n allMessages={allMessages}\n toolRenderers={toolRenderers}\n toolIcons={toolIcons}\n />\n );\n }\n\n const message = item.message;\n const messageText = message.content?.message;\n const hasFiles = message.content?.files && message.content.files.length > 0;\n const isAssistant = message.role === 'assistant';\n const currentFeedback = feedbackMap?.get(message.uid) || 'none';\n\n return (\n <div\n key={message.uid}\n className=\"devic-message\"\n data-role={message.role}\n >\n <div className=\"devic-message-bubble\">\n {messageText && isAssistant ? (\n <Markdown options={{ overrides: markdownOverrides }}>{messageText}</Markdown>\n ) : (\n messageText\n )}\n {hasFiles && (\n <div className=\"devic-message-files\">\n {message.content!.files!.map((file, fileIdx) => (\n <div key={fileIdx} className=\"devic-message-file\">\n <FileIcon />\n <span>{file.name}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n <div className=\"devic-message-footer\">\n <span className=\"devic-message-time\">\n {formatTime(message.timestamp)}\n </span>\n {isAssistant && showFeedback && onFeedback && (\n <MessageActions\n messageId={message.uid}\n messageContent={messageText}\n currentFeedback={currentFeedback as FeedbackState}\n onFeedback={onFeedback}\n showCopy={true}\n showFeedback={true}\n />\n )}\n </div>\n </div>\n );\n })}\n\n {showLoadingDots && (\n loadingIndicator ? (\n <div className=\"devic-loading\">{loadingIndicator}</div>\n ) : (\n <div className=\"devic-loading\">\n <span className=\"devic-loading-dot\"></span>\n <span className=\"devic-loading-dot\"></span>\n <span className=\"devic-loading-dot\"></span>\n </div>\n )\n )}\n </div>\n );\n}\n\n/* ── Icons ── */\n\nfunction SpinnerIcon(): JSX.Element {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n className=\"devic-spinner\"\n >\n <path d=\"M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83\" />\n </svg>\n );\n}\n\nfunction ToolDoneIcon(): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <polyline points=\"20,6 9,17 4,12\" />\n </svg>\n );\n}\n\nfunction ChevronDownIcon(): JSX.Element {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"6,9 12,15 18,9\" />\n </svg>\n );\n}\n\nfunction ChevronUpIcon(): JSX.Element {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"6,15 12,9 18,15\" />\n </svg>\n );\n}\n\nfunction FileIcon(): JSX.Element {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <polyline points=\"14,2 14,8 20,8\" />\n </svg>\n );\n}\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { ConversationSelectorProps } from './ChatDrawer.types';
|
|
2
|
+
export declare function ConversationSelector({ assistantId, currentChatUid, onSelect, onNewChat, apiKey: propsApiKey, baseUrl: propsBaseUrl, tenantId: propsTenantId, }: ConversationSelectorProps): JSX.Element;
|