@messenger-box/tailwind-ui-inbox 10.0.3-alpha.71 → 10.0.3-alpha.72
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 +4 -0
- package/lib/components/AIAgent/AIAgent.d.ts +14 -0
- package/lib/components/AIAgent/AIAgent.d.ts.map +1 -0
- package/lib/components/AIAgent/AIAgent.js +1148 -0
- package/lib/components/AIAgent/AIAgent.js.map +1 -0
- package/lib/components/AIAgent/index.d.ts +2 -0
- package/lib/components/AIAgent/index.d.ts.map +1 -0
- package/lib/components/InboxMessage/InputComponent.d.ts +9 -0
- package/lib/components/InboxMessage/InputComponent.d.ts.map +1 -0
- package/lib/components/InboxMessage/InputComponent.js +210 -0
- package/lib/components/InboxMessage/InputComponent.js.map +1 -0
- package/lib/components/InboxMessage/MessageInput.d.ts.map +1 -1
- package/lib/components/InboxMessage/MessageInput.js +14 -10
- package/lib/components/InboxMessage/MessageInput.js.map +1 -1
- package/lib/components/InboxMessage/MessageInputComponent.d.ts +9 -0
- package/lib/components/InboxMessage/MessageInputComponent.d.ts.map +1 -0
- package/lib/components/InboxMessage/MessageInputComponent.js +173 -0
- package/lib/components/InboxMessage/MessageInputComponent.js.map +1 -0
- package/lib/components/InboxMessage/Messages.d.ts.map +1 -1
- package/lib/components/InboxMessage/Messages.js +4 -54
- package/lib/components/InboxMessage/Messages.js.map +1 -1
- package/lib/components/InboxMessage/MessagesBuilderUi.d.ts +17 -0
- package/lib/components/InboxMessage/MessagesBuilderUi.d.ts.map +1 -0
- package/lib/components/InboxMessage/MessagesBuilderUi.js +162 -0
- package/lib/components/InboxMessage/MessagesBuilderUi.js.map +1 -0
- package/lib/components/InboxMessage/UploadImageButton.d.ts +1 -0
- package/lib/components/InboxMessage/UploadImageButton.d.ts.map +1 -1
- package/lib/components/InboxMessage/UploadImageButton.js +3 -3
- package/lib/components/InboxMessage/UploadImageButton.js.map +1 -1
- package/lib/components/InboxMessage/index.d.ts +3 -0
- package/lib/components/InboxMessage/index.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/CommonMessage.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/CommonMessage.js +11 -6
- package/lib/components/InboxMessage/message-widgets/CommonMessage.js.map +1 -1
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +14 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +1525 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -0
- package/lib/components/InboxMessage/message-widgets/PlainMessage.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/PlainMessage.js +6 -3
- package/lib/components/InboxMessage/message-widgets/PlainMessage.js.map +1 -1
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js +207 -12
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js.map +1 -1
- package/lib/components/InboxMessage/message-widgets/index.d.ts +1 -0
- package/lib/components/InboxMessage/message-widgets/index.d.ts.map +1 -1
- package/lib/components/index.d.ts +2 -1
- package/lib/components/index.d.ts.map +1 -1
- package/lib/compute.d.ts.map +1 -1
- package/lib/compute.js +79 -3
- package/lib/compute.js.map +1 -1
- package/lib/config/env-config.d.ts +6 -0
- package/lib/config/env-config.d.ts.map +1 -1
- package/lib/config/env-config.js +19 -1
- package/lib/config/env-config.js.map +1 -1
- package/lib/container/AiInbox.d.ts +15 -0
- package/lib/container/AiInbox.d.ts.map +1 -0
- package/lib/container/AiInbox.js +1520 -0
- package/lib/container/AiInbox.js.map +1 -0
- package/lib/container/AiInboxWithLoader.d.ts +36 -0
- package/lib/container/AiInboxWithLoader.d.ts.map +1 -0
- package/lib/container/AiInboxWithLoader.js +300 -0
- package/lib/container/AiInboxWithLoader.js.map +1 -0
- package/lib/container/AiLandingInput.d.ts +4 -0
- package/lib/container/AiLandingInput.d.ts.map +1 -0
- package/lib/container/AiLandingInput.js +164 -0
- package/lib/container/AiLandingInput.js.map +1 -0
- package/lib/container/Inbox.d.ts.map +1 -1
- package/lib/container/Inbox.js +6 -4
- package/lib/container/Inbox.js.map +1 -1
- package/lib/container/InboxAiMessagesLoader.d.ts +36 -0
- package/lib/container/InboxAiMessagesLoader.d.ts.map +1 -0
- package/lib/container/InboxAiMessagesLoader.js +47 -0
- package/lib/container/InboxAiMessagesLoader.js.map +1 -0
- package/lib/container/InboxContainer.d.ts +12 -0
- package/lib/container/InboxContainer.d.ts.map +1 -0
- package/lib/container/InboxContainer.js +31 -0
- package/lib/container/InboxContainer.js.map +1 -0
- package/lib/container/InboxTemplate1.d.ts +15 -0
- package/lib/container/InboxTemplate1.d.ts.map +1 -0
- package/lib/container/InboxTemplate1.js +1375 -0
- package/lib/container/InboxTemplate1.js.map +1 -0
- package/lib/container/InboxTemplate1WithLoader.d.ts +36 -0
- package/lib/container/InboxTemplate1WithLoader.d.ts.map +1 -0
- package/lib/container/InboxTemplate2.d.ts +15 -0
- package/lib/container/InboxTemplate2.d.ts.map +1 -0
- package/lib/container/InboxTemplate2.js +1426 -0
- package/lib/container/InboxTemplate2.js.map +1 -0
- package/lib/container/InboxWithAiLoader.d.ts +15 -0
- package/lib/container/InboxWithAiLoader.d.ts.map +1 -0
- package/lib/container/InboxWithAiLoader.js +56 -0
- package/lib/container/InboxWithAiLoader.js.map +1 -0
- 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/container/index.d.ts +4 -1
- package/lib/container/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/machines/aiAgentMachine.d.ts +3 -0
- package/lib/machines/aiAgentMachine.d.ts.map +1 -0
- package/lib/machines/aiAgentMachine.js +1040 -0
- package/lib/machines/aiAgentMachine.js.map +1 -0
- package/lib/machines/types.d.ts +77 -0
- package/lib/machines/types.d.ts.map +1 -0
- package/lib/routes.json +40 -0
- package/lib/templates/InboxWithAi.d.ts +15 -0
- package/lib/templates/InboxWithAi.d.ts.map +1 -0
- package/lib/templates/InboxWithAi.js +405 -0
- package/lib/templates/InboxWithAi.js.map +1 -0
- package/lib/templates/InboxWithAi.tsx +502 -0
- package/package.json +7 -5
- package/src/components/AIAgent/AIAgent.tsx +1351 -0
- package/src/components/AIAgent/README.md +82 -0
- package/src/components/AIAgent/index.ts +1 -0
- package/src/components/InboxMessage/InputComponent.tsx +263 -0
- package/src/components/InboxMessage/MessageInput.tsx +73 -66
- package/src/components/InboxMessage/MessageInputComponent.tsx +245 -0
- package/src/components/InboxMessage/Messages.tsx +2 -56
- package/src/components/InboxMessage/MessagesBuilderUi.tsx +205 -0
- package/src/components/InboxMessage/UploadImageButton.tsx +3 -2
- package/src/components/InboxMessage/index.ts +3 -0
- package/src/components/InboxMessage/message-widgets/CommonMessage.tsx +39 -21
- package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +1968 -0
- package/src/components/InboxMessage/message-widgets/PlainMessage.tsx +6 -2
- package/src/components/InboxMessage/message-widgets/SlackLikeMessageGroup.tsx +306 -54
- package/src/components/InboxMessage/message-widgets/index.ts +1 -0
- package/src/components/index.ts +4 -0
- package/src/compute.ts +83 -2
- package/src/config/env-config.ts +6 -0
- package/src/container/AiInbox.tsx +1796 -0
- package/src/container/AiInboxWithLoader.tsx +356 -0
- package/src/container/AiLandingInput.tsx +168 -0
- package/src/container/Inbox.tsx +8 -5
- package/src/container/InboxAiMessagesLoader.tsx +68 -0
- package/src/container/InboxContainer.tsx +35 -0
- package/src/container/InboxTemplate1.tsx +1542 -0
- package/src/container/InboxTemplate1WithLoader.tsx +338 -0
- package/src/container/InboxTemplate2.tsx +1606 -0
- package/src/container/InboxWithAiLoader.tsx +76 -0
- package/src/container/index.ts +15 -1
- package/src/machines/aiAgentMachine.ts +1248 -0
- package/src/machines/types.ts +59 -0
- package/src/templates/InboxWithAi.tsx +502 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# AI Agent Integration
|
|
2
|
+
|
|
3
|
+
This component integrates an AI-powered chat agent using XState state machines to provide intelligent conversation capabilities within the messenger inbox.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **AI-Powered Conversations**: Uses Claude AI through the XState machine
|
|
8
|
+
- **Multi-Message Responses**: Automatically breaks down complex queries into multiple messages
|
|
9
|
+
- **Real-Time Data**: Integrates with MCP (Model Context Protocol) for web search and news
|
|
10
|
+
- **Smart Query Detection**: Automatically detects when queries need multi-message responses or real-time data
|
|
11
|
+
- **Toggle Mode**: Easy switch between regular messaging and AI agent mode
|
|
12
|
+
|
|
13
|
+
## Components
|
|
14
|
+
|
|
15
|
+
### AIAgent
|
|
16
|
+
|
|
17
|
+
The main AI agent component that handles:
|
|
18
|
+
|
|
19
|
+
- Message display and input
|
|
20
|
+
- AI response generation
|
|
21
|
+
- Multi-message planning and execution
|
|
22
|
+
- Real-time data integration
|
|
23
|
+
- Error handling and retry mechanisms
|
|
24
|
+
|
|
25
|
+
### aiAgentMachine
|
|
26
|
+
|
|
27
|
+
XState machine that manages:
|
|
28
|
+
|
|
29
|
+
- Message flow and state transitions
|
|
30
|
+
- AI response generation
|
|
31
|
+
- Multi-message planning
|
|
32
|
+
- MCP data fetching
|
|
33
|
+
- Error handling
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
The AI Agent is integrated into the main inbox interface with a toggle switch. When enabled:
|
|
38
|
+
|
|
39
|
+
1. **Toggle AI Mode**: Use the toggle switch in the header to enable AI agent mode
|
|
40
|
+
2. **Chat with AI**: Send messages to the AI agent and receive intelligent responses
|
|
41
|
+
3. **Multi-Message**: Complex queries automatically trigger multi-message responses
|
|
42
|
+
4. **Real-Time Data**: Queries needing current information automatically fetch web search and news data
|
|
43
|
+
|
|
44
|
+
## Configuration
|
|
45
|
+
|
|
46
|
+
The AI Agent requires:
|
|
47
|
+
|
|
48
|
+
- Anthropic API key configured on the server
|
|
49
|
+
- MCP server running for real-time data
|
|
50
|
+
- XState and @xstate/react packages
|
|
51
|
+
|
|
52
|
+
## States
|
|
53
|
+
|
|
54
|
+
The AI Agent machine has several states:
|
|
55
|
+
|
|
56
|
+
- `idle`: Waiting for user input
|
|
57
|
+
- `analyzing`: Determining response strategy
|
|
58
|
+
- `planningMultiMessage`: Creating multi-message plan
|
|
59
|
+
- `multiProcessing`: Generating multi-message responses
|
|
60
|
+
- `fetchingData`: Retrieving real-time data
|
|
61
|
+
- `processing`: Generating AI response
|
|
62
|
+
- `error`: Handling errors
|
|
63
|
+
|
|
64
|
+
## Events
|
|
65
|
+
|
|
66
|
+
Supported events:
|
|
67
|
+
|
|
68
|
+
- `SEND_MESSAGE`: Send a user message
|
|
69
|
+
- `FORCE_MULTI_MESSAGE`: Force multi-message response
|
|
70
|
+
- `INPUT_CHANGE`: Update input field
|
|
71
|
+
- `RETRY`: Retry failed operations
|
|
72
|
+
- `CLEAR_ERROR`: Clear error state
|
|
73
|
+
|
|
74
|
+
## Integration Points
|
|
75
|
+
|
|
76
|
+
The AI Agent integrates with:
|
|
77
|
+
|
|
78
|
+
- Main inbox interface
|
|
79
|
+
- Message handling system
|
|
80
|
+
- User authentication
|
|
81
|
+
- Channel management
|
|
82
|
+
- File upload system
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AIAgent } from './AIAgent';
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState, useRef, useEffect } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { config } from '../../config';
|
|
4
|
+
import { UploadImageButton } from './UploadImageButton';
|
|
5
|
+
import { FilesList } from '../inbox';
|
|
6
|
+
|
|
7
|
+
type MessageInputProps = {
|
|
8
|
+
channelId?: string;
|
|
9
|
+
handleSend?: (message: string, files: File[]) => Promise<void>;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const InputComponent = ({ handleSend: handleSendProp, placeholder }: MessageInputProps) => {
|
|
14
|
+
const [message, setMessage] = useState('');
|
|
15
|
+
const [sending, setSending] = useState(false);
|
|
16
|
+
const [files, setFiles] = useState<File[]>([]);
|
|
17
|
+
const [showToast, setShowToast] = useState(false);
|
|
18
|
+
const [toastMessage, setToastMessage] = useState('');
|
|
19
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
20
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
21
|
+
const { t } = useTranslation('translations');
|
|
22
|
+
|
|
23
|
+
// Auto-focus the textarea when component mounts
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (textareaRef.current) {
|
|
26
|
+
textareaRef.current.focus();
|
|
27
|
+
}
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
const showToastMessage = useCallback((message: string) => {
|
|
31
|
+
setToastMessage(message);
|
|
32
|
+
setShowToast(true);
|
|
33
|
+
setTimeout(() => setShowToast(false), 3000);
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
const handleSend = useCallback(() => {
|
|
37
|
+
if (!message.trim() && files.length === 0) return;
|
|
38
|
+
|
|
39
|
+
setSending(true);
|
|
40
|
+
handleSendProp(message, files)
|
|
41
|
+
.then(() => {
|
|
42
|
+
setMessage('');
|
|
43
|
+
setFiles([]);
|
|
44
|
+
// Auto-focus the textarea after sending a message
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
if (textareaRef.current) {
|
|
47
|
+
textareaRef.current.focus();
|
|
48
|
+
}
|
|
49
|
+
}, 100);
|
|
50
|
+
})
|
|
51
|
+
.finally(() => setSending(false));
|
|
52
|
+
}, [files, handleSendProp, message]);
|
|
53
|
+
|
|
54
|
+
const handleKeyDown = useCallback(
|
|
55
|
+
(e) => {
|
|
56
|
+
const keyCode = e.which || e.keyCode;
|
|
57
|
+
if (keyCode == 13 && !e.shiftKey) {
|
|
58
|
+
e.preventDefault();
|
|
59
|
+
handleSend();
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
[handleSend],
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const inputHeight = useMemo(() => {
|
|
66
|
+
const lines = message.split('\n').length;
|
|
67
|
+
return Math.max(120, Math.min(200, 120 + (lines - 1) * 20));
|
|
68
|
+
}, [message]);
|
|
69
|
+
|
|
70
|
+
const onUploadImageChange = useCallback(
|
|
71
|
+
({ target }) => {
|
|
72
|
+
let fileList = [];
|
|
73
|
+
let index = 0;
|
|
74
|
+
if (files.length + target.files.length > config.FILES_PER_MESSAGE) {
|
|
75
|
+
showToastMessage(
|
|
76
|
+
t('tailwind_ui_inbox.you_can_not_upload_more_than_files', {
|
|
77
|
+
files_per_message: config.FILES_PER_MESSAGE,
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
while (target.files[index]) {
|
|
83
|
+
fileList.push(target.files[index]);
|
|
84
|
+
index += 1;
|
|
85
|
+
}
|
|
86
|
+
setFiles((oldFiles) => [...oldFiles, ...fileList]);
|
|
87
|
+
},
|
|
88
|
+
[setFiles, files, showToastMessage, t],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const canSend = message.trim() || files.length > 0;
|
|
92
|
+
const hasContent = message.trim().length > 0;
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div className="bg-gray-50 border-t border-gray-200">
|
|
96
|
+
{/* Toast notification */}
|
|
97
|
+
{showToast && (
|
|
98
|
+
<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">
|
|
99
|
+
<div className="flex items-center">
|
|
100
|
+
<svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
|
|
101
|
+
<path
|
|
102
|
+
fillRule="evenodd"
|
|
103
|
+
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
|
|
104
|
+
clipRule="evenodd"
|
|
105
|
+
/>
|
|
106
|
+
</svg>
|
|
107
|
+
{toastMessage}
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
|
|
112
|
+
{/* Files list */}
|
|
113
|
+
{files?.length > 0 && !sending && (
|
|
114
|
+
<div className="px-4 py-3 border-b border-gray-200">
|
|
115
|
+
<FilesList files={files} />
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
|
|
119
|
+
{/* Input container with textarea first, toolbar at bottom */}
|
|
120
|
+
<div className="px-4 py-3">
|
|
121
|
+
{/* Input field */}
|
|
122
|
+
<div className="relative mb-3">
|
|
123
|
+
<textarea
|
|
124
|
+
ref={textareaRef}
|
|
125
|
+
className={`w-full text-base bg-white border-2 rounded-2xl pl-4 pr-4 py-3 resize-none focus:outline-none placeholder-gray-500 transition-all duration-200 ${
|
|
126
|
+
isFocused ? 'border-blue-500 ring-2 ring-blue-200' : 'border-gray-300'
|
|
127
|
+
}`}
|
|
128
|
+
style={{
|
|
129
|
+
height: `${inputHeight}px`,
|
|
130
|
+
minHeight: '120px',
|
|
131
|
+
maxHeight: '200px',
|
|
132
|
+
}}
|
|
133
|
+
placeholder={placeholder || 'Message'}
|
|
134
|
+
value={sending ? '' : message}
|
|
135
|
+
onKeyDown={handleKeyDown}
|
|
136
|
+
onChange={(e) => setMessage(e.target.value)}
|
|
137
|
+
onFocus={() => setIsFocused(true)}
|
|
138
|
+
onBlur={() => setIsFocused(false)}
|
|
139
|
+
disabled={sending}
|
|
140
|
+
rows={1}
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
{/* Toolbar buttons row at bottom */}
|
|
145
|
+
<div className="flex items-center gap-2">
|
|
146
|
+
<UploadImageButton onChange={onUploadImageChange}>
|
|
147
|
+
<div className="flex items-center justify-center w-8 h-8 rounded-lg border border-gray-300 bg-white hover:bg-gray-50 transition-colors">
|
|
148
|
+
<svg
|
|
149
|
+
className="w-4 h-4 text-gray-600"
|
|
150
|
+
fill="none"
|
|
151
|
+
stroke="currentColor"
|
|
152
|
+
viewBox="0 0 24 24"
|
|
153
|
+
>
|
|
154
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
155
|
+
</svg>
|
|
156
|
+
</div>
|
|
157
|
+
</UploadImageButton>
|
|
158
|
+
|
|
159
|
+
<button className="flex items-center justify-center w-8 h-8 rounded-lg border border-gray-300 bg-white hover:bg-gray-50 transition-colors">
|
|
160
|
+
<svg className="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
161
|
+
<path
|
|
162
|
+
strokeLinecap="round"
|
|
163
|
+
strokeLinejoin="round"
|
|
164
|
+
strokeWidth={2}
|
|
165
|
+
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
166
|
+
/>
|
|
167
|
+
</svg>
|
|
168
|
+
</button>
|
|
169
|
+
|
|
170
|
+
<button className="flex items-center justify-center w-8 h-8 rounded-lg border border-gray-300 bg-white hover:bg-gray-50 transition-colors">
|
|
171
|
+
<svg className="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
172
|
+
<path
|
|
173
|
+
strokeLinecap="round"
|
|
174
|
+
strokeLinejoin="round"
|
|
175
|
+
strokeWidth={2}
|
|
176
|
+
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
|
|
177
|
+
/>
|
|
178
|
+
</svg>
|
|
179
|
+
</button>
|
|
180
|
+
|
|
181
|
+
<button className="flex items-center justify-center w-8 h-8 rounded-lg border border-gray-300 bg-white hover:bg-gray-50 transition-colors">
|
|
182
|
+
<svg className="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
183
|
+
<path
|
|
184
|
+
strokeLinecap="round"
|
|
185
|
+
strokeLinejoin="round"
|
|
186
|
+
strokeWidth={2}
|
|
187
|
+
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
|
188
|
+
/>
|
|
189
|
+
</svg>
|
|
190
|
+
</button>
|
|
191
|
+
|
|
192
|
+
<div className="flex-1"></div>
|
|
193
|
+
|
|
194
|
+
<button className="flex items-center justify-center w-8 h-8 rounded-lg border border-gray-300 bg-white hover:bg-gray-50 transition-colors">
|
|
195
|
+
<svg className="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
196
|
+
<path
|
|
197
|
+
strokeLinecap="round"
|
|
198
|
+
strokeLinejoin="round"
|
|
199
|
+
strokeWidth={2}
|
|
200
|
+
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"
|
|
201
|
+
/>
|
|
202
|
+
<path
|
|
203
|
+
strokeLinecap="round"
|
|
204
|
+
strokeLinejoin="round"
|
|
205
|
+
strokeWidth={2}
|
|
206
|
+
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
207
|
+
/>
|
|
208
|
+
</svg>
|
|
209
|
+
</button>
|
|
210
|
+
|
|
211
|
+
{/* Send button - updated to handle sending messages */}
|
|
212
|
+
<button
|
|
213
|
+
className={`flex items-center justify-center w-8 h-8 rounded-lg border transition-colors ${
|
|
214
|
+
canSend && !sending
|
|
215
|
+
? 'border-blue-500 bg-blue-500 hover:bg-blue-600 text-white'
|
|
216
|
+
: 'border-gray-300 bg-gray-100 text-gray-400 cursor-not-allowed'
|
|
217
|
+
}`}
|
|
218
|
+
onClick={handleSend}
|
|
219
|
+
disabled={!canSend || sending}
|
|
220
|
+
type="button"
|
|
221
|
+
>
|
|
222
|
+
{sending ? (
|
|
223
|
+
<svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
|
224
|
+
<circle
|
|
225
|
+
className="opacity-25"
|
|
226
|
+
cx="12"
|
|
227
|
+
cy="12"
|
|
228
|
+
r="10"
|
|
229
|
+
stroke="currentColor"
|
|
230
|
+
strokeWidth="4"
|
|
231
|
+
></circle>
|
|
232
|
+
<path
|
|
233
|
+
className="opacity-75"
|
|
234
|
+
fill="currentColor"
|
|
235
|
+
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"
|
|
236
|
+
></path>
|
|
237
|
+
</svg>
|
|
238
|
+
) : (
|
|
239
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
240
|
+
<path
|
|
241
|
+
strokeLinecap="round"
|
|
242
|
+
strokeLinejoin="round"
|
|
243
|
+
strokeWidth={2}
|
|
244
|
+
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
|
|
245
|
+
/>
|
|
246
|
+
</svg>
|
|
247
|
+
)}
|
|
248
|
+
</button>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
{/* Beta text and notification icon */}
|
|
253
|
+
{/* <div className="px-4 py-2 bg-gray-50 border-t border-gray-200 flex items-center justify-between">
|
|
254
|
+
<span className="text-sm text-gray-500">This feature is in beta. Send feedback</span>
|
|
255
|
+
<button className="p-1 hover:bg-gray-100 rounded transition-colors">
|
|
256
|
+
<svg className="w-5 h-5 text-gray-500" fill="currentColor" viewBox="0 0 24 24">
|
|
257
|
+
<path d="M12 2C13.1 2 14 2.9 14 4C14 5.1 13.1 6 12 6C10.9 6 10 5.1 10 4C10 2.9 10.9 2 12 2ZM21 19V20H3V19L5 17V11C5 7.9 7.03 5.17 10 4.29C10 4.19 10 4.1 10 4C10 2.9 10.9 2 12 2C13.1 2 14 2.9 14 4C14 4.1 14 4.19 14 4.29C16.97 5.17 19 7.9 19 11V17L21 19ZM14 21C14 22.1 13.1 23 12 23C10.9 23 10 22.1 10 21" />
|
|
258
|
+
</svg>
|
|
259
|
+
</button>
|
|
260
|
+
</div> */}
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
263
|
+
};
|
|
@@ -64,7 +64,7 @@ export const MessageInput = ({ handleSend: handleSendProp, placeholder }: Messag
|
|
|
64
64
|
|
|
65
65
|
const inputHeight = useMemo(() => {
|
|
66
66
|
const lines = message.split('\n').length;
|
|
67
|
-
return Math.max(
|
|
67
|
+
return Math.max(40, Math.min(120, 40 + (lines - 1) * 20));
|
|
68
68
|
}, [message]);
|
|
69
69
|
|
|
70
70
|
const onUploadImageChange = useCallback(
|
|
@@ -117,73 +117,80 @@ export const MessageInput = ({ handleSend: handleSendProp, placeholder }: Messag
|
|
|
117
117
|
|
|
118
118
|
<div className="p-4">
|
|
119
119
|
<div
|
|
120
|
-
className={`relative bg-
|
|
121
|
-
isFocused
|
|
120
|
+
className={`relative bg-white border rounded-lg transition-all duration-200 ease-in-out ${
|
|
121
|
+
isFocused
|
|
122
|
+
? 'ring-2 ring-blue-500 ring-opacity-50 border-blue-300 shadow-sm'
|
|
123
|
+
: 'border-gray-300 hover:border-gray-400'
|
|
122
124
|
}`}
|
|
123
125
|
>
|
|
124
|
-
{/*
|
|
125
|
-
<div className="
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
className="
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
126
|
+
{/* Input container with flex layout like Slack */}
|
|
127
|
+
<div className="flex items-end gap-2 p-2">
|
|
128
|
+
{/* Upload button - positioned like Slack */}
|
|
129
|
+
<div className="flex-shrink-0 self-end pb-1">
|
|
130
|
+
<UploadImageButton onChange={onUploadImageChange} />
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
{/* Textarea container */}
|
|
134
|
+
<div className="flex-grow min-w-0">
|
|
135
|
+
<textarea
|
|
136
|
+
ref={textareaRef}
|
|
137
|
+
className="w-full text-base px-3 py-2 bg-transparent border-none resize-none overflow-hidden placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-0 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent"
|
|
138
|
+
style={{
|
|
139
|
+
height: `${inputHeight}px`,
|
|
140
|
+
minHeight: '40px',
|
|
141
|
+
maxHeight: '120px',
|
|
142
|
+
}}
|
|
143
|
+
placeholder={placeholder || t('tailwind_ui_inbox.reminder_leave_a_review')}
|
|
144
|
+
value={sending ? '' : message}
|
|
145
|
+
onKeyDown={handleKeyDown}
|
|
146
|
+
onChange={(e) => setMessage(e.target.value)}
|
|
147
|
+
onFocus={() => setIsFocused(true)}
|
|
148
|
+
onBlur={() => setIsFocused(false)}
|
|
149
|
+
disabled={sending}
|
|
150
|
+
rows={1}
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
{/* Send button - positioned like Slack */}
|
|
155
|
+
<div className="flex-shrink-0 self-end pb-1">
|
|
156
|
+
<button
|
|
157
|
+
className={`w-8 h-8 rounded-md flex items-center justify-center transition-all duration-200 ease-in-out ${
|
|
158
|
+
canSend && !sending
|
|
159
|
+
? 'bg-green-600 hover:bg-green-700 text-white shadow-sm hover:shadow-md'
|
|
160
|
+
: 'bg-gray-200 text-gray-400 cursor-not-allowed'
|
|
161
|
+
}`}
|
|
162
|
+
onClick={handleSend}
|
|
163
|
+
disabled={!canSend || sending}
|
|
164
|
+
type="button"
|
|
165
|
+
>
|
|
166
|
+
{sending ? (
|
|
167
|
+
<svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
|
168
|
+
<circle
|
|
169
|
+
className="opacity-25"
|
|
170
|
+
cx="12"
|
|
171
|
+
cy="12"
|
|
172
|
+
r="10"
|
|
173
|
+
stroke="currentColor"
|
|
174
|
+
strokeWidth="4"
|
|
175
|
+
></circle>
|
|
176
|
+
<path
|
|
177
|
+
className="opacity-75"
|
|
178
|
+
fill="currentColor"
|
|
179
|
+
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"
|
|
180
|
+
></path>
|
|
181
|
+
</svg>
|
|
182
|
+
) : (
|
|
183
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
184
|
+
<path
|
|
185
|
+
strokeLinecap="round"
|
|
186
|
+
strokeLinejoin="round"
|
|
187
|
+
strokeWidth={2}
|
|
188
|
+
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
|
|
189
|
+
/>
|
|
190
|
+
</svg>
|
|
191
|
+
)}
|
|
192
|
+
</button>
|
|
193
|
+
</div>
|
|
187
194
|
</div>
|
|
188
195
|
</div>
|
|
189
196
|
|