@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.1.3 → 0.1.4

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 (91) hide show
  1. package/dist/ChatBot.d.ts +5 -0
  2. package/dist/ChatBot.d.ts.map +1 -0
  3. package/dist/ChatBot.js +35 -0
  4. package/dist/assets/images/adiSideItalicDataUri.d.ts +2 -0
  5. package/dist/assets/images/adiSideItalicDataUri.d.ts.map +1 -0
  6. package/dist/assets/images/adiSideItalicDataUri.js +1 -0
  7. package/dist/context/ChatBotContext.d.ts +5 -0
  8. package/dist/context/ChatBotContext.d.ts.map +1 -0
  9. package/dist/context/ChatBotContext.js +2908 -0
  10. package/dist/index.d.ts +5 -13
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -11
  13. package/dist/legacy/chatbot/context/ChatBotContext.d.ts.map +1 -1
  14. package/dist/legacy/chatbot/context/ChatBotContext.js +14 -0
  15. package/dist/types/ChatBotTypes.d.ts +166 -0
  16. package/dist/types/ChatBotTypes.d.ts.map +1 -0
  17. package/dist/types/ChatBotTypes.js +1 -0
  18. package/dist/ui/BerifyMeModal.d.ts +17 -0
  19. package/dist/ui/BerifyMeModal.d.ts.map +1 -0
  20. package/dist/ui/BerifyMeModal.js +110 -0
  21. package/dist/ui/ChatBotHeader.d.ts +15 -0
  22. package/dist/ui/ChatBotHeader.d.ts.map +1 -0
  23. package/dist/ui/ChatBotHeader.js +62 -0
  24. package/dist/ui/ChatBotUI.d.ts +3 -0
  25. package/dist/ui/ChatBotUI.d.ts.map +1 -0
  26. package/dist/ui/ChatBotUI.js +557 -0
  27. package/dist/ui/MessageInput.d.ts +3 -0
  28. package/dist/ui/MessageInput.d.ts.map +1 -0
  29. package/dist/ui/MessageInput.js +321 -0
  30. package/dist/ui/MessageList.d.ts +4 -0
  31. package/dist/ui/MessageList.d.ts.map +1 -0
  32. package/dist/ui/MessageList.js +455 -0
  33. package/dist/ui/ModelSelector.d.ts +4 -0
  34. package/dist/ui/ModelSelector.d.ts.map +1 -0
  35. package/dist/ui/ModelSelector.js +122 -0
  36. package/dist/ui/NotificationModal.d.ts +15 -0
  37. package/dist/ui/NotificationModal.d.ts.map +1 -0
  38. package/dist/ui/NotificationModal.js +53 -0
  39. package/dist/ui/PermissionForm.d.ts +8 -0
  40. package/dist/ui/PermissionForm.d.ts.map +1 -0
  41. package/dist/ui/PermissionForm.js +465 -0
  42. package/dist/ui/PresetMessages.d.ts +4 -0
  43. package/dist/ui/PresetMessages.d.ts.map +1 -0
  44. package/dist/ui/PresetMessages.js +33 -0
  45. package/dist/ui/VoiceModePanel.d.ts +3 -0
  46. package/dist/ui/VoiceModePanel.d.ts.map +1 -0
  47. package/dist/ui/VoiceModePanel.js +95 -0
  48. package/dist/ui/chatBotHeaderParts.d.ts +15 -0
  49. package/dist/ui/chatBotHeaderParts.d.ts.map +1 -0
  50. package/dist/ui/chatBotHeaderParts.js +50 -0
  51. package/dist/ui/index.d.ts +9 -0
  52. package/dist/ui/index.d.ts.map +1 -0
  53. package/dist/ui/index.js +8 -0
  54. package/dist/ui/styles/isolatedStyles.d.ts +73 -0
  55. package/dist/ui/styles/isolatedStyles.d.ts.map +1 -0
  56. package/dist/ui/styles/isolatedStyles.js +985 -0
  57. package/package.json +2 -2
  58. package/src/{legacy/chatbot/context → context}/ChatBotContext.tsx +0 -1
  59. package/src/index.ts +17 -40
  60. package/src/{legacy/chatbot/ui → ui}/ModelSelector.tsx +1 -1
  61. package/src/{legacy/chatbot/ui → ui}/VoiceModePanel.tsx +1 -1
  62. package/src/ui/index.ts +8 -0
  63. package/src/NxtlinqAgentChat.tsx +0 -79
  64. package/src/components/AgentAssistantShell.tsx +0 -104
  65. package/src/components/AgentComposer.tsx +0 -134
  66. package/src/components/AgentMessageList.tsx +0 -78
  67. package/src/components/AgentRemoteAudio.tsx +0 -34
  68. package/src/components/AgentVoiceBar.tsx +0 -173
  69. package/src/components/PresetMessageChips.tsx +0 -41
  70. package/src/context/AgentAssistantContext.tsx +0 -294
  71. package/src/legacy/index.ts +0 -26
  72. package/src/theme/defaultTheme.ts +0 -22
  73. package/src/types.ts +0 -65
  74. package/src/voice/useVoiceConnectOrchestration.ts +0 -117
  75. package/src/voice/useVoiceMicState.ts +0 -117
  76. package/src/voice/useVoiceTranscriptMessages.ts +0 -188
  77. package/src/voice/voiceMicConstants.ts +0 -13
  78. package/src/voice/voiceUserBubble.ts +0 -71
  79. /package/src/{legacy/chatbot/ChatBot.tsx → ChatBot.tsx} +0 -0
  80. /package/src/{legacy/assets → assets}/images/adiSideItalicDataUri.ts +0 -0
  81. /package/src/{legacy/chatbot/types → types}/ChatBotTypes.ts +0 -0
  82. /package/src/{legacy/chatbot/ui → ui}/BerifyMeModal.tsx +0 -0
  83. /package/src/{legacy/chatbot/ui → ui}/ChatBotHeader.tsx +0 -0
  84. /package/src/{legacy/chatbot/ui → ui}/ChatBotUI.tsx +0 -0
  85. /package/src/{legacy/chatbot/ui → ui}/MessageInput.tsx +0 -0
  86. /package/src/{legacy/chatbot/ui → ui}/MessageList.tsx +0 -0
  87. /package/src/{legacy/chatbot/ui → ui}/NotificationModal.tsx +0 -0
  88. /package/src/{legacy/chatbot/ui → ui}/PermissionForm.tsx +0 -0
  89. /package/src/{legacy/chatbot/ui → ui}/PresetMessages.tsx +0 -0
  90. /package/src/{legacy/chatbot/ui → ui}/chatBotHeaderParts.tsx +0 -0
  91. /package/src/{legacy/chatbot/ui → ui}/styles/isolatedStyles.ts +0 -0
@@ -0,0 +1,321 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@emotion/react/jsx-runtime";
2
+ /** @jsxImportSource @emotion/react */
3
+ import { css } from '@emotion/react';
4
+ import MicIcon from '@mui/icons-material/Mic';
5
+ import MicOffIcon from '@mui/icons-material/MicOff';
6
+ import SendIcon from '@mui/icons-material/Send';
7
+ import VolumeUpIcon from '@mui/icons-material/VolumeUp';
8
+ import VolumeOffIcon from '@mui/icons-material/VolumeOff';
9
+ import AttachFileIcon from '@mui/icons-material/AttachFile';
10
+ import CloseIcon from '@mui/icons-material/Close';
11
+ import { IconButton, InputBase, Tooltip } from '@mui/material';
12
+ import * as React from 'react';
13
+ import { useChatBot } from '../context/ChatBotContext';
14
+ import { walletTextUtils } from '@bytexbyte/nxtlinq-ai-agent-web-development';
15
+ import { actionButton } from './styles/isolatedStyles';
16
+ export const MessageInput = () => {
17
+ const { inputValue, setInputValue, isLoading, isAITLoading, handleSubmit, uploadAttachment, showError, isMicEnabled, isAwaitingMicGesture, startRecording, stopRecording, textInputRef, autoSendEnabled, setAutoSendEnabled, textToSpeechEnabled, setTextToSpeechEnabled, serviceId, getCurrentModel, props: { placeholder = 'Type a message...' } } = useChatBot();
18
+ const [pendingAttachments, setPendingAttachments] = React.useState([]);
19
+ const [isUploading, setIsUploading] = React.useState(false);
20
+ const fileInputRef = React.useRef(null);
21
+ // Check if current model is llama (upload not supported)
22
+ const currentModel = getCurrentModel();
23
+ const isLlamaModel = currentModel.value === 'llama' || currentModel.value.includes('llama');
24
+ // Clear pending attachments when switching to llama model
25
+ React.useEffect(() => {
26
+ if (isLlamaModel && pendingAttachments.length > 0) {
27
+ setPendingAttachments([]);
28
+ // Also clear the file input
29
+ if (fileInputRef.current) {
30
+ fileInputRef.current.value = '';
31
+ }
32
+ }
33
+ }, [isLlamaModel, pendingAttachments.length]);
34
+ const isDisabled = isLoading || isAITLoading || isUploading;
35
+ const inputPlaceholder = isAITLoading ? walletTextUtils.getWalletText('Loading wallet configuration...', serviceId) : placeholder;
36
+ const hasContent = React.useCallback(() => (textInputRef.current?.value ?? inputValue).trim().length > 0 || pendingAttachments.length > 0, [inputValue, pendingAttachments.length]);
37
+ const doSubmit = React.useCallback(async (e) => {
38
+ if (!hasContent() || isDisabled)
39
+ return;
40
+ let attachmentsToSend = [];
41
+ if (pendingAttachments.length > 0) {
42
+ setIsUploading(true);
43
+ const uploadErrors = [];
44
+ try {
45
+ for (const { attachment, file } of pendingAttachments) {
46
+ try {
47
+ const res = await uploadAttachment(file);
48
+ if ('error' in res) {
49
+ uploadErrors.push(`${attachment.name}: ${res.error || 'Upload failed'}`);
50
+ continue; // Continue with other files instead of returning
51
+ }
52
+ attachmentsToSend.push({
53
+ type: attachment.type,
54
+ url: res.url,
55
+ name: attachment.name,
56
+ mimeType: attachment.mimeType,
57
+ size: attachment.size,
58
+ });
59
+ }
60
+ catch (err) {
61
+ const errorMessage = err instanceof Error ? err.message : 'Upload failed';
62
+ uploadErrors.push(`${attachment.name}: ${errorMessage}`);
63
+ console.error(`Failed to upload ${attachment.name}:`, err);
64
+ }
65
+ }
66
+ // Show errors if any files failed, but continue if some succeeded
67
+ if (uploadErrors.length > 0) {
68
+ if (attachmentsToSend.length === 0) {
69
+ // All files failed
70
+ showError(`All file uploads failed:\n${uploadErrors.join('\n')}`);
71
+ return;
72
+ }
73
+ else {
74
+ // Some files succeeded, some failed
75
+ showError(`Some files failed to upload:\n${uploadErrors.join('\n')}\n\n${attachmentsToSend.length} file(s) uploaded successfully; continuing to send.`);
76
+ }
77
+ }
78
+ }
79
+ finally {
80
+ setIsUploading(false);
81
+ }
82
+ }
83
+ setPendingAttachments([]);
84
+ await handleSubmit(e, attachmentsToSend);
85
+ }, [
86
+ hasContent,
87
+ pendingAttachments,
88
+ isDisabled,
89
+ uploadAttachment,
90
+ showError,
91
+ handleSubmit,
92
+ ]);
93
+ const handleKeyPress = (e) => {
94
+ if (e.key === 'Enter' && !e.shiftKey) {
95
+ e.preventDefault();
96
+ const syntheticEvent = {
97
+ preventDefault: () => e.preventDefault(),
98
+ stopPropagation: () => e.stopPropagation(),
99
+ nativeEvent: e.nativeEvent,
100
+ currentTarget: e.currentTarget,
101
+ target: e.target,
102
+ bubbles: e.bubbles,
103
+ cancelable: e.cancelable,
104
+ defaultPrevented: e.defaultPrevented,
105
+ eventPhase: e.eventPhase,
106
+ isTrusted: e.isTrusted,
107
+ timeStamp: e.timeStamp,
108
+ type: 'submit',
109
+ };
110
+ void doSubmit(syntheticEvent);
111
+ }
112
+ };
113
+ const fileToDataURL = (file) => {
114
+ return new Promise((resolve, reject) => {
115
+ const reader = new FileReader();
116
+ reader.onload = () => resolve(reader.result);
117
+ reader.onerror = reject;
118
+ reader.readAsDataURL(file);
119
+ });
120
+ };
121
+ const getFileType = (mimeType) => {
122
+ if (mimeType.startsWith('image/'))
123
+ return 'image';
124
+ return 'file';
125
+ };
126
+ const MAX_FILE_SIZE = 20 * 1024 * 1024; // 20MB - matches backend limit
127
+ const handleFileSelect = async (e) => {
128
+ const files = e.target.files;
129
+ if (!files || files.length === 0)
130
+ return;
131
+ const newPending = [];
132
+ const errors = [];
133
+ for (let i = 0; i < files.length; i++) {
134
+ const file = files[i];
135
+ // Check file size before processing
136
+ if (file.size > MAX_FILE_SIZE) {
137
+ errors.push(`${file.name}: File exceeds size limit (max 20MB)`);
138
+ continue;
139
+ }
140
+ // Check if file size is exactly at limit (should be < not <=)
141
+ if (file.size >= MAX_FILE_SIZE) {
142
+ errors.push(`${file.name}: File exceeds size limit (max 20MB)`);
143
+ continue;
144
+ }
145
+ try {
146
+ const dataUrl = await fileToDataURL(file);
147
+ const type = getFileType(file.type);
148
+ newPending.push({
149
+ attachment: {
150
+ type,
151
+ url: dataUrl,
152
+ name: file.name,
153
+ mimeType: file.type,
154
+ size: file.size,
155
+ },
156
+ file,
157
+ });
158
+ }
159
+ catch (err) {
160
+ const errorMessage = err instanceof Error ? err.message : 'Failed to read file';
161
+ errors.push(`${file.name}: ${errorMessage}`);
162
+ console.error(`Error reading file ${file.name}:`, err);
163
+ }
164
+ }
165
+ // Show errors if any, but still add successfully processed files
166
+ if (errors.length > 0) {
167
+ showError(`Some files could not be added:\n${errors.join('\n')}`);
168
+ }
169
+ // Add successfully processed files
170
+ if (newPending.length > 0) {
171
+ setPendingAttachments(prev => [...prev, ...newPending]);
172
+ }
173
+ if (fileInputRef.current)
174
+ fileInputRef.current.value = '';
175
+ };
176
+ const removeAttachment = (index) => {
177
+ setPendingAttachments(prev => prev.filter((_, i) => i !== index));
178
+ };
179
+ const handleSubmitWithAttachments = (e) => {
180
+ void doSubmit(e);
181
+ };
182
+ return (_jsxs(_Fragment, { children: [isAwaitingMicGesture && (_jsx("div", { role: "status", "aria-live": "polite", css: css `
183
+ margin: 12px 15px 0 !important;
184
+ padding: 10px 14px !important;
185
+ border-radius: 8px !important;
186
+ border: 1px solid #ffeeba !important;
187
+ background-color: #fff3cd !important;
188
+ color: #856404 !important;
189
+ font-size: 13px !important;
190
+ line-height: 1.4 !important;
191
+ z-index: 1000 !important;
192
+ position: relative !important;
193
+ display: block !important;
194
+ visibility: visible !important;
195
+ opacity: 1 !important;
196
+ `, children: "\u26A0\uFE0F The microphone needs a user interaction to re-enable. Please click on the page or press any key." })), pendingAttachments.length > 0 && (_jsx("div", { css: css `
197
+ padding: 10px 15px 0 !important;
198
+ display: flex !important;
199
+ flex-wrap: wrap !important;
200
+ gap: 8px !important;
201
+ border-top: 1px solid #eee !important;
202
+ `, children: pendingAttachments.map(({ attachment }, index) => (_jsxs("div", { css: css `
203
+ position: relative !important;
204
+ display: inline-block !important;
205
+ border: 1px solid #ddd !important;
206
+ border-radius: 8px !important;
207
+ overflow: hidden !important;
208
+ background: #f5f5f5 !important;
209
+ `, children: [attachment.type === 'image' && (_jsx("img", { src: attachment.url, alt: attachment.name, css: css `
210
+ max-width: 100px !important;
211
+ max-height: 100px !important;
212
+ object-fit: cover !important;
213
+ display: block !important;
214
+ ` })), attachment.type === 'file' && (_jsxs("div", { css: css `
215
+ padding: 20px 10px !important;
216
+ text-align: center !important;
217
+ min-width: 100px !important;
218
+ min-height: 100px !important;
219
+ display: flex !important;
220
+ align-items: center !important;
221
+ justify-content: center !important;
222
+ flex-direction: column !important;
223
+ `, children: [_jsx(AttachFileIcon, { css: css `font-size: 32px !important; color: #666 !important;` }), _jsx("span", { css: css `
224
+ font-size: 11px !important;
225
+ color: #666 !important;
226
+ margin-top: 4px !important;
227
+ word-break: break-word !important;
228
+ max-width: 80px !important;
229
+ `, children: attachment.name })] })), _jsx("button", { onClick: () => removeAttachment(index), css: css `
230
+ position: absolute !important;
231
+ top: 4px !important;
232
+ right: 4px !important;
233
+ background: rgba(0, 0, 0, 0.6) !important;
234
+ border: none !important;
235
+ border-radius: 50% !important;
236
+ width: 20px !important;
237
+ height: 20px !important;
238
+ display: flex !important;
239
+ align-items: center !important;
240
+ justify-content: center !important;
241
+ cursor: pointer !important;
242
+ padding: 0 !important;
243
+ color: white !important;
244
+ font-size: 12px !important;
245
+
246
+ &:hover {
247
+ background: rgba(0, 0, 0, 0.8) !important;
248
+ }
249
+ `, children: _jsx(CloseIcon, { fontSize: "small" }) })] }, index))) })), _jsxs("div", { css: css `
250
+ padding: 15px !important;
251
+ display: flex !important;
252
+ align-items: center !important;
253
+ gap: 10px !important;
254
+ border-top: 1px solid #eee !important;
255
+ `, children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, accept: "image/*,.pdf,.doc,.docx,.txt,.csv,.xlsx,.xls", onChange: handleFileSelect, css: css `
256
+ display: none !important;
257
+ ` }), _jsx(Tooltip, { title: isLlamaModel
258
+ ? 'Llama model does not support file upload. Please switch to another model to use this feature.'
259
+ : 'Upload file', children: _jsx("span", { children: _jsx(IconButton, { onClick: () => !isLlamaModel && fileInputRef.current?.click(), disabled: isDisabled || isLlamaModel, css: css `
260
+ padding: 8px !important;
261
+ color: ${isDisabled || isLlamaModel ? '#ccc' : '#666'} !important;
262
+ cursor: ${isLlamaModel ? 'not-allowed' : 'pointer'} !important;
263
+ `, children: _jsx(AttachFileIcon, {}) }) }) }), _jsx(InputBase, { value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyPress: handleKeyPress, placeholder: inputPlaceholder, fullWidth: true, inputProps: {
264
+ ref: textInputRef
265
+ }, endAdornment: _jsxs(_Fragment, { children: [_jsx(Tooltip, { title: textToSpeechEnabled ? 'Text-to-speech enabled' : 'Text-to-speech disabled', children: _jsx(IconButton, { size: "small", onClick: (e) => {
266
+ e.stopPropagation();
267
+ setTextToSpeechEnabled(!textToSpeechEnabled);
268
+ }, css: css `
269
+ padding: 4px !important;
270
+ margin-right: 4px !important;
271
+ color: ${textToSpeechEnabled ? '#1976d2' : '#9e9e9e'} !important;
272
+ `, children: textToSpeechEnabled ? _jsx(VolumeUpIcon, { fontSize: "small" }) : _jsx(VolumeOffIcon, { fontSize: "small" }) }) }), isMicEnabled && (_jsx(Tooltip, { title: autoSendEnabled ? 'Auto send enabled' : 'Auto send disabled', children: _jsx(IconButton, { size: "small", onClick: (e) => {
273
+ e.stopPropagation();
274
+ setAutoSendEnabled(!autoSendEnabled);
275
+ }, css: css `
276
+ padding: 4px !important;
277
+ margin-right: 4px !important;
278
+ color: ${autoSendEnabled ? '#1976d2' : '#9e9e9e'} !important;
279
+ `, children: _jsx(SendIcon, { fontSize: "small" }) }) })), _jsx(IconButton, { onClick: () => (isMicEnabled ? stopRecording() : startRecording()), children: isMicEnabled ? _jsx(MicIcon, {}) : _jsx(MicOffIcon, {}) })] }), css: css `
280
+ flex: 1 !important;
281
+ padding: 10px !important;
282
+ border: 1px solid #ddd !important;
283
+ border-radius: 20px !important;
284
+ outline: none !important;
285
+ font-size: 14px !important;
286
+ background-color: #fff !important;
287
+ height: 40px !important;
288
+ box-sizing: border-box !important;
289
+
290
+ @media (max-width: 768px) {
291
+ font-size: 16px !important;
292
+ & input {
293
+ font-size: 16px !important;
294
+ }
295
+ }
296
+ ` }), _jsxs("button", { onClick: handleSubmitWithAttachments, disabled: isDisabled || !hasContent(), css: css `
297
+ ${actionButton}
298
+ padding: 10px 20px !important;
299
+ border-radius: 20px !important;
300
+ position: relative !important;
301
+ display: flex !important;
302
+ align-items: center !important;
303
+ justify-content: center !important;
304
+ height: 40px !important;
305
+ box-sizing: border-box !important;
306
+
307
+ &:disabled {
308
+ background-color: #e9ecef !important;
309
+ color: #6c757d !important;
310
+ cursor: not-allowed !important;
311
+ }
312
+
313
+ &:hover:not(:disabled) {
314
+ background-color: #0056b3 !important;
315
+ }
316
+ `, children: ["Send", (isLoading || isAITLoading || isUploading) && (_jsx("span", { css: css `
317
+ margin-left: 8px !important;
318
+ display: flex !important;
319
+ align-items: center !important;
320
+ `, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 50 50", children: _jsx("circle", { cx: "25", cy: "25", r: "20", fill: "none", stroke: "#fff", strokeWidth: "4", strokeDasharray: "31.4 31.4", strokeLinecap: "round", children: _jsx("animateTransform", { attributeName: "transform", type: "rotate", from: "0 25 25", to: "360 25 25", dur: "1s", repeatCount: "indefinite" }) }) }) }))] })] })] }));
321
+ };
@@ -0,0 +1,4 @@
1
+ /** @jsxImportSource @emotion/react */
2
+ import * as React from 'react';
3
+ export declare const MessageList: React.FC;
4
+ //# sourceMappingURL=MessageList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/ui/MessageList.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAuT/B,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAmc/B,CAAC"}