@messenger-box/tailwind-ui-inbox 10.0.3-alpha.71 → 10.0.3-alpha.73

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 (143) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/components/AIAgent/AIAgent.d.ts +14 -0
  3. package/lib/components/AIAgent/AIAgent.d.ts.map +1 -0
  4. package/lib/components/AIAgent/AIAgent.js +1148 -0
  5. package/lib/components/AIAgent/AIAgent.js.map +1 -0
  6. package/lib/components/AIAgent/index.d.ts +2 -0
  7. package/lib/components/AIAgent/index.d.ts.map +1 -0
  8. package/lib/components/InboxMessage/InputComponent.d.ts +9 -0
  9. package/lib/components/InboxMessage/InputComponent.d.ts.map +1 -0
  10. package/lib/components/InboxMessage/InputComponent.js +210 -0
  11. package/lib/components/InboxMessage/InputComponent.js.map +1 -0
  12. package/lib/components/InboxMessage/MessageInput.d.ts.map +1 -1
  13. package/lib/components/InboxMessage/MessageInput.js +14 -10
  14. package/lib/components/InboxMessage/MessageInput.js.map +1 -1
  15. package/lib/components/InboxMessage/MessageInputComponent.d.ts +9 -0
  16. package/lib/components/InboxMessage/MessageInputComponent.d.ts.map +1 -0
  17. package/lib/components/InboxMessage/Messages.d.ts.map +1 -1
  18. package/lib/components/InboxMessage/Messages.js +4 -54
  19. package/lib/components/InboxMessage/Messages.js.map +1 -1
  20. package/lib/components/InboxMessage/MessagesBuilderUi.d.ts +17 -0
  21. package/lib/components/InboxMessage/MessagesBuilderUi.d.ts.map +1 -0
  22. package/lib/components/InboxMessage/UploadImageButton.d.ts +1 -0
  23. package/lib/components/InboxMessage/UploadImageButton.d.ts.map +1 -1
  24. package/lib/components/InboxMessage/UploadImageButton.js +3 -3
  25. package/lib/components/InboxMessage/UploadImageButton.js.map +1 -1
  26. package/lib/components/InboxMessage/index.d.ts +3 -0
  27. package/lib/components/InboxMessage/index.d.ts.map +1 -1
  28. package/lib/components/InboxMessage/message-widgets/CommonMessage.d.ts.map +1 -1
  29. package/lib/components/InboxMessage/message-widgets/CommonMessage.js +11 -6
  30. package/lib/components/InboxMessage/message-widgets/CommonMessage.js.map +1 -1
  31. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +14 -0
  32. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -0
  33. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +1525 -0
  34. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -0
  35. package/lib/components/InboxMessage/message-widgets/PlainMessage.d.ts.map +1 -1
  36. package/lib/components/InboxMessage/message-widgets/PlainMessage.js +6 -3
  37. package/lib/components/InboxMessage/message-widgets/PlainMessage.js.map +1 -1
  38. package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.d.ts.map +1 -1
  39. package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js +207 -12
  40. package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js.map +1 -1
  41. package/lib/components/InboxMessage/message-widgets/index.d.ts +1 -0
  42. package/lib/components/InboxMessage/message-widgets/index.d.ts.map +1 -1
  43. package/lib/components/index.d.ts +2 -1
  44. package/lib/components/index.d.ts.map +1 -1
  45. package/lib/compute.d.ts.map +1 -1
  46. package/lib/compute.js +79 -3
  47. package/lib/compute.js.map +1 -1
  48. package/lib/config/env-config.d.ts +6 -0
  49. package/lib/config/env-config.d.ts.map +1 -1
  50. package/lib/config/env-config.js +19 -1
  51. package/lib/config/env-config.js.map +1 -1
  52. package/lib/container/AiInbox.d.ts +15 -0
  53. package/lib/container/AiInbox.d.ts.map +1 -0
  54. package/lib/container/AiInboxWithLoader.d.ts +36 -0
  55. package/lib/container/AiInboxWithLoader.d.ts.map +1 -0
  56. package/lib/container/AiLandingInput.d.ts +4 -0
  57. package/lib/container/AiLandingInput.d.ts.map +1 -0
  58. package/lib/container/AiLandingInput.js +164 -0
  59. package/lib/container/AiLandingInput.js.map +1 -0
  60. package/lib/container/Inbox.d.ts.map +1 -1
  61. package/lib/container/Inbox.js +6 -4
  62. package/lib/container/Inbox.js.map +1 -1
  63. package/lib/container/InboxAiMessagesLoader.d.ts +36 -0
  64. package/lib/container/InboxAiMessagesLoader.d.ts.map +1 -0
  65. package/lib/container/InboxAiMessagesLoader.js +44 -0
  66. package/lib/container/InboxAiMessagesLoader.js.map +1 -0
  67. package/lib/container/InboxContainer.d.ts +12 -0
  68. package/lib/container/InboxContainer.d.ts.map +1 -0
  69. package/lib/container/InboxContainer.js +31 -0
  70. package/lib/container/InboxContainer.js.map +1 -0
  71. package/lib/container/InboxTemplate1.d.ts +15 -0
  72. package/lib/container/InboxTemplate1.d.ts.map +1 -0
  73. package/lib/container/InboxTemplate1WithLoader.d.ts +36 -0
  74. package/lib/container/InboxTemplate1WithLoader.d.ts.map +1 -0
  75. package/lib/container/InboxTemplate2.d.ts +15 -0
  76. package/lib/container/InboxTemplate2.d.ts.map +1 -0
  77. package/lib/container/InboxWithAiLoader.d.ts +15 -0
  78. package/lib/container/InboxWithAiLoader.d.ts.map +1 -0
  79. package/lib/container/InboxWithAiLoader.js +56 -0
  80. package/lib/container/InboxWithAiLoader.js.map +1 -0
  81. package/lib/container/ServiceInbox.js +1 -1
  82. package/lib/container/ServiceInbox.js.map +1 -1
  83. package/lib/container/ThreadMessages.js +1 -1
  84. package/lib/container/ThreadMessages.js.map +1 -1
  85. package/lib/container/ThreadMessagesInbox.js +1 -1
  86. package/lib/container/ThreadMessagesInbox.js.map +1 -1
  87. package/lib/container/Threads.js +1 -1
  88. package/lib/container/Threads.js.map +1 -1
  89. package/lib/container/index.d.ts +4 -1
  90. package/lib/container/index.d.ts.map +1 -1
  91. package/lib/index.d.ts +3 -2
  92. package/lib/index.d.ts.map +1 -1
  93. package/lib/index.js +1 -1
  94. package/lib/machines/aiAgentMachine.d.ts +3 -0
  95. package/lib/machines/aiAgentMachine.d.ts.map +1 -0
  96. package/lib/machines/aiAgentMachine.js +1040 -0
  97. package/lib/machines/aiAgentMachine.js.map +1 -0
  98. package/lib/machines/types.d.ts +77 -0
  99. package/lib/machines/types.d.ts.map +1 -0
  100. package/lib/routes.json +40 -0
  101. package/lib/templates/InboxWithAi.d.ts +15 -0
  102. package/lib/templates/InboxWithAi.d.ts.map +1 -0
  103. package/lib/templates/InboxWithAi.js +405 -0
  104. package/lib/templates/InboxWithAi.js.map +1 -0
  105. package/lib/templates/InboxWithAi.tsx +502 -0
  106. package/lib/templates/index.d.ts +2 -0
  107. package/lib/templates/index.d.ts.map +1 -0
  108. package/lib/templates/index.ts +1 -0
  109. package/package.json +7 -5
  110. package/src/components/AIAgent/AIAgent.tsx +1351 -0
  111. package/src/components/AIAgent/README.md +82 -0
  112. package/src/components/AIAgent/index.ts +1 -0
  113. package/src/components/InboxMessage/InputComponent.tsx +263 -0
  114. package/src/components/InboxMessage/MessageInput.tsx +73 -66
  115. package/src/components/InboxMessage/MessageInputComponent.tsx +245 -0
  116. package/src/components/InboxMessage/Messages.tsx +2 -56
  117. package/src/components/InboxMessage/MessagesBuilderUi.tsx +205 -0
  118. package/src/components/InboxMessage/UploadImageButton.tsx +3 -2
  119. package/src/components/InboxMessage/index.ts +3 -0
  120. package/src/components/InboxMessage/message-widgets/CommonMessage.tsx +39 -21
  121. package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +1968 -0
  122. package/src/components/InboxMessage/message-widgets/PlainMessage.tsx +6 -2
  123. package/src/components/InboxMessage/message-widgets/SlackLikeMessageGroup.tsx +306 -54
  124. package/src/components/InboxMessage/message-widgets/index.ts +1 -0
  125. package/src/components/index.ts +4 -0
  126. package/src/compute.ts +83 -2
  127. package/src/config/env-config.ts +6 -0
  128. package/src/container/AiInbox.tsx +1796 -0
  129. package/src/container/AiInboxWithLoader.tsx +356 -0
  130. package/src/container/AiLandingInput.tsx +168 -0
  131. package/src/container/Inbox.tsx +8 -5
  132. package/src/container/InboxAiMessagesLoader.tsx +58 -0
  133. package/src/container/InboxContainer.tsx +35 -0
  134. package/src/container/InboxTemplate1.tsx +1542 -0
  135. package/src/container/InboxTemplate1WithLoader.tsx +338 -0
  136. package/src/container/InboxTemplate2.tsx +1606 -0
  137. package/src/container/InboxWithAiLoader.tsx +76 -0
  138. package/src/container/index.ts +21 -1
  139. package/src/index.ts +12 -1
  140. package/src/machines/aiAgentMachine.ts +1248 -0
  141. package/src/machines/types.ts +59 -0
  142. package/src/templates/InboxWithAi.tsx +502 -0
  143. package/src/templates/index.ts +1 -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(48, Math.min(120, 48 + (lines - 1) * 20));
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-gray-50 rounded-2xl transition-all duration-200 ease-in-out ${
121
- isFocused ? 'ring-2 ring-blue-500 ring-opacity-50 bg-white shadow-sm' : 'hover:bg-gray-100'
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
- {/* Upload button */}
125
- <div className="absolute left-3 bottom-3 z-10">
126
- <UploadImageButton onChange={onUploadImageChange} />
127
- </div>
128
-
129
- {/* Textarea */}
130
- <textarea
131
- ref={textareaRef}
132
- className="w-full text-base pl-14 pr-20 py-3 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"
133
- style={{
134
- height: `${inputHeight}px`,
135
- minHeight: '48px',
136
- maxHeight: '120px',
137
- }}
138
- placeholder={placeholder || t('tailwind_ui_inbox.reminder_leave_a_review')}
139
- value={sending ? '' : message}
140
- onKeyDown={handleKeyDown}
141
- onChange={(e) => setMessage(e.target.value)}
142
- onFocus={() => setIsFocused(true)}
143
- onBlur={() => setIsFocused(false)}
144
- disabled={sending}
145
- rows={1}
146
- />
147
-
148
- {/* Send button */}
149
- <div className="absolute right-2 bottom-2">
150
- <button
151
- className={`w-8 h-8 rounded-full flex items-center justify-center transition-all duration-200 ease-in-out ${
152
- canSend && !sending
153
- ? 'bg-blue-500 hover:bg-blue-600 text-white shadow-md hover:shadow-lg transform hover:scale-105 active:scale-95'
154
- : 'bg-gray-200 text-gray-400 cursor-not-allowed'
155
- }`}
156
- onClick={handleSend}
157
- disabled={!canSend || sending}
158
- type="button"
159
- >
160
- {sending ? (
161
- <svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
162
- <circle
163
- className="opacity-25"
164
- cx="12"
165
- cy="12"
166
- r="10"
167
- stroke="currentColor"
168
- strokeWidth="4"
169
- ></circle>
170
- <path
171
- className="opacity-75"
172
- fill="currentColor"
173
- 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"
174
- ></path>
175
- </svg>
176
- ) : (
177
- <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
178
- <path
179
- strokeLinecap="round"
180
- strokeLinejoin="round"
181
- strokeWidth={2}
182
- d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
183
- />
184
- </svg>
185
- )}
186
- </button>
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