@messenger-box/tailwind-ui-inbox 10.0.3-alpha.70 → 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 +8 -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/LeftSidebar.d.ts +2 -0
- package/lib/components/InboxMessage/LeftSidebar.d.ts.map +1 -1
- package/lib/components/InboxMessage/LeftSidebar.js +16 -5
- package/lib/components/InboxMessage/LeftSidebar.js.map +1 -1
- 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 +3 -1
- package/lib/components/InboxMessage/Messages.d.ts.map +1 -1
- package/lib/components/InboxMessage/Messages.js +16 -60
- 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 +2 -0
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js +225 -26
- 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 +177 -58
- 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/LeftSidebar.tsx +14 -4
- package/src/components/InboxMessage/MessageInput.tsx +73 -66
- package/src/components/InboxMessage/MessageInputComponent.tsx +245 -0
- package/src/components/InboxMessage/Messages.tsx +24 -68
- 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 +337 -53
- 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 +280 -130
- 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
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
interface IPlainMessageProps {
|
|
4
|
-
message: string;
|
|
4
|
+
message: string; // may contain HTML
|
|
5
5
|
index: number;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export const PlainMessage = ({ message, index }: IPlainMessageProps) => {
|
|
9
9
|
return (
|
|
10
10
|
<div className="w-full flex justify-center mb-7" key={`msgList_${index}`}>
|
|
11
|
-
<p className="text-sm font-bold">{message}</p>
|
|
11
|
+
{/* <p className="text-sm font-bold">{message}</p> */}
|
|
12
|
+
<div
|
|
13
|
+
className="text-sm font-bold whitespace-pre-wrap prose prose-sm max-w-none"
|
|
14
|
+
dangerouslySetInnerHTML={{ __html: message }}
|
|
15
|
+
/>
|
|
12
16
|
</div>
|
|
13
17
|
);
|
|
14
18
|
};
|
|
@@ -8,6 +8,8 @@ interface SlackLikeMessageGroupProps {
|
|
|
8
8
|
currentUser: IAuthUser;
|
|
9
9
|
onOpen: (element?: any) => void;
|
|
10
10
|
onMessageClick: (msg: IPost) => void;
|
|
11
|
+
isDesktopView?: boolean;
|
|
12
|
+
isSmallScreen?: boolean;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
interface MessageGroupProps {
|
|
@@ -16,6 +18,8 @@ interface MessageGroupProps {
|
|
|
16
18
|
currentUser: IAuthUser;
|
|
17
19
|
onOpen: (element?: any) => void;
|
|
18
20
|
onMessageClick: (msg: IPost) => void;
|
|
21
|
+
isDesktopView?: boolean;
|
|
22
|
+
isSmallScreen?: boolean;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
// Utility function to group messages by user and time
|
|
@@ -53,7 +57,238 @@ export const groupMessagesByUserAndTime = (messages: IPost[], timeThresholdMinut
|
|
|
53
57
|
return groups;
|
|
54
58
|
};
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
// Detect if a string likely contains HTML
|
|
61
|
+
const isProbablyHTML = (value: string): boolean => {
|
|
62
|
+
if (!value) return false;
|
|
63
|
+
return /<\/?[a-z][\s\S]*>/i.test(value.trim());
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Minimal sanitizer without extra deps
|
|
67
|
+
const sanitizeHtml = (html: string): string => {
|
|
68
|
+
try {
|
|
69
|
+
if (typeof window === 'undefined' || typeof window.DOMParser === 'undefined') {
|
|
70
|
+
return html
|
|
71
|
+
.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, '')
|
|
72
|
+
.replace(/<style[\s\S]*?>[\s\S]*?<\/style>/gi, '')
|
|
73
|
+
.replace(/on\w+\s*=\s*"[^"]*"/gi, '')
|
|
74
|
+
.replace(/on\w+\s*=\s*'[^']*'/gi, '')
|
|
75
|
+
.replace(/on\w+\s*=\s*[^\s>]+/gi, '')
|
|
76
|
+
.replace(/javascript:/gi, '');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const parser = new window.DOMParser();
|
|
80
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
81
|
+
|
|
82
|
+
const dangerousTags = ['script', 'style', 'iframe', 'object', 'embed', 'link', 'meta'];
|
|
83
|
+
dangerousTags.forEach((tag) => {
|
|
84
|
+
doc.querySelectorAll(tag).forEach((el) => el.remove());
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const walker = doc.createTreeWalker(doc.body, (NodeFilter as any).SHOW_ELEMENT);
|
|
88
|
+
let node = walker.nextNode() as Element | null;
|
|
89
|
+
while (node) {
|
|
90
|
+
Array.from(node.attributes).forEach((attr) => {
|
|
91
|
+
const name = attr.name.toLowerCase();
|
|
92
|
+
const value = (attr.value || '').toLowerCase();
|
|
93
|
+
if (name.startsWith('on') || value.startsWith('javascript:')) {
|
|
94
|
+
node?.removeAttribute(attr.name);
|
|
95
|
+
}
|
|
96
|
+
if ((name === 'src' || name === 'href') && !/^https?:|^\/.*/.test(attr.value)) {
|
|
97
|
+
node?.removeAttribute(attr.name);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
node = walker.nextNode() as Element | null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return doc.body.innerHTML;
|
|
104
|
+
} catch (e) {
|
|
105
|
+
return html.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, '');
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Render markdown-like content with fenced code blocks
|
|
110
|
+
const FormattedMessageContent: React.FC<{ content: string }> = ({ content }) => {
|
|
111
|
+
if (!content) return null;
|
|
112
|
+
// Normalize escaped fences (\``` -> ```)
|
|
113
|
+
// Unescape any backslash-escaped backticks so Markdown fences work
|
|
114
|
+
const normalized = content.replace(/\\`/g, '`');
|
|
115
|
+
const blocks = normalized.split('\n\n').filter((b) => b.trim());
|
|
116
|
+
return (
|
|
117
|
+
<div className="space-y-3">
|
|
118
|
+
{blocks.map((block, index) => {
|
|
119
|
+
const trimmed = block.trim();
|
|
120
|
+
|
|
121
|
+
// Fenced code anywhere in block
|
|
122
|
+
if (trimmed.includes('```')) {
|
|
123
|
+
const parts: React.ReactNode[] = [];
|
|
124
|
+
const pushText = (t: string, key: string) => {
|
|
125
|
+
const txt = (t || '').trim();
|
|
126
|
+
if (!txt) return;
|
|
127
|
+
if (txt.startsWith('# ')) {
|
|
128
|
+
parts.push(
|
|
129
|
+
<h1 key={key} className="text-xl font-semibold text-gray-900">
|
|
130
|
+
{txt.substring(2)}
|
|
131
|
+
</h1>,
|
|
132
|
+
);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (txt.startsWith('## ')) {
|
|
136
|
+
parts.push(
|
|
137
|
+
<h2 key={key} className="text-base font-semibold text-gray-800">
|
|
138
|
+
{txt.substring(3)}
|
|
139
|
+
</h2>,
|
|
140
|
+
);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (txt.startsWith('### ')) {
|
|
144
|
+
parts.push(
|
|
145
|
+
<h3 key={key} className="text-sm font-medium text-gray-700">
|
|
146
|
+
{txt.substring(4)}
|
|
147
|
+
</h3>,
|
|
148
|
+
);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (txt.startsWith('- ') || txt.startsWith('• ')) {
|
|
152
|
+
const items = txt.split('\n').filter((l) => l.trim());
|
|
153
|
+
parts.push(
|
|
154
|
+
<ul key={key} className="list-disc pl-5 space-y-1">
|
|
155
|
+
{items.map((it, i) => (
|
|
156
|
+
<li key={`${key}-li-${i}`} className="text-gray-700">
|
|
157
|
+
{it.replace(/^[\u2212\-•]\s*/, '')}
|
|
158
|
+
</li>
|
|
159
|
+
))}
|
|
160
|
+
</ul>,
|
|
161
|
+
);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (txt.includes('**') || txt.includes('`')) {
|
|
165
|
+
const formatted = txt
|
|
166
|
+
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
167
|
+
.replace(/`([^`]+)`/g, '<code class="bg-gray-100 px-1 rounded">$1</code>');
|
|
168
|
+
parts.push(
|
|
169
|
+
<div
|
|
170
|
+
key={key}
|
|
171
|
+
className="text-gray-700"
|
|
172
|
+
dangerouslySetInnerHTML={{ __html: sanitizeHtml(formatted) }}
|
|
173
|
+
/>,
|
|
174
|
+
);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
parts.push(
|
|
178
|
+
<p key={key} className="text-gray-700 whitespace-pre-wrap">
|
|
179
|
+
{txt}
|
|
180
|
+
</p>,
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const codeRegex = /```([\w-]*)\n?([\s\S]*?)```/g;
|
|
185
|
+
let lastIndex = 0;
|
|
186
|
+
let m: RegExpExecArray | null;
|
|
187
|
+
while ((m = codeRegex.exec(trimmed)) !== null) {
|
|
188
|
+
const [full, lang, code] = m;
|
|
189
|
+
const before = trimmed.slice(lastIndex, m.index);
|
|
190
|
+
pushText(before, `${index}-before-${lastIndex}`);
|
|
191
|
+
const language = (lang || '').trim() || 'code';
|
|
192
|
+
parts.push(
|
|
193
|
+
<div
|
|
194
|
+
key={`${index}-code-${m.index}`}
|
|
195
|
+
className="border border-gray-200 rounded overflow-hidden"
|
|
196
|
+
>
|
|
197
|
+
<div className="px-3 py-1.5 text-[10px] font-semibold uppercase tracking-wide bg-gray-100 text-gray-600">
|
|
198
|
+
{language}
|
|
199
|
+
</div>
|
|
200
|
+
<div className="bg-gray-900 p-3 overflow-x-auto">
|
|
201
|
+
<pre className="text-xs text-gray-100 font-mono leading-relaxed not-prose">
|
|
202
|
+
<code>{code}</code>
|
|
203
|
+
</pre>
|
|
204
|
+
</div>
|
|
205
|
+
</div>,
|
|
206
|
+
);
|
|
207
|
+
lastIndex = m.index + full.length;
|
|
208
|
+
}
|
|
209
|
+
const after = trimmed.slice(lastIndex);
|
|
210
|
+
pushText(after, `${index}-after`);
|
|
211
|
+
return (
|
|
212
|
+
<div key={index} className="space-y-2">
|
|
213
|
+
{parts}
|
|
214
|
+
</div>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (isProbablyHTML(trimmed)) {
|
|
219
|
+
return (
|
|
220
|
+
<div
|
|
221
|
+
key={index}
|
|
222
|
+
className="prose prose-sm max-w-none"
|
|
223
|
+
dangerouslySetInnerHTML={{ __html: sanitizeHtml(trimmed) }}
|
|
224
|
+
/>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (trimmed.startsWith('### '))
|
|
229
|
+
return (
|
|
230
|
+
<h3 key={index} className="text-sm font-medium text-gray-700">
|
|
231
|
+
{trimmed.substring(4)}
|
|
232
|
+
</h3>
|
|
233
|
+
);
|
|
234
|
+
if (trimmed.startsWith('## '))
|
|
235
|
+
return (
|
|
236
|
+
<h2 key={index} className="text-base font-semibold text-gray-800">
|
|
237
|
+
{trimmed.substring(3)}
|
|
238
|
+
</h2>
|
|
239
|
+
);
|
|
240
|
+
if (trimmed.startsWith('# '))
|
|
241
|
+
return (
|
|
242
|
+
<h1 key={index} className="text-xl font-semibold text-gray-900">
|
|
243
|
+
{trimmed.substring(2)}
|
|
244
|
+
</h1>
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
if (trimmed.startsWith('- ') || trimmed.startsWith('• ')) {
|
|
248
|
+
const items = trimmed.split('\n').filter((l) => l.trim());
|
|
249
|
+
return (
|
|
250
|
+
<ul key={index} className="list-disc pl-5 space-y-1">
|
|
251
|
+
{items.map((it, i) => (
|
|
252
|
+
<li key={`${index}-li-${i}`} className="text-gray-700">
|
|
253
|
+
{it.replace(/^[\u2212\-•]\s*/, '')}
|
|
254
|
+
</li>
|
|
255
|
+
))}
|
|
256
|
+
</ul>
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (trimmed.includes('**') || trimmed.includes('`')) {
|
|
261
|
+
const formatted = trimmed
|
|
262
|
+
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
263
|
+
.replace(/`([^`]+)`/g, '<code class="bg-gray-100 px-1 rounded">$1</code>');
|
|
264
|
+
return (
|
|
265
|
+
<div
|
|
266
|
+
key={index}
|
|
267
|
+
className="text-gray-700"
|
|
268
|
+
dangerouslySetInnerHTML={{ __html: sanitizeHtml(formatted) }}
|
|
269
|
+
/>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return (
|
|
274
|
+
<p key={index} className="text-gray-700 whitespace-pre-wrap">
|
|
275
|
+
{trimmed}
|
|
276
|
+
</p>
|
|
277
|
+
);
|
|
278
|
+
})}
|
|
279
|
+
</div>
|
|
280
|
+
);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const MessageGroup: React.FC<MessageGroupProps> = ({
|
|
284
|
+
author,
|
|
285
|
+
messages,
|
|
286
|
+
currentUser,
|
|
287
|
+
onOpen,
|
|
288
|
+
onMessageClick,
|
|
289
|
+
isDesktopView = false,
|
|
290
|
+
isSmallScreen = false,
|
|
291
|
+
}) => {
|
|
57
292
|
const isOwnMessage = author?.id === currentUser?.id;
|
|
58
293
|
const authorName =
|
|
59
294
|
author?.givenName && author?.familyName
|
|
@@ -66,37 +301,60 @@ const MessageGroup: React.FC<MessageGroupProps> = ({ author, messages, currentUs
|
|
|
66
301
|
return format(date, 'h:mm a');
|
|
67
302
|
};
|
|
68
303
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
<img
|
|
75
|
-
className="w-9 h-9 rounded-lg cursor-pointer hover:opacity-80 transition-opacity"
|
|
76
|
-
src={author?.picture || '/default-avatar.svg'}
|
|
77
|
-
alt={authorName}
|
|
78
|
-
onClick={() => onOpen(firstMessage)}
|
|
79
|
-
onError={(e) => {
|
|
80
|
-
e.currentTarget.src = '/default-avatar.svg';
|
|
81
|
-
}}
|
|
82
|
-
/>
|
|
83
|
-
</div>
|
|
304
|
+
// Determine if this is an AI/system message for special styling
|
|
305
|
+
const isSystemMessage =
|
|
306
|
+
author?.username?.toLowerCase().includes('ai') ||
|
|
307
|
+
author?.username?.toLowerCase().includes('assistant') ||
|
|
308
|
+
author?.username?.toLowerCase().includes('system');
|
|
84
309
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
310
|
+
return (
|
|
311
|
+
<div
|
|
312
|
+
className={`group hover:bg-white hover:bg-opacity-60 rounded transition-colors ${
|
|
313
|
+
isDesktopView ? 'mb-8 -mx-6 px-6 py-4' : isSmallScreen ? 'mb-4 -mx-2 px-2 py-2' : 'mb-6 -mx-4 px-4 py-3'
|
|
314
|
+
}`}
|
|
315
|
+
>
|
|
316
|
+
<div
|
|
317
|
+
className={`flex items-start ${
|
|
318
|
+
isDesktopView ? 'space-x-4' : isSmallScreen ? 'space-x-2' : 'space-x-3'
|
|
319
|
+
}`}
|
|
320
|
+
>
|
|
321
|
+
{/* Avatar - Only show for non-system messages */}
|
|
322
|
+
{!isSystemMessage && (
|
|
323
|
+
<div className="flex-shrink-0 mt-0.5">
|
|
324
|
+
<img
|
|
325
|
+
className={`rounded-lg cursor-pointer hover:opacity-80 transition-opacity ${
|
|
326
|
+
isDesktopView ? 'w-12 h-12' : isSmallScreen ? 'w-8 h-8' : 'w-10 h-10'
|
|
327
|
+
}`}
|
|
328
|
+
src={author?.picture || '/default-avatar.svg'}
|
|
329
|
+
alt={authorName}
|
|
330
|
+
onClick={() => onOpen(firstMessage)}
|
|
331
|
+
onError={(e) => {
|
|
332
|
+
e.currentTarget.src = '/default-avatar.svg';
|
|
333
|
+
}}
|
|
334
|
+
/>
|
|
91
335
|
</div>
|
|
336
|
+
)}
|
|
337
|
+
|
|
338
|
+
<div className="flex-1 min-w-0 overflow-hidden">
|
|
339
|
+
{/* Author name and timestamp - Only show for non-system messages */}
|
|
340
|
+
{!isSystemMessage && (
|
|
341
|
+
<div className="flex items-center space-x-2 mb-1">
|
|
342
|
+
<span className="text-sm font-semibold text-gray-900 truncate">{authorName}</span>
|
|
343
|
+
<span className="text-xs text-gray-500 flex-shrink-0">
|
|
344
|
+
{formatTime(firstMessage.createdAt)}
|
|
345
|
+
</span>
|
|
346
|
+
{isOwnMessage && <span className="text-xs text-gray-400 italic flex-shrink-0">(you)</span>}
|
|
347
|
+
</div>
|
|
348
|
+
)}
|
|
92
349
|
|
|
93
350
|
{/* Messages in the group - single line for each message */}
|
|
94
|
-
<div className="space-y-
|
|
351
|
+
<div className="space-y-0.5">
|
|
95
352
|
{messages.map((message, index) => (
|
|
96
353
|
<MessageBubble
|
|
97
354
|
key={message.id}
|
|
98
355
|
message={message}
|
|
99
356
|
isOwnMessage={isOwnMessage}
|
|
357
|
+
isSystemMessage={isSystemMessage}
|
|
100
358
|
isFirstInGroup={index === 0}
|
|
101
359
|
isLastInGroup={index === messages.length - 1}
|
|
102
360
|
showTimestamp={isOwnMessage && index === 0}
|
|
@@ -123,11 +381,13 @@ interface MessageBubbleProps {
|
|
|
123
381
|
totalInGroup: number;
|
|
124
382
|
authorName: string;
|
|
125
383
|
formatTime: (timestamp: string) => string;
|
|
384
|
+
isSystemMessage: boolean;
|
|
126
385
|
}
|
|
127
386
|
|
|
128
387
|
const MessageBubble: React.FC<MessageBubbleProps> = ({
|
|
129
388
|
message,
|
|
130
389
|
isOwnMessage,
|
|
390
|
+
isSystemMessage,
|
|
131
391
|
isFirstInGroup,
|
|
132
392
|
isLastInGroup,
|
|
133
393
|
showTimestamp,
|
|
@@ -137,41 +397,61 @@ const MessageBubble: React.FC<MessageBubbleProps> = ({
|
|
|
137
397
|
formatTime,
|
|
138
398
|
}) => {
|
|
139
399
|
const handleClick = () => {
|
|
140
|
-
|
|
400
|
+
if (!isSystemMessage) {
|
|
401
|
+
onMessageClick?.(message);
|
|
402
|
+
}
|
|
141
403
|
};
|
|
142
404
|
|
|
143
405
|
// All messages use the same format (Slack style - left aligned)
|
|
144
406
|
return (
|
|
145
|
-
<div
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
407
|
+
<div
|
|
408
|
+
className={`py-1 hover:bg-gray-50 hover:bg-opacity-50 rounded px-1 sm:px-2 -mx-1 sm:-mx-2 group ${
|
|
409
|
+
isSystemMessage ? '' : 'cursor-pointer'
|
|
410
|
+
}`}
|
|
411
|
+
>
|
|
412
|
+
<div className="flex items-start justify-between gap-2">
|
|
413
|
+
<div
|
|
414
|
+
className={`text-sm hover:bg-gray-100 px-1 sm:px-2 py-1 rounded flex-1 ${
|
|
415
|
+
isSystemMessage ? 'text-gray-800' : 'text-gray-900 cursor-pointer'
|
|
416
|
+
}`}
|
|
417
|
+
onClick={handleClick}
|
|
418
|
+
>
|
|
419
|
+
{message.message && (
|
|
420
|
+
<div className="break-words leading-relaxed">
|
|
421
|
+
{message.message.includes('```') ? (
|
|
422
|
+
<FormattedMessageContent content={message.message} />
|
|
423
|
+
) : isProbablyHTML(message.message) ? (
|
|
424
|
+
<div
|
|
425
|
+
className="prose prose-sm max-w-none"
|
|
426
|
+
dangerouslySetInnerHTML={{ __html: sanitizeHtml(message.message) }}
|
|
427
|
+
/>
|
|
428
|
+
) : (
|
|
429
|
+
<FormattedMessageContent content={message.message} />
|
|
430
|
+
)}
|
|
431
|
+
</div>
|
|
432
|
+
)}
|
|
155
433
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
{/* Show
|
|
164
|
-
|
|
165
|
-
<div className="text-xs text-gray-400 mt-1 clear-both">
|
|
166
|
-
{message.isDelivered
|
|
167
|
-
? message.isRead
|
|
168
|
-
? '✓✓ Read'
|
|
169
|
-
: '✓✓ Delivered'
|
|
170
|
-
: '✓ Sent'
|
|
171
|
-
}
|
|
172
|
-
</div>
|
|
173
|
-
)} */}
|
|
434
|
+
{message.files?.totalCount > 0 && (
|
|
435
|
+
<div className="mt-2">
|
|
436
|
+
<FilesList uploaded files={message.files.data} />
|
|
437
|
+
</div>
|
|
438
|
+
)}
|
|
439
|
+
</div>
|
|
440
|
+
|
|
441
|
+
{/* Show timestamp to the right of message */}
|
|
442
|
+
<span className="text-xs text-gray-500 flex-shrink-0 mt-1">{formatTime(message.createdAt)}</span>
|
|
174
443
|
</div>
|
|
444
|
+
|
|
445
|
+
{/* Show delivery status for own messages */}
|
|
446
|
+
{/* {isOwnMessage && message.isDelivered !== undefined && (
|
|
447
|
+
<div className="text-xs text-gray-400 mt-1 clear-both">
|
|
448
|
+
{message.isDelivered
|
|
449
|
+
? message.isRead
|
|
450
|
+
? '✓✓ Read'
|
|
451
|
+
: '✓✓ Delivered'
|
|
452
|
+
: '✓ Sent'}
|
|
453
|
+
</div>
|
|
454
|
+
)} */}
|
|
175
455
|
</div>
|
|
176
456
|
);
|
|
177
457
|
};
|
|
@@ -181,6 +461,8 @@ export const SlackLikeMessageGroup: React.FC<SlackLikeMessageGroupProps> = ({
|
|
|
181
461
|
currentUser,
|
|
182
462
|
onOpen,
|
|
183
463
|
onMessageClick,
|
|
464
|
+
isDesktopView = false,
|
|
465
|
+
isSmallScreen = false,
|
|
184
466
|
}) => {
|
|
185
467
|
// Filter out non-message items (like date strings)
|
|
186
468
|
const actualMessages = messages.filter((msg) => typeof msg !== 'string') as IPost[];
|
|
@@ -189,7 +471,7 @@ export const SlackLikeMessageGroup: React.FC<SlackLikeMessageGroupProps> = ({
|
|
|
189
471
|
const messageGroups = groupMessagesByUserAndTime(actualMessages);
|
|
190
472
|
|
|
191
473
|
return (
|
|
192
|
-
<div className=
|
|
474
|
+
<div className={`min-h-fit ${isDesktopView ? 'space-y-8' : isSmallScreen ? 'space-y-4' : 'space-y-6'}`}>
|
|
193
475
|
{messageGroups.map((group, groupIndex) => {
|
|
194
476
|
const author = group[0]?.author;
|
|
195
477
|
return (
|
|
@@ -200,6 +482,8 @@ export const SlackLikeMessageGroup: React.FC<SlackLikeMessageGroupProps> = ({
|
|
|
200
482
|
currentUser={currentUser}
|
|
201
483
|
onOpen={onOpen}
|
|
202
484
|
onMessageClick={onMessageClick}
|
|
485
|
+
isDesktopView={isDesktopView}
|
|
486
|
+
isSmallScreen={isSmallScreen}
|
|
203
487
|
/>
|
|
204
488
|
);
|
|
205
489
|
})}
|
package/src/components/index.ts
CHANGED
|
@@ -3,6 +3,8 @@ export {
|
|
|
3
3
|
LeftSidebar,
|
|
4
4
|
RightSidebar,
|
|
5
5
|
MessageInput,
|
|
6
|
+
MessageInputComponent,
|
|
7
|
+
InputComponent,
|
|
6
8
|
Messages,
|
|
7
9
|
CommonMessage,
|
|
8
10
|
MessageSliceRenderer,
|
|
@@ -11,5 +13,7 @@ export {
|
|
|
11
13
|
UploadImageButton,
|
|
12
14
|
UserModalContent,
|
|
13
15
|
ServiceInboxItem,
|
|
16
|
+
MessagesBuilderUi,
|
|
14
17
|
} from './InboxMessage';
|
|
15
18
|
export { ChatMessageFiller, ChatMessageFill2 } from './slot-fill';
|
|
19
|
+
export { AIAgent } from './AIAgent';
|
package/src/compute.ts
CHANGED
|
@@ -25,7 +25,7 @@ export const messengerPageStore: IRouteModule[] | { [key: string]: any } = [
|
|
|
25
25
|
component: () => import('./container/InboxWithLoader'),
|
|
26
26
|
tab: 'Chat App',
|
|
27
27
|
position: IMenuPosition.Middle,
|
|
28
|
-
//
|
|
28
|
+
//authority: [PreDefineAccountPermissions.secureUser],
|
|
29
29
|
name: 'Inbox',
|
|
30
30
|
path: '/o/:orgName/inbox',
|
|
31
31
|
},
|
|
@@ -41,10 +41,91 @@ export const messengerPageStore: IRouteModule[] | { [key: string]: any } = [
|
|
|
41
41
|
path: '//o/:orgName/direct-message/:id?/:postId?',
|
|
42
42
|
// path: '/o/:orgName/inbox',
|
|
43
43
|
},
|
|
44
|
+
// {
|
|
45
|
+
// exact: false,
|
|
46
|
+
// icon: 'AiOutlineInbox',
|
|
47
|
+
// key: 'ai-messenger',
|
|
48
|
+
// component: () => import('./container/AiInboxWithLoader'),
|
|
49
|
+
// tab: 'DM',
|
|
50
|
+
// position: IMenuPosition.Middle,
|
|
51
|
+
// // authority: [PreDefineAccountPermissions.secureUser],
|
|
52
|
+
// name: 'AiMessenger',
|
|
53
|
+
// path: '//o/:orgName/ai-messenger/:id?/:postId?',
|
|
54
|
+
// // path: '/o/:orgName/inbox',
|
|
55
|
+
// },
|
|
56
|
+
{
|
|
57
|
+
exact: false,
|
|
58
|
+
icon: 'AiOutlineInbox',
|
|
59
|
+
key: 'ai-messenger',
|
|
60
|
+
component: () => import('./container/InboxWithAiLoader'),
|
|
61
|
+
tab: 'Ai Messenger',
|
|
62
|
+
position: IMenuPosition.Middle,
|
|
63
|
+
name: 'AiMessenger',
|
|
64
|
+
path: '/ai-messenger',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
exact: false,
|
|
68
|
+
icon: 'AiOutlineInbox',
|
|
69
|
+
key: 'ai-messenger-with-org-name',
|
|
70
|
+
component: () => import('./container/InboxWithAiLoader'),
|
|
71
|
+
tab: 'Ai Messenger',
|
|
72
|
+
position: IMenuPosition.Middle,
|
|
73
|
+
name: 'AiMessengerWithOrgName',
|
|
74
|
+
path: '//o/:orgName/ai-messenger',
|
|
75
|
+
},
|
|
76
|
+
// {
|
|
77
|
+
// exact: false,
|
|
78
|
+
// icon: 'AiOutlineInbox',
|
|
79
|
+
// key: 'ai-messenger-with-channel-id',
|
|
80
|
+
// component: () => import('./templates/InboxWithAi'),
|
|
81
|
+
// tab: 'Ai Messenger',
|
|
82
|
+
// position: IMenuPosition.Middle,
|
|
83
|
+
// name: 'AiMessengerWithChannelId',
|
|
84
|
+
// path: '//o?/:orgName?/ai-messenger/:id',
|
|
85
|
+
// },
|
|
86
|
+
|
|
87
|
+
// {
|
|
88
|
+
// exact: false,
|
|
89
|
+
// icon: 'AiOutlineInbox',
|
|
90
|
+
// key: 'messenger-ai',
|
|
91
|
+
// component: () => import('./container/InboxAiLoader'),
|
|
92
|
+
// tab: 'Ai Messenger',
|
|
93
|
+
// position: IMenuPosition.Middle,
|
|
94
|
+
// name: 'MessengerAi',
|
|
95
|
+
// path: '/messenger-ai',
|
|
96
|
+
// },
|
|
97
|
+
{
|
|
98
|
+
exact: false,
|
|
99
|
+
icon: 'AiOutlineInbox',
|
|
100
|
+
key: 'ai-messenger-with-message-app',
|
|
101
|
+
component: () => import('./container/InboxAiMessagesLoader'),
|
|
102
|
+
tab: 'Ai Messenger',
|
|
103
|
+
position: IMenuPosition.Middle,
|
|
104
|
+
name: 'AiMessengerWithMessageApp',
|
|
105
|
+
path: '/ai-messenger/app',
|
|
106
|
+
},
|
|
107
|
+
// {
|
|
108
|
+
// exact: false,
|
|
109
|
+
// icon: 'AiOutlineInbox',
|
|
110
|
+
// key: 'ai-messenger-with-preview',
|
|
111
|
+
// component: () => import('./container/InboxWithAiLoader'),
|
|
112
|
+
// tab: 'Ai Messenger',
|
|
113
|
+
// position: IMenuPosition.Middle,
|
|
114
|
+
// name: 'AiMessengerWithPreview',
|
|
115
|
+
// path: '//o?/:orgName?/ai-messenger/app/:id/preview',
|
|
116
|
+
// },
|
|
44
117
|
];
|
|
45
118
|
|
|
46
119
|
// get only selected Routes to work
|
|
47
|
-
const selectedRoutes = [
|
|
120
|
+
const selectedRoutes = [
|
|
121
|
+
'inbox',
|
|
122
|
+
'direct-message',
|
|
123
|
+
'ai-messenger',
|
|
124
|
+
'ai-messenger-with-org-name',
|
|
125
|
+
'ai-messenger-with-channel-id',
|
|
126
|
+
'ai-messenger-with-message-app',
|
|
127
|
+
'ai-messenger-with-preview',
|
|
128
|
+
];
|
|
48
129
|
|
|
49
130
|
const filteredRoutes = getFilteredRoutes(messengerPageStore, selectedRoutes);
|
|
50
131
|
|
package/src/config/env-config.ts
CHANGED
|
@@ -4,8 +4,14 @@ import { getEnvironment } from '@common-stack/core';
|
|
|
4
4
|
const env = getEnvironment();
|
|
5
5
|
|
|
6
6
|
export const config = cleanEnv(env, {
|
|
7
|
+
CLIENT_URL: str({ default: 'http://localhost:3011' }),
|
|
7
8
|
WEB_INBOX_MESSEGE_PATH: str({ default: 'inbox' }),
|
|
8
9
|
WEB_THREAD_MESSEGE_PATH: str({ default: 'thread' }),
|
|
9
10
|
MESSAGES_PER_PAGE: num({ devDefault: 10, default: 10 }),
|
|
10
11
|
FILES_PER_MESSAGE: num({ default: 10 }),
|
|
12
|
+
VITE_OPENAI_API_KEY: str({ default: '' }),
|
|
13
|
+
VITE_ANTHROPIC_API_KEY: str({ default: '' }),
|
|
14
|
+
ANTHROPIC_API_KEY: str({ default: '' }),
|
|
15
|
+
NEWS_API_KEY: str({ default: '' }),
|
|
16
|
+
OPENAI_API_KEY: str({ default: '' }),
|
|
11
17
|
});
|