@messenger-box/tailwind-ui-inbox 10.0.3-alpha.121 → 10.0.3-alpha.123
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/CHANGELOG.md +8 -0
- package/lib/components/AIAgent/AIAgent.d.ts +2 -0
- package/lib/components/AIAgent/AIAgent.d.ts.map +1 -1
- package/lib/components/AIAgent/AIAgent.js +48 -29
- package/lib/components/AIAgent/AIAgent.js.map +1 -1
- package/lib/components/InboxMessage/InputComponent.d.ts +4 -1
- package/lib/components/InboxMessage/InputComponent.d.ts.map +1 -1
- package/lib/components/InboxMessage/InputComponent.js +20 -261
- package/lib/components/InboxMessage/InputComponent.js.map +1 -1
- package/lib/components/InboxMessage/UploadImageButton.js +2 -6
- package/lib/components/InboxMessage/UploadImageButton.js.map +1 -1
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +1 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +15 -5
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -1
- package/lib/components/ModelConfigPanel.d.ts +10 -0
- package/lib/components/ModelConfigPanel.d.ts.map +1 -1
- package/lib/components/ModelConfigPanel.js +551 -2
- package/lib/components/ModelConfigPanel.js.map +1 -1
- package/lib/components/filler-components/RightSiderBar.d.ts +1 -0
- package/lib/components/filler-components/RightSiderBar.d.ts.map +1 -1
- package/lib/components/filler-components/RightSiderBar.js +173 -148
- package/lib/components/filler-components/RightSiderBar.js.map +1 -1
- package/lib/components/slot-fill/right-sidebar-filler.d.ts.map +1 -1
- package/lib/components/slot-fill/right-sidebar-filler.js.map +1 -1
- package/lib/config/env-config.d.ts +5 -0
- package/lib/config/env-config.d.ts.map +1 -1
- package/lib/config/env-config.js +16 -1
- package/lib/config/env-config.js.map +1 -1
- package/lib/container/AiLandingInput.d.ts.map +1 -1
- package/lib/container/AiLandingInput.js +19 -15
- package/lib/container/AiLandingInput.js.map +1 -1
- package/lib/container/Inbox.js +1 -1
- package/lib/container/Inbox.js.map +1 -1
- package/lib/container/InboxAiMessagesLoader.d.ts +1 -0
- package/lib/container/InboxAiMessagesLoader.d.ts.map +1 -1
- package/lib/container/InboxAiMessagesLoader.js +4 -1
- package/lib/container/InboxAiMessagesLoader.js.map +1 -1
- package/lib/container/InboxContainer.d.ts +1 -0
- package/lib/container/InboxContainer.d.ts.map +1 -1
- package/lib/container/InboxContainer.js +1 -6
- package/lib/container/InboxContainer.js.map +1 -1
- package/lib/container/InboxWithAiLoader.d.ts +1 -0
- package/lib/container/InboxWithAiLoader.d.ts.map +1 -1
- package/lib/container/InboxWithAiLoader.js +2 -1
- package/lib/container/InboxWithAiLoader.js.map +1 -1
- package/lib/container/ServiceInbox.js +1 -1
- package/lib/container/ServiceInbox.js.map +1 -1
- package/lib/container/ThreadMessages.js +1 -1
- package/lib/container/ThreadMessages.js.map +1 -1
- package/lib/container/ThreadMessagesInbox.js +1 -1
- package/lib/container/ThreadMessagesInbox.js.map +1 -1
- package/lib/container/Threads.js +1 -1
- package/lib/container/Threads.js.map +1 -1
- package/lib/hooks/usePersistentModelConfig.d.ts +5 -2
- package/lib/hooks/usePersistentModelConfig.d.ts.map +1 -1
- package/lib/hooks/usePersistentModelConfig.js +30 -10
- package/lib/hooks/usePersistentModelConfig.js.map +1 -1
- package/lib/module.js +1 -1
- package/lib/module.js.map +1 -1
- package/lib/templates/InboxWithAi.d.ts +1 -0
- package/lib/templates/InboxWithAi.d.ts.map +1 -1
- package/lib/templates/InboxWithAi.js +9 -6
- package/lib/templates/InboxWithAi.js.map +1 -1
- package/lib/templates/InboxWithAi.tsx +7 -3
- package/lib/xstate/index.d.ts +3 -0
- package/lib/xstate/index.d.ts.map +1 -0
- package/lib/xstate/rightSidebar.machine.d.ts +4 -0
- package/lib/xstate/rightSidebar.machine.d.ts.map +1 -0
- package/lib/xstate/rightSidebar.machine.js +174 -0
- package/lib/xstate/rightSidebar.machine.js.map +1 -0
- package/lib/xstate/rightSidebar.types.d.ts +52 -0
- package/lib/xstate/rightSidebar.types.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/components/AIAgent/AIAgent.tsx +55 -28
- package/src/components/InboxMessage/InputComponent.tsx +23 -325
- package/src/components/InboxMessage/UploadImageButton.tsx +4 -4
- package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +17 -0
- package/src/components/ModelConfigPanel.tsx +666 -0
- package/src/components/filler-components/RightSiderBar.tsx +193 -146
- package/src/components/slot-fill/right-sidebar-filler.tsx +1 -0
- package/src/config/env-config.ts +6 -1
- package/src/container/AiLandingInput.tsx +32 -119
- package/src/container/InboxAiMessagesLoader.tsx +13 -2
- package/src/container/InboxContainer.tsx +2 -8
- package/src/container/InboxWithAiLoader.tsx +2 -0
- package/src/hooks/usePersistentModelConfig.ts +26 -13
- package/src/templates/InboxWithAi.tsx +7 -3
- package/src/xstate/index.ts +2 -0
- package/src/xstate/rightSidebar.machine.ts +139 -0
- package/src/xstate/rightSidebar.types.ts +55 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import React, { useCallback, useMemo, useState, useRef, useEffect } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { config } from '../../config';
|
|
4
|
-
import { UploadImageButton } from './UploadImageButton';
|
|
5
4
|
import { FilesList } from '../inbox';
|
|
6
|
-
import {
|
|
5
|
+
import { ModelToolbar } from '../ModelConfigPanel';
|
|
7
6
|
import { ModelConfig } from '../../hooks/usePersistentModelConfig';
|
|
8
7
|
|
|
9
8
|
type MessageInputProps = {
|
|
@@ -12,6 +11,9 @@ type MessageInputProps = {
|
|
|
12
11
|
placeholder?: string;
|
|
13
12
|
modelConfig?: ModelConfig;
|
|
14
13
|
onModelConfigChange?: (config: ModelConfig) => void;
|
|
14
|
+
textareaClassName?: string;
|
|
15
|
+
textareaStyles?: React.CSSProperties;
|
|
16
|
+
toolBarParentClassName?: string;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
export const InputComponent = ({
|
|
@@ -19,6 +21,9 @@ export const InputComponent = ({
|
|
|
19
21
|
placeholder,
|
|
20
22
|
modelConfig,
|
|
21
23
|
onModelConfigChange,
|
|
24
|
+
textareaClassName,
|
|
25
|
+
toolBarParentClassName,
|
|
26
|
+
textareaStyles,
|
|
22
27
|
}: MessageInputProps) => {
|
|
23
28
|
const [message, setMessage] = useState('');
|
|
24
29
|
const [sending, setSending] = useState(false);
|
|
@@ -26,12 +31,7 @@ export const InputComponent = ({
|
|
|
26
31
|
const [showToast, setShowToast] = useState(false);
|
|
27
32
|
const [toastMessage, setToastMessage] = useState('');
|
|
28
33
|
const [isFocused, setIsFocused] = useState(false);
|
|
29
|
-
const [showModelDropdown, setShowModelDropdown] = useState(false);
|
|
30
|
-
const [showTemplateDropdown, setShowTemplateDropdown] = useState(false);
|
|
31
|
-
const [showSettingsModal, setShowSettingsModal] = useState(false);
|
|
32
34
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
33
|
-
const modelDropdownRef = useRef<HTMLDivElement>(null);
|
|
34
|
-
const templateDropdownRef = useRef<HTMLDivElement>(null);
|
|
35
35
|
const { t } = useTranslation('translations');
|
|
36
36
|
|
|
37
37
|
// Auto-focus the textarea when component mounts
|
|
@@ -48,19 +48,7 @@ export const InputComponent = ({
|
|
|
48
48
|
}
|
|
49
49
|
}, [modelConfig, onModelConfigChange]);
|
|
50
50
|
|
|
51
|
-
//
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
54
|
-
if (modelDropdownRef.current && !modelDropdownRef.current.contains(event.target as Node)) {
|
|
55
|
-
setShowModelDropdown(false);
|
|
56
|
-
}
|
|
57
|
-
if (templateDropdownRef.current && !templateDropdownRef.current.contains(event.target as Node)) {
|
|
58
|
-
setShowTemplateDropdown(false);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
62
|
-
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
63
|
-
}, []);
|
|
51
|
+
// no dropdowns managed here; toolbar handles its own
|
|
64
52
|
|
|
65
53
|
const showToastMessage = useCallback((message: string) => {
|
|
66
54
|
setToastMessage(message);
|
|
@@ -126,38 +114,11 @@ export const InputComponent = ({
|
|
|
126
114
|
const canSend = message.trim() || files.length > 0;
|
|
127
115
|
const hasContent = message.trim().length > 0;
|
|
128
116
|
|
|
129
|
-
//
|
|
130
|
-
const allModelOptions = useMemo(() => {
|
|
131
|
-
return getAllModels();
|
|
132
|
-
}, []);
|
|
133
|
-
|
|
134
|
-
const templateOptionsList = useMemo(() => {
|
|
135
|
-
return templateOptions.map((t) => ({ value: t.value, label: t.label, icon: t.icon }));
|
|
136
|
-
}, []);
|
|
137
|
-
|
|
138
|
-
const handleModelSelect = useCallback(
|
|
139
|
-
(modelValue: string) => {
|
|
140
|
-
if (onModelConfigChange && modelConfig) {
|
|
141
|
-
onModelConfigChange({ ...modelConfig, model: modelValue });
|
|
142
|
-
}
|
|
143
|
-
setShowModelDropdown(false);
|
|
144
|
-
},
|
|
145
|
-
[modelConfig, onModelConfigChange],
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
const handleTemplateSelect = useCallback(
|
|
149
|
-
(templateValue: string) => {
|
|
150
|
-
if (onModelConfigChange && modelConfig) {
|
|
151
|
-
onModelConfigChange({ ...modelConfig, template: templateValue as any });
|
|
152
|
-
}
|
|
153
|
-
setShowTemplateDropdown(false);
|
|
154
|
-
},
|
|
155
|
-
[modelConfig, onModelConfigChange],
|
|
156
|
-
);
|
|
117
|
+
// toolbar now owns model/template selections
|
|
157
118
|
|
|
158
119
|
return (
|
|
159
120
|
// <div className="bg-gray-50 border-t border-gray-200">
|
|
160
|
-
<div className="bg-
|
|
121
|
+
<div className="bg-white ">
|
|
161
122
|
{/* Toast notification */}
|
|
162
123
|
{showToast && (
|
|
163
124
|
<div className="fixed top-4 right-4 z-50 bg-orange-50 border border-orange-200 text-orange-800 px-4 py-3 rounded-lg shadow-lg animate-bounce">
|
|
@@ -187,13 +148,14 @@ export const InputComponent = ({
|
|
|
187
148
|
<div className="relative mb-3">
|
|
188
149
|
<textarea
|
|
189
150
|
ref={textareaRef}
|
|
190
|
-
className={`w-full text-base bg-white border-
|
|
191
|
-
isFocused ? 'border-
|
|
192
|
-
}`}
|
|
151
|
+
className={`w-full text-base bg-white border border-gray-300 rounded-2xl pl-4 pr-4 py-3 resize-none focus:border-gray-300 focus:outline-none placeholder-gray-500 transition-all duration-200 ${
|
|
152
|
+
isFocused ? 'border-gray-400 ring-2 ring-gray-300 focus:ring-gray-300' : 'border-gray-300'
|
|
153
|
+
} ${textareaClassName}`}
|
|
193
154
|
style={{
|
|
194
155
|
height: `${inputHeight}px`,
|
|
195
156
|
minHeight: '120px',
|
|
196
157
|
maxHeight: '200px',
|
|
158
|
+
...textareaStyles,
|
|
197
159
|
}}
|
|
198
160
|
placeholder={placeholder || 'Message'}
|
|
199
161
|
value={sending ? '' : message}
|
|
@@ -205,281 +167,17 @@ export const InputComponent = ({
|
|
|
205
167
|
rows={1}
|
|
206
168
|
/>
|
|
207
169
|
</div>
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
viewBox="0 0 24 24"
|
|
218
|
-
>
|
|
219
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
220
|
-
</svg>
|
|
221
|
-
</div>
|
|
222
|
-
</UploadImageButton>
|
|
223
|
-
{/* 1. Template Selection Dropdown */}
|
|
224
|
-
<div className="relative" ref={templateDropdownRef}>
|
|
225
|
-
<button
|
|
226
|
-
onClick={() => setShowTemplateDropdown(!showTemplateDropdown)}
|
|
227
|
-
className="flex items-center gap-2 px-3 py-1.5 text-sm rounded-lg border border-gray-300 bg-white hover:bg-gray-50 transition-colors min-w-[120px]"
|
|
228
|
-
>
|
|
229
|
-
{(() => {
|
|
230
|
-
if (modelConfig && modelConfig.template) {
|
|
231
|
-
const currentTemplate = templateOptionsList.find(
|
|
232
|
-
(option) => option.value === modelConfig.template,
|
|
233
|
-
);
|
|
234
|
-
return currentTemplate ? (
|
|
235
|
-
<>
|
|
236
|
-
<span className="text-lg">{currentTemplate.icon}</span>
|
|
237
|
-
<span className="text-gray-700">{currentTemplate.label}</span>
|
|
238
|
-
</>
|
|
239
|
-
) : (
|
|
240
|
-
<>
|
|
241
|
-
<span className="text-lg">📝</span>
|
|
242
|
-
<span className="text-gray-500">Template</span>
|
|
243
|
-
</>
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
return (
|
|
247
|
-
<>
|
|
248
|
-
<span className="text-lg">📝</span>
|
|
249
|
-
<span className="text-gray-500">Template</span>
|
|
250
|
-
</>
|
|
251
|
-
);
|
|
252
|
-
})()}
|
|
253
|
-
<svg
|
|
254
|
-
className="w-4 h-4 text-gray-500 ml-auto"
|
|
255
|
-
fill="none"
|
|
256
|
-
stroke="currentColor"
|
|
257
|
-
viewBox="0 0 24 24"
|
|
258
|
-
>
|
|
259
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
260
|
-
</svg>
|
|
261
|
-
</button>
|
|
262
|
-
|
|
263
|
-
{showTemplateDropdown && (
|
|
264
|
-
<div className="absolute bottom-full left-0 mb-2 w-48 bg-white border border-gray-200 rounded-lg shadow-lg z-50">
|
|
265
|
-
<div className="py-1">
|
|
266
|
-
{templateOptionsList.map((option) => (
|
|
267
|
-
<button
|
|
268
|
-
key={option.value}
|
|
269
|
-
onClick={() => handleTemplateSelect(option.value)}
|
|
270
|
-
className={`w-full px-3 py-2 text-left hover:bg-gray-50 transition-colors flex items-center gap-2 ${
|
|
271
|
-
modelConfig?.template === option.value ? 'bg-blue-50' : ''
|
|
272
|
-
}`}
|
|
273
|
-
>
|
|
274
|
-
<span className="text-lg">{option.icon}</span>
|
|
275
|
-
<span className="font-medium text-gray-900">{option.label}</span>
|
|
276
|
-
</button>
|
|
277
|
-
))}
|
|
278
|
-
</div>
|
|
279
|
-
</div>
|
|
280
|
-
)}
|
|
281
|
-
</div>
|
|
282
|
-
|
|
283
|
-
{/* 2. Model Selection Dropdown */}
|
|
284
|
-
<div className="relative" ref={modelDropdownRef}>
|
|
285
|
-
<button
|
|
286
|
-
onClick={() => setShowModelDropdown(!showModelDropdown)}
|
|
287
|
-
className="flex items-center gap-2 px-3 py-1.5 text-sm rounded-lg border border-gray-300 bg-white hover:bg-gray-50 transition-colors min-w-[140px]"
|
|
288
|
-
>
|
|
289
|
-
{(() => {
|
|
290
|
-
if (modelConfig && modelConfig.model) {
|
|
291
|
-
const currentModel = allModelOptions.find(
|
|
292
|
-
(option) => option.value === modelConfig.model,
|
|
293
|
-
);
|
|
294
|
-
return currentModel ? (
|
|
295
|
-
<span className="text-gray-700">{currentModel.label}</span>
|
|
296
|
-
) : (
|
|
297
|
-
<span className="text-gray-500">Model</span>
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
return <span className="text-gray-500">Model</span>;
|
|
301
|
-
})()}
|
|
302
|
-
<svg
|
|
303
|
-
className="w-4 h-4 text-gray-500 ml-auto"
|
|
304
|
-
fill="none"
|
|
305
|
-
stroke="currentColor"
|
|
306
|
-
viewBox="0 0 24 24"
|
|
307
|
-
>
|
|
308
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
309
|
-
</svg>
|
|
310
|
-
</button>
|
|
311
|
-
|
|
312
|
-
{showModelDropdown && (
|
|
313
|
-
<div className="absolute bottom-full left-0 mb-2 w-64 bg-white border border-gray-200 rounded-lg shadow-lg z-50 max-h-60 overflow-y-auto">
|
|
314
|
-
<div className="py-1">
|
|
315
|
-
{allModelOptions.map((option) => (
|
|
316
|
-
<button
|
|
317
|
-
key={option.value}
|
|
318
|
-
onClick={() => handleModelSelect(option.value)}
|
|
319
|
-
className={`w-full px-3 py-2 text-left hover:bg-gray-50 transition-colors ${
|
|
320
|
-
modelConfig?.model === option.value ? 'bg-blue-50' : ''
|
|
321
|
-
}`}
|
|
322
|
-
>
|
|
323
|
-
<div className="font-medium text-gray-900">{option.label}</div>
|
|
324
|
-
</button>
|
|
325
|
-
))}
|
|
326
|
-
</div>
|
|
327
|
-
</div>
|
|
328
|
-
)}
|
|
329
|
-
</div>
|
|
330
|
-
|
|
331
|
-
<div className="flex-1"></div>
|
|
332
|
-
|
|
333
|
-
{/* 3. Upload Button */}
|
|
334
|
-
|
|
335
|
-
{/* 4. Project Settings Button */}
|
|
336
|
-
<button
|
|
337
|
-
onClick={() => setShowSettingsModal(true)}
|
|
338
|
-
className="flex items-center justify-center w-8 h-8 rounded-lg border border-gray-300 bg-white hover:bg-gray-50 transition-colors"
|
|
339
|
-
>
|
|
340
|
-
<svg className="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
341
|
-
<path
|
|
342
|
-
strokeLinecap="round"
|
|
343
|
-
strokeLinejoin="round"
|
|
344
|
-
strokeWidth={2}
|
|
345
|
-
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
|
346
|
-
/>
|
|
347
|
-
<path
|
|
348
|
-
strokeLinecap="round"
|
|
349
|
-
strokeLinejoin="round"
|
|
350
|
-
strokeWidth={2}
|
|
351
|
-
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
352
|
-
/>
|
|
353
|
-
</svg>
|
|
354
|
-
</button>
|
|
355
|
-
|
|
356
|
-
{/* 5. Send Button */}
|
|
357
|
-
<button
|
|
358
|
-
className={`flex items-center justify-center w-8 h-8 rounded-lg border transition-colors ${
|
|
359
|
-
canSend && !sending
|
|
360
|
-
? 'border-blue-500 bg-blue-500 hover:bg-blue-600 text-white'
|
|
361
|
-
: 'border-gray-300 bg-gray-100 text-gray-400 cursor-not-allowed'
|
|
362
|
-
}`}
|
|
363
|
-
onClick={handleSend}
|
|
364
|
-
disabled={!canSend || sending}
|
|
365
|
-
type="button"
|
|
366
|
-
>
|
|
367
|
-
{sending ? (
|
|
368
|
-
<svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
|
369
|
-
<circle
|
|
370
|
-
className="opacity-25"
|
|
371
|
-
cx="12"
|
|
372
|
-
cy="12"
|
|
373
|
-
r="10"
|
|
374
|
-
stroke="currentColor"
|
|
375
|
-
strokeWidth="4"
|
|
376
|
-
></circle>
|
|
377
|
-
<path
|
|
378
|
-
className="opacity-75"
|
|
379
|
-
fill="currentColor"
|
|
380
|
-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
381
|
-
></path>
|
|
382
|
-
</svg>
|
|
383
|
-
) : (
|
|
384
|
-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
385
|
-
<path
|
|
386
|
-
strokeLinecap="round"
|
|
387
|
-
strokeLinejoin="round"
|
|
388
|
-
strokeWidth={2}
|
|
389
|
-
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
|
|
390
|
-
/>
|
|
391
|
-
</svg>
|
|
392
|
-
)}
|
|
393
|
-
</button>
|
|
170
|
+
<div className={`${toolBarParentClassName}`}>
|
|
171
|
+
<ModelToolbar
|
|
172
|
+
modelConfig={modelConfig}
|
|
173
|
+
onModelConfigChange={onModelConfigChange}
|
|
174
|
+
sending={sending}
|
|
175
|
+
canSend={!!canSend}
|
|
176
|
+
onSend={handleSend}
|
|
177
|
+
onUploadImageChange={onUploadImageChange}
|
|
178
|
+
/>
|
|
394
179
|
</div>
|
|
395
180
|
</div>
|
|
396
|
-
|
|
397
|
-
{/* Simplified Settings Modal - Only API Key and Model */}
|
|
398
|
-
{showSettingsModal && (
|
|
399
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
|
|
400
|
-
<div className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4">
|
|
401
|
-
<div className="p-6 mb-4">
|
|
402
|
-
<div className="flex items-center justify-between mb-4">
|
|
403
|
-
<h3 className="text-lg font-semibold text-gray-900">Project Settings</h3>
|
|
404
|
-
<button
|
|
405
|
-
onClick={() => setShowSettingsModal(false)}
|
|
406
|
-
className="text-gray-400 hover:text-gray-600 transition-colors"
|
|
407
|
-
>
|
|
408
|
-
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
409
|
-
<path
|
|
410
|
-
strokeLinecap="round"
|
|
411
|
-
strokeLinejoin="round"
|
|
412
|
-
strokeWidth={2}
|
|
413
|
-
d="M6 18L18 6M6 6l12 12"
|
|
414
|
-
/>
|
|
415
|
-
</svg>
|
|
416
|
-
</button>
|
|
417
|
-
</div>
|
|
418
|
-
|
|
419
|
-
{modelConfig && onModelConfigChange && (
|
|
420
|
-
<div className="space-y-4">
|
|
421
|
-
{/* API Key Input */}
|
|
422
|
-
<div>
|
|
423
|
-
<label className="block text-sm font-medium text-gray-700 mb-2">API Key</label>
|
|
424
|
-
<input
|
|
425
|
-
type="password"
|
|
426
|
-
value={modelConfig.apiKey}
|
|
427
|
-
onChange={(e) =>
|
|
428
|
-
onModelConfigChange({ ...modelConfig, apiKey: e.target.value })
|
|
429
|
-
}
|
|
430
|
-
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
431
|
-
placeholder="Enter your API key"
|
|
432
|
-
/>
|
|
433
|
-
</div>
|
|
434
|
-
|
|
435
|
-
{/* Extension ID Input */}
|
|
436
|
-
<div>
|
|
437
|
-
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
438
|
-
Extension ID
|
|
439
|
-
</label>
|
|
440
|
-
<input
|
|
441
|
-
type="text"
|
|
442
|
-
value={modelConfig.extensionId || ''}
|
|
443
|
-
onChange={(e) =>
|
|
444
|
-
onModelConfigChange({ ...modelConfig, extensionId: e.target.value })
|
|
445
|
-
}
|
|
446
|
-
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
447
|
-
placeholder="Enter your extension ID"
|
|
448
|
-
/>
|
|
449
|
-
</div>
|
|
450
|
-
|
|
451
|
-
{/* Model Selection */}
|
|
452
|
-
<div>
|
|
453
|
-
<label className="block text-sm font-medium text-gray-700 mb-2">Model</label>
|
|
454
|
-
<select
|
|
455
|
-
value={modelConfig.model}
|
|
456
|
-
onChange={(e) =>
|
|
457
|
-
onModelConfigChange({ ...modelConfig, model: e.target.value })
|
|
458
|
-
}
|
|
459
|
-
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
460
|
-
>
|
|
461
|
-
{allModelOptions.map((option) => (
|
|
462
|
-
<option key={option.value} value={option.value}>
|
|
463
|
-
{option.label}
|
|
464
|
-
</option>
|
|
465
|
-
))}
|
|
466
|
-
</select>
|
|
467
|
-
</div>
|
|
468
|
-
</div>
|
|
469
|
-
)}
|
|
470
|
-
|
|
471
|
-
{/* <div className="mt-6 flex justify-end">
|
|
472
|
-
<button
|
|
473
|
-
onClick={() => setShowSettingsModal(false)}
|
|
474
|
-
className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
|
|
475
|
-
>
|
|
476
|
-
Save Settings
|
|
477
|
-
</button>
|
|
478
|
-
</div> */}
|
|
479
|
-
</div>
|
|
480
|
-
</div>
|
|
481
|
-
</div>
|
|
482
|
-
)}
|
|
483
181
|
</div>
|
|
484
182
|
);
|
|
485
183
|
};
|
|
@@ -28,14 +28,14 @@ export const UploadImageButton = (props: IUploadImageButtonProps) => {
|
|
|
28
28
|
onChange={props.onChange}
|
|
29
29
|
/>
|
|
30
30
|
{/* Tooltip */}
|
|
31
|
-
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-3 py-2 bg-gray-900 text-white text-xs rounded-lg shadow-lg opacity-0 group-hover:opacity-100 transition-all duration-200 pointer-events-none whitespace-nowrap z-20">
|
|
31
|
+
{/* <div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-3 py-2 bg-gray-900 text-white text-xs rounded-lg shadow-lg opacity-0 group-hover:opacity-100 transition-all duration-200 pointer-events-none whitespace-nowrap z-20">
|
|
32
32
|
{t('tailwind_ui_inbox.upload_image')}
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
<div className="absolute top-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-4 border-r-4 border-t-4 border-l-transparent border-r-transparent border-t-gray-900"></div>
|
|
35
|
-
</div>
|
|
35
|
+
</div> */}
|
|
36
36
|
<button
|
|
37
37
|
type="button"
|
|
38
|
-
className="flex items-center justify-center w-8 h-8 bg-transparent hover:bg-gray-100 rounded-md focus:outline-none
|
|
38
|
+
className="flex items-center justify-center w-8 h-8 bg-transparent hover:bg-gray-100 rounded-md focus:outline-none focus:ring-opacity-50 transition-all duration-200 ease-in-out"
|
|
39
39
|
onClick={selectFile}
|
|
40
40
|
aria-label={t('tailwind_ui_inbox.upload_image')}
|
|
41
41
|
>
|
|
@@ -402,6 +402,7 @@ const useInjectStyles = () => {
|
|
|
402
402
|
interface ModernMessageGroupProps {
|
|
403
403
|
showAuthorName?: boolean;
|
|
404
404
|
showAvatar?: boolean;
|
|
405
|
+
showTimestamp?: boolean;
|
|
405
406
|
messages: IPost[];
|
|
406
407
|
currentUser: IAuthUser;
|
|
407
408
|
onOpen: (element?: any) => void;
|
|
@@ -417,6 +418,7 @@ interface ModernMessageGroupProps {
|
|
|
417
418
|
interface MessageGroupProps {
|
|
418
419
|
showAuthorName?: boolean;
|
|
419
420
|
showAvatar?: boolean;
|
|
421
|
+
showTimestamp?: boolean;
|
|
420
422
|
author: any;
|
|
421
423
|
messages: IPost[];
|
|
422
424
|
currentUser: IAuthUser;
|
|
@@ -919,6 +921,7 @@ const ModernMessageGroup: React.FC<MessageGroupProps> = ({
|
|
|
919
921
|
currentUser,
|
|
920
922
|
showAuthorName = false,
|
|
921
923
|
showAvatar = false,
|
|
924
|
+
showTimestamp = false,
|
|
922
925
|
onOpen,
|
|
923
926
|
onMessageClick,
|
|
924
927
|
isDesktopView = false,
|
|
@@ -977,6 +980,7 @@ const ModernMessageGroup: React.FC<MessageGroupProps> = ({
|
|
|
977
980
|
formatTime={formatTime}
|
|
978
981
|
showAuthorName={showAuthorName}
|
|
979
982
|
showAvatar={showAvatar}
|
|
983
|
+
showTimestamp={showTimestamp}
|
|
980
984
|
/>
|
|
981
985
|
))}
|
|
982
986
|
</div>
|
|
@@ -1034,6 +1038,7 @@ const ModernMessageGroup: React.FC<MessageGroupProps> = ({
|
|
|
1034
1038
|
isLastInGroup={index === messages.length - 1}
|
|
1035
1039
|
onMessageClick={onMessageClick}
|
|
1036
1040
|
formatTime={formatTime}
|
|
1041
|
+
showTimestamp={showTimestamp}
|
|
1037
1042
|
/>
|
|
1038
1043
|
))}
|
|
1039
1044
|
</div>
|
|
@@ -1046,6 +1051,7 @@ const ModernMessageGroup: React.FC<MessageGroupProps> = ({
|
|
|
1046
1051
|
interface ModernMessageBubbleProps {
|
|
1047
1052
|
showAuthorName?: boolean;
|
|
1048
1053
|
showAvatar?: boolean;
|
|
1054
|
+
showTimestamp?: boolean;
|
|
1049
1055
|
message: IPost;
|
|
1050
1056
|
isOwnMessage: boolean;
|
|
1051
1057
|
isSystemMessage: boolean;
|
|
@@ -1058,6 +1064,7 @@ interface ModernMessageBubbleProps {
|
|
|
1058
1064
|
const ModernMessageBubble: React.FC<ModernMessageBubbleProps> = ({
|
|
1059
1065
|
showAuthorName = false,
|
|
1060
1066
|
showAvatar = false,
|
|
1067
|
+
showTimestamp = false,
|
|
1061
1068
|
message,
|
|
1062
1069
|
isOwnMessage,
|
|
1063
1070
|
isSystemMessage,
|
|
@@ -1142,6 +1149,11 @@ const ModernMessageBubble: React.FC<ModernMessageBubbleProps> = ({
|
|
|
1142
1149
|
<FilesList uploaded files={message.files.data} />
|
|
1143
1150
|
</div>
|
|
1144
1151
|
)}
|
|
1152
|
+
{showTimestamp ? (
|
|
1153
|
+
<div className="text-[10px] text-gray-500 mt-1 text-right">
|
|
1154
|
+
{formatTime(message.createdAt)}
|
|
1155
|
+
</div>
|
|
1156
|
+
) : null}
|
|
1145
1157
|
</div>
|
|
1146
1158
|
</div>
|
|
1147
1159
|
|
|
@@ -1262,6 +1274,9 @@ const ModernMessageBubble: React.FC<ModernMessageBubbleProps> = ({
|
|
|
1262
1274
|
</div>
|
|
1263
1275
|
)} */}
|
|
1264
1276
|
</div>
|
|
1277
|
+
{showTimestamp ? (
|
|
1278
|
+
<div className="text-[10px] text-gray-500 mt-1 text-right">{formatTime(message.createdAt)}</div>
|
|
1279
|
+
) : null}
|
|
1265
1280
|
</div>
|
|
1266
1281
|
);
|
|
1267
1282
|
};
|
|
@@ -1271,6 +1286,7 @@ export const ModernMessageGroupComponent: React.FC<ModernMessageGroupProps> = ({
|
|
|
1271
1286
|
currentUser,
|
|
1272
1287
|
showAuthorName = false,
|
|
1273
1288
|
showAvatar = false,
|
|
1289
|
+
showTimestamp = false,
|
|
1274
1290
|
onOpen,
|
|
1275
1291
|
onMessageClick,
|
|
1276
1292
|
isDesktopView = false,
|
|
@@ -1304,6 +1320,7 @@ export const ModernMessageGroupComponent: React.FC<ModernMessageGroupProps> = ({
|
|
|
1304
1320
|
isSmallScreen={isSmallScreen}
|
|
1305
1321
|
showAuthorName={showAuthorName}
|
|
1306
1322
|
showAvatar={showAvatar}
|
|
1323
|
+
showTimestamp={showTimestamp}
|
|
1307
1324
|
/>
|
|
1308
1325
|
);
|
|
1309
1326
|
})}
|