@admin-layout/tailwind-chat-pro 12.2.4-alpha.81 → 12.2.4-alpha.85

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 (31) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/lib/components/messages-container-ui/ErrorFixCard.d.ts +10 -0
  3. package/lib/components/messages-container-ui/ErrorFixCard.d.ts.map +1 -0
  4. package/lib/components/messages-container-ui/ErrorFixCard.js +219 -0
  5. package/lib/components/messages-container-ui/ErrorFixCard.js.map +1 -0
  6. package/lib/components/messages-container-ui/FilesList.d.ts +19 -0
  7. package/lib/components/messages-container-ui/FilesList.d.ts.map +1 -0
  8. package/lib/components/messages-container-ui/FilesList.js +75 -0
  9. package/lib/components/messages-container-ui/FilesList.js.map +1 -0
  10. package/lib/components/messages-container-ui/ModernMessageGroup.d.ts +46 -0
  11. package/lib/components/messages-container-ui/ModernMessageGroup.d.ts.map +1 -0
  12. package/lib/components/messages-container-ui/ModernMessageGroup.js +1884 -0
  13. package/lib/components/messages-container-ui/ModernMessageGroup.js.map +1 -0
  14. package/lib/components/messages-container-ui/index.d.ts +3 -0
  15. package/lib/components/messages-container-ui/index.d.ts.map +1 -1
  16. package/lib/components/messages-container-ui/messenger-slot-fill-name-enum.d.ts +15 -0
  17. package/lib/components/messages-container-ui/messenger-slot-fill-name-enum.d.ts.map +1 -0
  18. package/lib/components/messages-container-ui/messenger-slot-fill-name-enum.js +15 -0
  19. package/lib/components/messages-container-ui/messenger-slot-fill-name-enum.js.map +1 -0
  20. package/lib/components/messages-container-ui/modernStreaming.d.ts +11 -0
  21. package/lib/components/messages-container-ui/modernStreaming.d.ts.map +1 -0
  22. package/lib/components/messages-container-ui/modernStreaming.js +17 -0
  23. package/lib/components/messages-container-ui/modernStreaming.js.map +1 -0
  24. package/lib/index.js +1 -1
  25. package/package.json +13 -7
  26. package/src/components/messages-container-ui/ErrorFixCard.tsx +239 -0
  27. package/src/components/messages-container-ui/FilesList.tsx +89 -0
  28. package/src/components/messages-container-ui/ModernMessageGroup.tsx +2225 -0
  29. package/src/components/messages-container-ui/index.ts +10 -0
  30. package/src/components/messages-container-ui/messenger-slot-fill-name-enum.ts +14 -0
  31. package/src/components/messages-container-ui/modernStreaming.tsx +18 -0
@@ -0,0 +1,1884 @@
1
+ import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {Slot}from'@common-stack/components-pro';import {AiAgentMessageRole}from'common';import {format,differenceInMinutes}from'date-fns';import {marked}from'marked';import React,{useMemo,useEffect}from'react';import ReactMarkdown from'react-markdown';import remarkBreaks from'remark-breaks';import remarkGfm from'remark-gfm';import {MessengerSlotFillNameEnum}from'./messenger-slot-fill-name-enum.js';import {FilesList}from'./FilesList.js';import {ErrorFixCard}from'./ErrorFixCard.js';// Configure marked for better markdown rendering
2
+ marked.setOptions({
3
+ breaks: true,
4
+ // Convert line breaks to <br>
5
+ gfm: true,
6
+ // GitHub Flavored Markdown
7
+ headerIds: false,
8
+ // Disable header IDs for cleaner HTML
9
+ mangle: false // Don't mangle email addresses
10
+ });
11
+ const ASSISTANT_RESPONSE_THEME_CLASS = 'llm-response-theme';
12
+ const ASSISTANT_RESPONSE_CLASS = 'llm-response';
13
+ const ASSISTANT_RESPONSE_BUBBLE_CLASS = 'llm-response__bubble';
14
+ const ASSISTANT_RESPONSE_MARKDOWN_CLASS = 'llm-response__markdown';
15
+ const USER_MESSAGE_TEXT_SIZE_CLASS = 'text-base';
16
+ const USER_MESSAGE_PROSE_SIZE_CLASS = 'prose-base';
17
+ function getAssistantResponseClasses(innerClassName) {
18
+ return [ASSISTANT_RESPONSE_MARKDOWN_CLASS, innerClassName].filter(Boolean).join(' ');
19
+ }
20
+ function AssistantResponseWrapper({
21
+ children,
22
+ bubbleClassName
23
+ }) {
24
+ return jsx("div", {
25
+ className: ASSISTANT_RESPONSE_THEME_CLASS,
26
+ children: jsx("div", {
27
+ className: ASSISTANT_RESPONSE_CLASS,
28
+ children: jsx("div", {
29
+ className: [ASSISTANT_RESPONSE_BUBBLE_CLASS, bubbleClassName].filter(Boolean).join(' '),
30
+ children: children
31
+ })
32
+ })
33
+ });
34
+ }
35
+ // Enhanced CSS styles for HTML content rendering with prettification
36
+ const htmlContentStyles = `
37
+ .html-content {
38
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
39
+ line-height: 1.6;
40
+ color: #374151;
41
+ }
42
+
43
+ .html-content h1, .html-content h2, .html-content h3, .html-content h4, .html-content h5, .html-content h6 {
44
+ margin-top: 1.5rem;
45
+ margin-bottom: 1rem;
46
+ font-weight: 600;
47
+ line-height: 1.25;
48
+ color: #111827;
49
+ }
50
+
51
+ .html-content h1 {
52
+ font-size: 1.875rem;
53
+ border-bottom: 2px solid #e5e7eb;
54
+ padding-bottom: 0.5rem;
55
+ }
56
+ .${ASSISTANT_RESPONSE_THEME_CLASS} {
57
+ --llm-content-max-width: 100%;
58
+ --llm-font-sans: 'Avenir Next', 'Segoe UI', sans-serif;
59
+ --llm-font-mono: 'IBM Plex Mono', 'SFMono-Regular', Menlo, Consolas, monospace;
60
+ --llm-color-bg: #ffffff;
61
+ --llm-color-surface: #f6f7f8;
62
+ --llm-color-surface-2: #eef1f4;
63
+ --llm-color-surface-hover: #e9edf2;
64
+ --llm-color-border: #e3e7ee;
65
+ --llm-color-border-strong: #cfd7e3;
66
+ --llm-color-text: #121926;
67
+ --llm-color-text-secondary: #667085;
68
+ --llm-color-marker: #8e98a8;
69
+ --llm-color-link-underline: rgba(18, 25, 38, 0.18);
70
+ --llm-color-code-bg: rgba(249, 115, 22, 0.1);
71
+ --llm-color-code-text: #d96b00;
72
+ --llm-color-code-block-border: #ebeef3;
73
+ --llm-shadow-soft: 0 1px 2px rgba(18, 25, 38, 0.04), 0 10px 32px rgba(18, 25, 38, 0.05);
74
+ --llm-radius-bubble: 1.5rem;
75
+ --llm-radius-code: 0.85rem;
76
+ --llm-line-height-body: 1.62;
77
+ --llm-line-height-mobile: 1.55;
78
+ --llm-line-height-heading: 1.35;
79
+ --llm-space-heading-top: 1.45rem;
80
+ --llm-space-heading-bottom: 0.75rem;
81
+ --llm-space-paragraph: 1rem;
82
+ --llm-space-list: 0.9rem;
83
+ color: var(--llm-color-text);
84
+ background: transparent;
85
+ }
86
+
87
+ .${ASSISTANT_RESPONSE_THEME_CLASS}[data-color-mode='dark'],
88
+ [data-color-mode='dark'] .${ASSISTANT_RESPONSE_THEME_CLASS} {
89
+ --llm-color-bg: #0f1115;
90
+ --llm-color-surface: #171a20;
91
+ --llm-color-surface-2: #1f2530;
92
+ --llm-color-surface-hover: #232a36;
93
+ --llm-color-border: #2a3240;
94
+ --llm-color-border-strong: #364052;
95
+ --llm-color-text: #f3f5f8;
96
+ --llm-color-text-secondary: #aab3c2;
97
+ --llm-color-marker: #7f8999;
98
+ --llm-color-link-underline: rgba(243, 245, 248, 0.24);
99
+ --llm-color-code-bg: rgba(251, 146, 60, 0.14);
100
+ --llm-color-code-text: #ffb36b;
101
+ --llm-color-code-block-border: #262d39;
102
+ --llm-shadow-soft: 0 1px 2px rgba(0, 0, 0, 0.28), 0 14px 36px rgba(0, 0, 0, 0.3);
103
+ }
104
+
105
+ .${ASSISTANT_RESPONSE_CLASS} {
106
+ position: relative;
107
+ display: flex;
108
+ flex-direction: column;
109
+ align-items: flex-start;
110
+ justify-content: center;
111
+ width: 100%;
112
+ max-width: var(--llm-content-max-width);
113
+ font-family: var(--llm-font-sans);
114
+ color: var(--llm-color-text);
115
+ }
116
+
117
+ .${ASSISTANT_RESPONSE_BUBBLE_CLASS} {
118
+ position: relative;
119
+ width: 100%;
120
+ min-height: 1.75rem;
121
+ border-radius: var(--llm-radius-bubble);
122
+ color: inherit;
123
+ word-break: break-word;
124
+ }
125
+
126
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} {
127
+ position: relative;
128
+ width: 100%;
129
+ max-width: none;
130
+ color: inherit;
131
+ font-size: 1rem;
132
+ line-height: var(--llm-line-height-body);
133
+ }
134
+
135
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} > :first-child {
136
+ margin-top: 0;
137
+ }
138
+
139
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} > :last-child {
140
+ margin-bottom: 0;
141
+ }
142
+
143
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} p,
144
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} li,
145
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} th,
146
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} td,
147
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} blockquote,
148
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS}.html-content,
149
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS}.assistant-html-content {
150
+ color: inherit;
151
+ }
152
+
153
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} p,
154
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS}.html-content p,
155
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS}.assistant-html-content p {
156
+ margin: 0 0 var(--llm-space-paragraph);
157
+ white-space: pre-wrap;
158
+ }
159
+
160
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} h1,
161
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} h2,
162
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} h3,
163
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} h4,
164
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} h5,
165
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} h6 {
166
+ margin: var(--llm-space-heading-top) 0 var(--llm-space-heading-bottom);
167
+ font-weight: 600;
168
+ letter-spacing: -0.02em;
169
+ line-height: var(--llm-line-height-heading);
170
+ color: var(--llm-color-text);
171
+ }
172
+
173
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} h1 { font-size: 1.75rem; }
174
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} h2 { font-size: 1.5rem; }
175
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} h3 { font-size: 1.25rem; }
176
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} h4 { font-size: 1.05rem; }
177
+
178
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} strong,
179
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} b {
180
+ font-weight: 600;
181
+ color: var(--llm-color-text);
182
+ }
183
+
184
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} em,
185
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} i {
186
+ color: var(--llm-color-text-secondary);
187
+ }
188
+
189
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} a {
190
+ color: currentColor;
191
+ text-decoration: underline;
192
+ text-decoration-color: var(--llm-color-link-underline);
193
+ text-underline-offset: 0.18em;
194
+ transition: color 120ms ease, text-decoration-color 120ms ease;
195
+ }
196
+
197
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} a:hover {
198
+ color: var(--llm-color-text);
199
+ text-decoration-color: currentColor;
200
+ }
201
+
202
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} ul,
203
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} ol {
204
+ margin: 0.35rem 0 var(--llm-space-list);
205
+ padding-left: 1.15rem;
206
+ }
207
+
208
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} ul {
209
+ list-style-type: disc;
210
+ }
211
+
212
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} ol {
213
+ list-style-type: decimal;
214
+ /* Wider gutter so multi-digit markers (e.g. "60.") aren't clipped at the
215
+ content's left edge — long numbered content (verse, step lists) showed
216
+ "0." for line 60. */
217
+ padding-left: 2rem;
218
+ }
219
+
220
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} li {
221
+ margin: 0.28rem 0;
222
+ padding-inline-start: 0.1rem;
223
+ /* \`normal\` collapses the stray "\\n" react-markdown inserts between <li>
224
+ and its child <p>; with \`pre-wrap\` that newline rendered as a blank
225
+ first line, stranding the marker above its text. Intra-paragraph
226
+ whitespace is preserved by the \`li > p\` rule below. */
227
+ white-space: normal;
228
+ }
229
+
230
+ /* Loose list items wrap their text in <p>. Keep pre-wrap for the text but
231
+ drop the paragraph margins so each item is a single tight line — fixes
232
+ poems / verse the model emits as numbered lists rendering as a sprawling,
233
+ double-spaced listicle. */
234
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} li > p {
235
+ margin-top: 0;
236
+ white-space: pre-wrap;
237
+ }
238
+
239
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} li > p:last-child {
240
+ margin-bottom: 0;
241
+ }
242
+
243
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} li > ul,
244
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} li > ol {
245
+ margin-top: 0.35rem;
246
+ margin-bottom: 0.15rem;
247
+ white-space: normal;
248
+ }
249
+
250
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} ::marker {
251
+ color: var(--llm-color-marker);
252
+ }
253
+
254
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} hr {
255
+ margin: 1.25rem 0;
256
+ border: 0;
257
+ border-top: 1px solid var(--llm-color-border);
258
+ }
259
+
260
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} blockquote {
261
+ margin: 1rem 0;
262
+ padding: 0.2rem 0 0.2rem 1rem;
263
+ border-left: 2px solid var(--llm-color-border-strong);
264
+ background: transparent;
265
+ color: var(--llm-color-text-secondary);
266
+ }
267
+
268
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} blockquote p {
269
+ margin-bottom: 0;
270
+ color: inherit;
271
+ }
272
+
273
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} :not(pre) > code,
274
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} .llm-inline-code {
275
+ display: inline-block;
276
+ padding: 0.08rem 0.38rem;
277
+ border: 0;
278
+ border-radius: 0.32rem;
279
+ font-family: var(--llm-font-mono);
280
+ font-size: 0.875em;
281
+ line-height: 1.45;
282
+ background: var(--llm-color-code-bg);
283
+ color: var(--llm-color-code-text);
284
+ box-decoration-break: clone;
285
+ -webkit-box-decoration-break: clone;
286
+ }
287
+
288
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} table {
289
+ width: 100%;
290
+ margin: 1rem 0 1.1rem;
291
+ border-collapse: separate;
292
+ border-spacing: 0;
293
+ border: 1px solid var(--llm-color-border);
294
+ border-radius: 1rem;
295
+ overflow: hidden;
296
+ box-shadow: var(--llm-shadow-soft);
297
+ }
298
+
299
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} thead {
300
+ background: var(--llm-color-surface);
301
+ }
302
+
303
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} th,
304
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} td {
305
+ padding: 0.85rem 1rem;
306
+ border-bottom: 1px solid var(--llm-color-border);
307
+ text-align: left;
308
+ vertical-align: top;
309
+ }
310
+
311
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} th {
312
+ font-size: 0.8rem;
313
+ font-weight: 600;
314
+ letter-spacing: 0.02em;
315
+ color: var(--llm-color-text-secondary);
316
+ background: var(--llm-color-surface);
317
+ }
318
+
319
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} td {
320
+ color: var(--llm-color-text);
321
+ background: var(--llm-color-bg);
322
+ }
323
+
324
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} tbody tr:last-child td {
325
+ border-bottom: 0;
326
+ }
327
+
328
+ @media (max-width: 768px) {
329
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} {
330
+ font-size: 0.95rem;
331
+ line-height: var(--llm-line-height-mobile);
332
+ }
333
+
334
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} th,
335
+ .${ASSISTANT_RESPONSE_MARKDOWN_CLASS} td {
336
+ padding: 0.75rem 0.8rem;
337
+ }
338
+ }
339
+ * we must neutralize it so it doesn't override the pill background/padding.
340
+ */
341
+ .user-message .message-container {
342
+ background: transparent !important;
343
+ padding-top: 0 !important;
344
+ padding-bottom: 0 !important;
345
+ margin: 0 !important;
346
+ transition: none !important;
347
+ box-shadow: none !important;
348
+ }
349
+
350
+ .assistant-message-container {
351
+ background: transparent;
352
+ padding-top: 0;
353
+ padding-bottom: 0;
354
+ margin: 0;
355
+ transition: none;
356
+ box-shadow: none;
357
+ }
358
+
359
+ .assistant-html-content {
360
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
361
+ line-height: 1.6;
362
+ color: #374151;
363
+ }
364
+
365
+ .assistant-html-content h1,
366
+ .assistant-html-content h2,
367
+ .assistant-html-content h3,
368
+ .assistant-html-content h4,
369
+ .assistant-html-content h5,
370
+ .assistant-html-content h6 {
371
+ font-weight: 600;
372
+ line-height: 1.25;
373
+ color: #111827;
374
+ }
375
+
376
+ .assistant-html-content h1 {
377
+ font-size: 1.875rem;
378
+ margin-top: 2rem;
379
+ margin-bottom: 1rem;
380
+ }
381
+
382
+ .assistant-html-content h2 {
383
+ font-size: 1.5rem;
384
+ margin-top: 2rem;
385
+ margin-bottom: 1rem;
386
+ }
387
+
388
+ .assistant-html-content h3 {
389
+ font-size: 1.25rem;
390
+ margin-top: 1.5rem;
391
+ margin-bottom: 1rem;
392
+ }
393
+
394
+ .assistant-html-content h4 {
395
+ font-size: 1.125rem;
396
+ margin-top: 1rem;
397
+ margin-bottom: 0.5rem;
398
+ }
399
+
400
+ .assistant-html-content p {
401
+ margin-top: 0;
402
+ margin-bottom: 1rem;
403
+ line-height: 1.625;
404
+ color: #374151;
405
+ }
406
+
407
+ .assistant-html-content ul,
408
+ .assistant-html-content ol {
409
+ margin: 1rem 0;
410
+ padding-left: 1.25rem;
411
+ }
412
+
413
+ .assistant-html-content ul {
414
+ list-style-type: disc;
415
+ }
416
+
417
+ .assistant-html-content ol {
418
+ list-style-type: decimal;
419
+ }
420
+
421
+ .assistant-html-content li {
422
+ margin-bottom: 0.375rem;
423
+ line-height: 1.625;
424
+ color: #374151;
425
+ }
426
+
427
+ .assistant-html-content li > ul,
428
+ .assistant-html-content li > ol {
429
+ margin-top: 0.375rem;
430
+ margin-bottom: 0.375rem;
431
+ }
432
+
433
+ .assistant-html-content a {
434
+ color: #2563eb;
435
+ text-decoration: none;
436
+ }
437
+
438
+ .assistant-html-content a:hover {
439
+ color: #1d4ed8;
440
+ text-decoration: underline;
441
+ text-underline-offset: 2px;
442
+ }
443
+
444
+ .assistant-html-content strong,
445
+ .assistant-html-content b {
446
+ font-weight: 600;
447
+ color: #111827;
448
+ }
449
+
450
+ .assistant-html-content em,
451
+ .assistant-html-content i {
452
+ font-style: italic;
453
+ color: #4b5563;
454
+ }
455
+
456
+ .assistant-html-content code {
457
+ background-color: #f3f4f6;
458
+ padding: 0.25rem 0.5rem;
459
+ border-radius: 0.375rem;
460
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
461
+ font-size: 0.875rem;
462
+ color: #dc2626;
463
+ border: 1px solid #e5e7eb;
464
+ }
465
+
466
+ .assistant-html-content pre {
467
+ background-color: #f9fafb;
468
+ padding: 1rem;
469
+ border-radius: 0.5rem;
470
+ overflow-x: auto;
471
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
472
+ font-size: 0.875rem;
473
+ line-height: 1.5;
474
+ border: 1px solid #e5e7eb;
475
+ margin: 1.5rem 0;
476
+ }
477
+
478
+ .assistant-html-content pre code {
479
+ background-color: transparent;
480
+ padding: 0;
481
+ border: none;
482
+ color: #374151;
483
+ font-size: inherit;
484
+ }
485
+
486
+ .assistant-html-content blockquote {
487
+ margin: 1.5rem 0;
488
+ padding: 0 0 0 1rem;
489
+ border-left: 4px solid #d1d5db;
490
+ font-style: italic;
491
+ color: #6b7280;
492
+ background-color: transparent;
493
+ border-radius: 0;
494
+ }
495
+
496
+ .assistant-html-content blockquote p {
497
+ margin-bottom: 0;
498
+ }
499
+
500
+ .assistant-html-content table {
501
+ border-collapse: collapse;
502
+ border: 1px solid #e5e7eb;
503
+ width: 100%;
504
+ margin: 1rem 0;
505
+ }
506
+
507
+ .assistant-html-content th,
508
+ .assistant-html-content td {
509
+ text-align: left;
510
+ vertical-align: top;
511
+ }
512
+
513
+ .assistant-html-content th {
514
+ padding: 0.5rem 1rem;
515
+ background-color: #f9fafb;
516
+ border: 1px solid #e5e7eb;
517
+ font-weight: 500;
518
+ font-size: 0.75rem;
519
+ letter-spacing: 0.04em;
520
+ text-transform: uppercase;
521
+ color: #4b5563;
522
+ }
523
+
524
+ .assistant-html-content td {
525
+ padding: 0.5rem 1rem;
526
+ border-top: 1px solid #e5e7eb;
527
+ color: #374151;
528
+ }
529
+
530
+ .assistant-html-content hr {
531
+ border: none;
532
+ border-top: 1px solid #e5e7eb;
533
+ margin: 2rem 0;
534
+ }
535
+
536
+ /* Markdown content styling */
537
+ .markdown-content {
538
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
539
+ line-height: 1.6;
540
+ color: #374151;
541
+ }
542
+
543
+ .markdown-content h1 {
544
+ font-size: 1.5rem;
545
+ font-weight: 700;
546
+ margin-top: 1.5rem;
547
+ margin-bottom: 1rem;
548
+ color: #111827;
549
+ padding-bottom: 0.5rem;
550
+ border-bottom: 2px solid #e5e7eb;
551
+ }
552
+
553
+ .markdown-content h2 {
554
+ font-size: 1.25rem;
555
+ font-weight: 600;
556
+ margin-top: 1.25rem;
557
+ margin-bottom: 0.75rem;
558
+ color: #1f2937;
559
+ padding-bottom: 0.375rem;
560
+ border-bottom: 1px solid #e5e7eb;
561
+ }
562
+
563
+ .markdown-content h3 {
564
+ font-size: 1.125rem;
565
+ font-weight: 600;
566
+ margin-top: 1rem;
567
+ margin-bottom: 0.5rem;
568
+ color: #374151;
569
+ }
570
+
571
+ .markdown-content p {
572
+ margin-bottom: 0.75rem;
573
+ line-height: 1.6;
574
+ color: #4b5563;
575
+ }
576
+
577
+ .markdown-content ul, .markdown-content ol {
578
+ margin: 1rem 0;
579
+ padding-left: 1.5rem;
580
+ }
581
+
582
+ .markdown-content li {
583
+ margin-bottom: 0.5rem;
584
+ line-height: 1.6;
585
+ }
586
+
587
+ .markdown-content code {
588
+ background-color: #f3f4f6;
589
+ padding: 0.125rem 0.375rem;
590
+ border-radius: 0.25rem;
591
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
592
+ font-size: 0.875rem;
593
+ color: #dc2626;
594
+ border: 1px solid #e5e7eb;
595
+ }
596
+
597
+ .markdown-content pre {
598
+ color: #1f2937;
599
+ border-radius: 8px;
600
+ overflow-x: auto;
601
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
602
+ font-size: 0.875rem;
603
+ line-height: 1.5;
604
+ margin: 1rem 0;
605
+ }
606
+
607
+ .markdown-content pre code {
608
+ background-color: transparent;
609
+ padding: 0;
610
+ border: none;
611
+ color: #1f2937;
612
+ font-size: inherit;
613
+ }
614
+
615
+ .markdown-content blockquote {
616
+ border-left: 4px solid #3b82f6;
617
+ background-color: #f8fafc;
618
+ padding: 1rem;
619
+ margin: 1rem 0;
620
+ border-radius: 6px;
621
+ font-style: italic;
622
+ color: #475569;
623
+ border-top: 1px solid #e5e7eb;
624
+ border-right: 1px solid #e5e7eb;
625
+ border-bottom: 1px solid #e5e7eb;
626
+ }
627
+
628
+ .markdown-content table {
629
+ border-collapse: collapse;
630
+ border: 1px solid #e5e7eb;
631
+ width: 100%;
632
+ margin: 1rem 0;
633
+ border-radius: 8px;
634
+ overflow: hidden;
635
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
636
+ }
637
+
638
+ .markdown-content th {
639
+ background-color: #f9fafb;
640
+ color: #374151;
641
+ font-weight: 600;
642
+ padding: 0.75rem;
643
+ text-align: left;
644
+ border-bottom: 1px solid #e5e7eb;
645
+ font-size: 0.875rem;
646
+ }
647
+
648
+ .markdown-content td {
649
+ border: 1px solid #e5e7eb;
650
+ padding: 0.75rem;
651
+ text-align: left;
652
+ vertical-align: top;
653
+ background-color: white;
654
+ font-size: 0.875rem;
655
+ }
656
+
657
+ .markdown-content tr:nth-child(even) td {
658
+ background-color: #f9fafb;
659
+ }
660
+
661
+ .markdown-content tr:hover td {
662
+ background-color: #f3f4f6;
663
+ }
664
+
665
+ /* Enhanced list styling */
666
+ .markdown-content ul li {
667
+ position: relative;
668
+ padding-left: 0.5rem;
669
+ }
670
+
671
+ .markdown-content ul li::before {
672
+ content: "•";
673
+ color: #000;
674
+ font-weight: bold;
675
+ position: absolute;
676
+ left: -1rem;
677
+ }
678
+
679
+ .markdown-content ol li {
680
+ position: relative;
681
+ padding-left: 0.5rem;
682
+ }
683
+
684
+ .markdown-content ol li::before {
685
+ content: counter(list-item) ".";
686
+ font-weight: 600;
687
+ position: absolute;
688
+ left: -1.5rem;
689
+ }
690
+
691
+ /* Code block container styling */
692
+ .code-block-container {
693
+ background: #ffffff;
694
+ border: 1px solid #e5e7eb;
695
+ border-radius: 8px;
696
+ overflow: hidden;
697
+ margin: 1rem 0;
698
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
699
+ }
700
+
701
+ .code-block-header {
702
+ background: #f9fafb;
703
+ padding: 0.5rem 1rem;
704
+ border-bottom: 1px solid #e5e7eb;
705
+ font-weight: 600;
706
+ color: #374151;
707
+ font-size: 0.875rem;
708
+ text-transform: uppercase;
709
+ letter-spacing: 0.05em;
710
+ }
711
+
712
+ .code-block-content {
713
+ padding: 1rem;
714
+ overflow-x: auto;
715
+ }
716
+
717
+ /* Enhanced prose styling for markdown content */
718
+ .prose pre {
719
+ background-color: #f9fafb;
720
+ border: 1px solid #e5e7eb;
721
+ border-radius: 0.5rem;
722
+ padding: 1rem;
723
+ overflow-x: auto;
724
+ }
725
+
726
+ .prose code {
727
+ background-color: #f3f4f6;
728
+ padding: 0.125rem 0.375rem;
729
+ border-radius: 0.25rem;
730
+ font-size: 0.875rem;
731
+ border: 1px solid #e5e7eb;
732
+ }
733
+
734
+ .prose pre code {
735
+ background-color: transparent;
736
+ padding: 0;
737
+ border: none;
738
+ color:#000;
739
+ }
740
+
741
+ .hover\:bg-gray-100:hover {
742
+ background-color: transparent !important;
743
+ }
744
+ `;
745
+ // Hook to inject CSS styles
746
+ const useInjectStyles = () => {
747
+ useEffect(() => {
748
+ const styleElementPresent = !!document.getElementById('html-content-styles');
749
+ // Check if styles are already injected
750
+ if (styleElementPresent) {
751
+ return;
752
+ }
753
+ // Create and inject style element
754
+ const styleElement = document.createElement('style');
755
+ styleElement.id = 'html-content-styles';
756
+ styleElement.textContent = htmlContentStyles;
757
+ document.head.appendChild(styleElement);
758
+ // Cleanup function
759
+ return () => {
760
+ const existingStyle = document.getElementById('html-content-styles');
761
+ if (existingStyle) {
762
+ existingStyle.remove();
763
+ }
764
+ };
765
+ }, []);
766
+ };
767
+ // Enhanced utility function to group messages by user and time
768
+ const groupMessagesByUserAndTime = (messages, timeThresholdMinutes = 5) => {
769
+ if (!messages || messages.length === 0) return [];
770
+ const groups = [];
771
+ let currentGroup = [];
772
+ let lastMessage = null;
773
+ for (const message of messages) {
774
+ if (typeof message === 'string') continue; // Skip date separators
775
+ const shouldStartNewGroup = !lastMessage || lastMessage.author?.id !== message.author?.id || differenceInMinutes(new Date(message.createdAt), new Date(lastMessage.createdAt)) > timeThresholdMinutes;
776
+ if (shouldStartNewGroup) {
777
+ if (currentGroup.length > 0) {
778
+ groups.push(currentGroup);
779
+ }
780
+ currentGroup = [message];
781
+ } else {
782
+ currentGroup.push(message);
783
+ }
784
+ lastMessage = message;
785
+ }
786
+ if (currentGroup.length > 0) {
787
+ groups.push(currentGroup);
788
+ }
789
+ return groups;
790
+ };
791
+ // Enhanced content detection that properly identifies different content types
792
+ const detectContentType = value => {
793
+ if (!value) return 'text';
794
+ const trimmed = value.trim();
795
+ // Check for markdown patterns FIRST (before code, as markdown can contain code blocks)
796
+ const markdownPatterns = [/^#{1,6}\s+.+$/gm,
797
+ // Headers (must have text after #)
798
+ /^\s*[-*+]\s+.+$/gm,
799
+ // Unordered lists
800
+ /^\s*\d+\.\s+.+$/gm,
801
+ // Numbered lists
802
+ /\[.+?\]\(.+?\)/g,
803
+ // Links
804
+ /!\[.+?\]\(.+?\)/g,
805
+ // Images
806
+ /```[\s\S]*?```/g,
807
+ // Fenced code blocks (markdown feature)
808
+ /^\s*>\s+/gm,
809
+ // Blockquotes
810
+ /\*\*.*?\*\*/g,
811
+ // Bold
812
+ /\*[^*].*?\*/g,
813
+ // Italic (not just asterisks)
814
+ /^\s*\|.+\|/gm // Tables
815
+ ];
816
+ // Count markdown patterns - if we have multiple, it's likely markdown
817
+ const markdownMatches = markdownPatterns.filter(pattern => {
818
+ const matches = trimmed.match(pattern);
819
+ return matches && matches.length > 0;
820
+ }).length;
821
+ // If we have 2+ markdown patterns, it's definitely markdown
822
+ if (markdownMatches >= 2) return 'markdown';
823
+ // If we have headers or lists, it's likely markdown
824
+ if (/^#{1,6}\s+/gm.test(trimmed) || /^\s*[-*+]\s+/gm.test(trimmed) || /^\s*\d+\.\s+/gm.test(trimmed)) {
825
+ return 'markdown';
826
+ }
827
+ // Check for pure code patterns (without markdown context)
828
+ const pureCodePatterns = [/^---\s*\w+\.\w+\s*---$/gm,
829
+ // File separators (standalone)
830
+ /^@tailwind\s+\w+/gm,
831
+ // Tailwind directives at start
832
+ /^export\s+default\s*\{/gm,
833
+ // JS/TS exports at start
834
+ /^import\s+.*\s+from\s+['"]/gm // Import statements at start
835
+ ];
836
+ const hasPureCodePatterns = pureCodePatterns.some(pattern => pattern.test(trimmed));
837
+ if (hasPureCodePatterns && markdownMatches === 0) return 'code';
838
+ // Check for HTML patterns
839
+ const htmlPatterns = [/<[a-z][\s\S]*?>/i,
840
+ // HTML tags
841
+ /&[a-z0-9#]+;/i,
842
+ // HTML entities
843
+ /<[^>]+>[^<]*<\/[^>]+>/i // HTML structure
844
+ ];
845
+ const hasHtmlPatterns = htmlPatterns.some(pattern => pattern.test(trimmed));
846
+ // Only return HTML if it's clearly HTML and not markdown
847
+ if (hasHtmlPatterns && markdownMatches === 0) return 'html';
848
+ // Default to text, but FormattedMessageContent will still try to parse as markdown
849
+ return 'text';
850
+ };
851
+ /** Map message type (THINKING, TOOLUSE, etc.) to slot fill name for Kimi-like streaming UI */
852
+ const getMessageSlotName = type => {
853
+ if (!type) return null;
854
+ const t = String(type).toUpperCase();
855
+ if (t === 'THINKING') return MessengerSlotFillNameEnum.THINKING_NAME;
856
+ if (t === 'TOOLUSE') return MessengerSlotFillNameEnum.TOOLUSE_NAME;
857
+ if (t === 'CODEBLOCK') return MessengerSlotFillNameEnum.CODEBLOCK_NAME;
858
+ if (t === 'SERVICE') return MessengerSlotFillNameEnum.SERVICE_NAME;
859
+ if (t === 'AIASSISTANT' || t === 'ASSISTANT') return MessengerSlotFillNameEnum.AIASSISTANT_NAME;
860
+ if (t === 'TRAVEL') return MessengerSlotFillNameEnum.TRAVEL_NAME;
861
+ if (t === 'MEDIA') return MessengerSlotFillNameEnum.MEDIA_NAME;
862
+ if (t === 'WORKFLOW') return MessengerSlotFillNameEnum.WORKFLOW_NAME;
863
+ if (t === 'CONNECTOR') return MessengerSlotFillNameEnum.CONNECTOR_NAME;
864
+ return null;
865
+ };
866
+ // Enhanced sanitizer that allows most HTML tags while maintaining security
867
+ const sanitizeHtml = html => {
868
+ try {
869
+ if (typeof window === 'undefined' || typeof window.DOMParser === 'undefined') {
870
+ return html.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, '').replace(/<style[\s\S]*?>[\s\S]*?<\/style>/gi, '').replace(/on\w+\s*=\s*"[^"]*"/gi, '').replace(/on\w+\s*=\s*'[^']*'/gi, '').replace(/on\w+\s*=\s*[^\s>]+/gi, '').replace(/javascript:/gi, '').replace(/data:text\/html/gi, '');
871
+ }
872
+ const parser = new window.DOMParser();
873
+ const doc = parser.parseFromString(html, 'text/html');
874
+ // Only remove the most dangerous tags
875
+ const dangerousTags = ['script', 'style'];
876
+ dangerousTags.forEach(tag => {
877
+ doc.querySelectorAll(tag).forEach(el => el.remove());
878
+ });
879
+ const walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_ELEMENT);
880
+ let node = walker.nextNode();
881
+ while (node) {
882
+ Array.from(node.attributes).forEach(attr => {
883
+ const name = attr.name.toLowerCase();
884
+ const value = (attr.value || '').toLowerCase();
885
+ // Remove event handlers and javascript protocols
886
+ if (name.startsWith('on') || value.startsWith('javascript:')) {
887
+ node?.removeAttribute(attr.name);
888
+ }
889
+ // Allow most attributes but validate URLs for src/href
890
+ if ((name === 'src' || name === 'href') && !/^https?:|^\/.*/.test(attr.value)) {
891
+ node?.removeAttribute(attr.name);
892
+ }
893
+ });
894
+ node = walker.nextNode();
895
+ }
896
+ return doc.body.innerHTML;
897
+ } catch (e) {
898
+ return html.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, '');
899
+ }
900
+ };
901
+ // Function to prettify HTML content
902
+ const prettifyHtmlContent = htmlContent => {
903
+ // Add semantic classes for better styling
904
+ let prettified = htmlContent;
905
+ // Enhance headings with better styling
906
+ prettified = prettified.replace(/<h([1-6])>/g, '<h$1 class="html-heading html-heading-$1">');
907
+ // Enhance lists with better styling
908
+ prettified = prettified.replace(/<ul>/g, '<ul class="html-list html-list-unordered">');
909
+ prettified = prettified.replace(/<ol>/g, '<ol class="html-list html-list-ordered">');
910
+ // Enhance paragraphs with better spacing
911
+ prettified = prettified.replace(/<p>/g, '<p class="html-paragraph">');
912
+ // Enhance code blocks
913
+ prettified = prettified.replace(/<code>/g, '<code class="html-inline-code">');
914
+ prettified = prettified.replace(/<pre>/g, '<pre class="html-code-block">');
915
+ // Enhance blockquotes
916
+ prettified = prettified.replace(/<blockquote>/g, '<blockquote class="html-blockquote">');
917
+ // Enhance tables
918
+ prettified = prettified.replace(/<table>/g, '<table class="html-table">');
919
+ prettified = prettified.replace(/<th>/g, '<th class="html-table-header">');
920
+ prettified = prettified.replace(/<td>/g, '<td class="html-table-cell">');
921
+ // Add info boxes for certain content patterns
922
+ prettified = prettified.replace(/<p>(Note|Tip|Info|Warning|Error):\s*(.*?)<\/p>/gi, '<div class="html-info-box"><strong>$1:</strong> $2</div>');
923
+ return prettified;
924
+ };
925
+ // Minimal Builder-like blocks renderer (text/image/button/columns)
926
+ const BuilderLikeRenderer = ({
927
+ blocks
928
+ }) => {
929
+ if (!blocks || !Array.isArray(blocks) || blocks.length === 0) return null;
930
+ return jsx("div", {
931
+ className: "space-y-3",
932
+ children: blocks.map((block, idx) => {
933
+ const type = block?.['@type'] || block?.type;
934
+ if (!type) return null;
935
+ if (/text/i.test(type)) {
936
+ const html = sanitizeHtml(block?.text || block?.data?.text || '');
937
+ return jsx("div", {
938
+ className: "max-w-none text-gray-800",
939
+ dangerouslySetInnerHTML: {
940
+ __html: html
941
+ }
942
+ }, idx);
943
+ }
944
+ if (/image/i.test(type)) {
945
+ const src = block?.src || block?.image || block?.data?.src;
946
+ const alt = block?.alt || block?.data?.alt || '';
947
+ if (!src) return null;
948
+ return jsx("div", {
949
+ className: "rounded-lg overflow-hidden border border-gray-200",
950
+ children: jsx("img", {
951
+ className: "w-full h-auto",
952
+ src: src,
953
+ alt: alt
954
+ })
955
+ }, idx);
956
+ }
957
+ if (/button/i.test(type)) {
958
+ const text = block?.text || block?.data?.text || 'Button';
959
+ const href = block?.href || block?.link || block?.data?.href || '#';
960
+ return jsx("a", {
961
+ href: href,
962
+ target: "_blank",
963
+ rel: "noopener noreferrer",
964
+ className: "inline-flex items-center px-3 py-1.5 rounded-md bg-blue-600 text-white text-sm hover:bg-blue-700",
965
+ children: text
966
+ }, idx);
967
+ }
968
+ if (/columns?/i.test(type)) {
969
+ const cols = block?.columns || block?.data?.columns || [];
970
+ return jsx("div", {
971
+ className: "grid grid-cols-1 sm:grid-cols-2 gap-3",
972
+ children: cols.map((col, colIdx) => jsx("div", {
973
+ className: "space-y-2",
974
+ children: jsx(BuilderLikeRenderer, {
975
+ blocks: col?.blocks || col?.children
976
+ })
977
+ }, colIdx))
978
+ }, idx);
979
+ }
980
+ return jsx("div", {
981
+ className: "message-container",
982
+ children: jsx("pre", {
983
+ className: "text-xs text-gray-600 overflow-x-auto",
984
+ children: JSON.stringify(block, null, 2)
985
+ })
986
+ }, idx);
987
+ })
988
+ });
989
+ };
990
+ // Code block with optional header (language + copy) and footer (copy + download) per Image 2
991
+ const CodeBlockWithActions = ({
992
+ content,
993
+ language,
994
+ showCopyDownload = true
995
+ }) => {
996
+ const [copied, setCopied] = React.useState(false);
997
+ const handleCopy = React.useCallback(() => {
998
+ navigator.clipboard.writeText(content).then(() => {
999
+ setCopied(true);
1000
+ setTimeout(() => setCopied(false), 2000);
1001
+ });
1002
+ }, [content]);
1003
+ const handleDownload = React.useCallback(() => {
1004
+ const ext = language === 'jsx' || language === 'tsx' ? '.tsx' : language === 'js' || language === 'ts' ? '.ts' : '.txt';
1005
+ const blob = new Blob([content], {
1006
+ type: 'text/plain'
1007
+ });
1008
+ const url = URL.createObjectURL(blob);
1009
+ const a = document.createElement('a');
1010
+ a.href = url;
1011
+ a.download = `snippet${ext}`;
1012
+ a.click();
1013
+ URL.revokeObjectURL(url);
1014
+ }, [content, language]);
1015
+ const langLabel = (language || 'code').toUpperCase();
1016
+ /**
1017
+ * Duplicate / copy: back square offset top-left (only its top-left shows behind),
1018
+ * front square on top toward bottom-right — matches typical “two sheets” reference art.
1019
+ */
1020
+ const IconCopy = ({
1021
+ className
1022
+ }) => jsxs("svg", {
1023
+ className: className,
1024
+ viewBox: "0 0 24 24",
1025
+ fill: "none",
1026
+ xmlns: "http://www.w3.org/2000/svg",
1027
+ "aria-hidden": true,
1028
+ children: [jsx("rect", {
1029
+ x: "3.25",
1030
+ y: "3.25",
1031
+ width: "10.5",
1032
+ height: "10.5",
1033
+ rx: "2.25",
1034
+ ry: "2.25",
1035
+ stroke: "currentColor",
1036
+ strokeWidth: "1.5",
1037
+ strokeLinecap: "round",
1038
+ strokeLinejoin: "round"
1039
+ }), jsx("rect", {
1040
+ x: "8.75",
1041
+ y: "8.75",
1042
+ width: "10.5",
1043
+ height: "10.5",
1044
+ rx: "2.25",
1045
+ ry: "2.25",
1046
+ stroke: "currentColor",
1047
+ strokeWidth: "1.5",
1048
+ strokeLinecap: "round",
1049
+ strokeLinejoin: "round"
1050
+ })]
1051
+ });
1052
+ /** Arrow into tray (download) */
1053
+ const IconDownload = ({
1054
+ className
1055
+ }) => jsx("svg", {
1056
+ className: className,
1057
+ viewBox: "0 0 24 24",
1058
+ fill: "none",
1059
+ stroke: "currentColor",
1060
+ strokeWidth: 1.5,
1061
+ strokeLinecap: "round",
1062
+ strokeLinejoin: "round",
1063
+ "aria-hidden": true,
1064
+ children: jsx("path", {
1065
+ d: "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3"
1066
+ })
1067
+ });
1068
+ const IconCheck = ({
1069
+ className
1070
+ }) => jsx("svg", {
1071
+ className: className,
1072
+ viewBox: "0 0 24 24",
1073
+ fill: "none",
1074
+ stroke: "currentColor",
1075
+ strokeWidth: 1.75,
1076
+ strokeLinecap: "round",
1077
+ strokeLinejoin: "round",
1078
+ "aria-hidden": true,
1079
+ children: jsx("path", {
1080
+ d: "M5 13l4 4L19 7"
1081
+ })
1082
+ });
1083
+ const iconMuted = 'text-slate-500 hover:text-slate-600';
1084
+ return jsxs("div", {
1085
+ className: "rounded-xl bg-white overflow-hidden my-3 shadow-sm ring-1 ring-slate-200/60",
1086
+ children: [jsxs("div", {
1087
+ className: "flex items-center justify-between px-4 pt-4 pb-3 bg-white",
1088
+ children: [jsx("span", {
1089
+ className: "text-[11px] font-semibold uppercase tracking-wide text-slate-500",
1090
+ children: langLabel
1091
+ }), jsx("button", {
1092
+ type: "button",
1093
+ onClick: handleCopy,
1094
+ className: `p-1 rounded-md hover:bg-slate-100/80 transition-colors ${iconMuted}`,
1095
+ title: "Copy",
1096
+ "aria-label": "Copy to clipboard",
1097
+ children: jsx(IconCopy, {
1098
+ className: "w-[15px] h-[15px] shrink-0"
1099
+ })
1100
+ })]
1101
+ }), jsx("div", {
1102
+ className: "px-4 pb-4",
1103
+ children: jsx("div", {
1104
+ className: "overflow-x-auto bg-white py-4",
1105
+ children: jsx("pre", {
1106
+ className: "text-sm text-slate-900 font-mono leading-relaxed whitespace-pre-wrap break-words m-0 p-0 border-0 bg-transparent",
1107
+ children: jsx("code", {
1108
+ className: "bg-transparent",
1109
+ children: content
1110
+ })
1111
+ })
1112
+ })
1113
+ }), showCopyDownload && jsxs("div", {
1114
+ className: "flex items-center justify-start gap-0.5 px-4 pb-3 pt-0 mt-2 bg-white",
1115
+ children: [jsx("button", {
1116
+ type: "button",
1117
+ onClick: handleCopy,
1118
+ className: `p-1 rounded-md hover:bg-slate-100/80 transition-colors ${iconMuted}`,
1119
+ title: copied ? 'Copied' : 'Copy',
1120
+ "aria-label": copied ? 'Copied' : 'Copy to clipboard',
1121
+ children: copied ? jsx(IconCheck, {
1122
+ className: "w-[15px] h-[15px] text-emerald-600"
1123
+ }) : jsx(IconCopy, {
1124
+ className: "w-[15px] h-[15px] shrink-0"
1125
+ })
1126
+ }), jsx("button", {
1127
+ type: "button",
1128
+ onClick: handleDownload,
1129
+ className: `p-1 rounded-md hover:bg-slate-100/80 transition-colors ${iconMuted}`,
1130
+ title: "Download",
1131
+ "aria-label": "Download",
1132
+ children: jsx(IconDownload, {
1133
+ className: "w-[15px] h-[15px] shrink-0"
1134
+ })
1135
+ })]
1136
+ })]
1137
+ });
1138
+ };
1139
+ // Infer language from file extension or content
1140
+ const inferCodeLanguage = (section, filename) => {
1141
+ if (/\bimport\b.*from\s+['"]react['"]/i.test(section) || /<\w+[\s>]/.test(section)) return 'jsx';
1142
+ if (/\bimport\b/.test(section) && /\.(ts|tsx)\b/.test(section)) return 'ts';
1143
+ return 'code';
1144
+ };
1145
+ // Enhanced code formatter for raw code content
1146
+ const CodeFormatter = ({
1147
+ content,
1148
+ showCopyDownload = true,
1149
+ variant = 'default',
1150
+ planMode = false
1151
+ }) => {
1152
+ if (!content) return null;
1153
+ // Split content by file separators and format each section
1154
+ const sections = content.split(/(---\s*\w+\.\w+\s*---)/g);
1155
+ return jsx("div", {
1156
+ className: variant === 'user' ? '' : 'message-container',
1157
+ children: jsx("div", {
1158
+ className: "space-y-4",
1159
+ children: sections.map((section, index) => {
1160
+ if (!section.trim()) return null;
1161
+ // Check if this is a file separator
1162
+ const fileMatch = section.match(/---\s*(\w+\.\w+)\s*---/);
1163
+ if (fileMatch) {
1164
+ return jsx("div", {
1165
+ className: "code-block-container",
1166
+ children: jsxs("div", {
1167
+ className: "code-block-header",
1168
+ children: ["\uD83D\uDCC4 ", fileMatch[1]]
1169
+ })
1170
+ }, index);
1171
+ }
1172
+ // Format the code content
1173
+ const lines = section.trim().split('\n');
1174
+ const isCode = lines.some(line => line.includes('@tailwind') || line.includes('export default') || line.includes('import ') || line.includes('{') || line.includes('}') || line.includes('//') || line.includes('/*'));
1175
+ if (isCode) {
1176
+ const lang = inferCodeLanguage(section);
1177
+ return jsx(CodeBlockWithActions, {
1178
+ content: section.trim(),
1179
+ language: lang,
1180
+ showCopyDownload: showCopyDownload
1181
+ }, index);
1182
+ }
1183
+ // Regular text content
1184
+ return jsx("div", {
1185
+ className: `${variant === 'user' ? USER_MESSAGE_TEXT_SIZE_CLASS : 'text-sm'} ${variant === 'user' ? planMode ? 'text-primary-foreground' : 'text-gray-900' : 'text-gray-700'} leading-relaxed whitespace-pre-wrap break-words`,
1186
+ children: section.trim()
1187
+ }, index);
1188
+ })
1189
+ })
1190
+ });
1191
+ };
1192
+ // Enhanced markdown renderer using marked (similar to MessageBubble.tsx)
1193
+ const FormattedMessageContent = ({
1194
+ content,
1195
+ variant = 'default',
1196
+ planMode = false,
1197
+ showCopyDownloadOnCodeBlock = false
1198
+ }) => {
1199
+ if (!content) return null;
1200
+ // Parse markdown using marked - be more aggressive in detection
1201
+ const {
1202
+ htmlContent,
1203
+ markdownSource
1204
+ } = useMemo(() => {
1205
+ try {
1206
+ const trimmed = content.trim();
1207
+ // Comprehensive markdown detection patterns
1208
+ const markdownIndicators = [/^#{1,6}\s+.+$/gm,
1209
+ // Headers with text
1210
+ /^\s*[-*+]\s+.+$/gm,
1211
+ // Unordered lists
1212
+ /^\s*\d+\.\s+.+$/gm,
1213
+ // Numbered lists
1214
+ /\[.+?\]\(.+?\)/g,
1215
+ // Links
1216
+ /!\[.+?\]\(.+?\)/g,
1217
+ // Images
1218
+ /```[\s\S]*?```/g,
1219
+ // Fenced code blocks
1220
+ /^\s*>\s+/gm,
1221
+ // Blockquotes
1222
+ /\*\*[^*]+\*\*/g,
1223
+ // Bold text
1224
+ /\*[^*\n]+\*/g,
1225
+ // Italic text (not just asterisks)
1226
+ /^\s*\|.+\|/gm,
1227
+ // Tables
1228
+ /~~.+~~/g // Strikethrough
1229
+ ];
1230
+ // Check if content has markdown patterns
1231
+ const hasMarkdown = markdownIndicators.some(pattern => {
1232
+ const matches = trimmed.match(pattern);
1233
+ return matches && matches.length > 0;
1234
+ });
1235
+ // Also check for common markdown characters that suggest markdown content
1236
+ const hasMarkdownChars = /[#*`>|\[\]()]/.test(trimmed) && trimmed.length > 10;
1237
+ // If we detect markdown, parse it
1238
+ if (hasMarkdown || hasMarkdownChars) {
1239
+ const parsed = marked.parse(trimmed, {
1240
+ async: false
1241
+ });
1242
+ // Only return if parsing actually produced HTML (not just the same text)
1243
+ if (parsed && parsed.trim() !== trimmed && parsed.includes('<')) {
1244
+ return {
1245
+ htmlContent: parsed,
1246
+ markdownSource: trimmed
1247
+ };
1248
+ }
1249
+ }
1250
+ return {
1251
+ htmlContent: null,
1252
+ markdownSource: null
1253
+ };
1254
+ } catch (error) {
1255
+ console.error('Error parsing markdown:', error);
1256
+ return {
1257
+ htmlContent: null,
1258
+ markdownSource: null
1259
+ };
1260
+ }
1261
+ }, [content]);
1262
+ const userMarkdownClasses = `${USER_MESSAGE_TEXT_SIZE_CLASS} prose ${USER_MESSAGE_PROSE_SIZE_CLASS} max-w-none leading-relaxed break-words
1263
+ prose-p:my-2 prose-p:text-gray-700
1264
+ prose-pre:my-2 prose-pre:bg-transparent prose-pre:border-0 prose-pre:p-0
1265
+ prose-ul:my-2 prose-ol:my-2
1266
+ prose-code:break-words prose-code:bg-gray-50 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded prose-code:text-sm prose-code:font-mono
1267
+ prose-headings:font-semibold prose-headings:text-gray-900 prose-headings:mt-4 prose-headings:mb-2
1268
+ prose-h1:text-2xl prose-h1:border-b prose-h1:border-gray-200 prose-h1:pb-2
1269
+ prose-h2:text-xl prose-h2:border-b prose-h2:border-gray-200 prose-h2:pb-1
1270
+ prose-h3:text-lg
1271
+ prose-h4:text-base
1272
+ prose-a:text-blue-600 prose-a:no-underline hover:prose-a:underline
1273
+ prose-strong:font-semibold prose-strong:text-gray-900
1274
+ prose-em:text-gray-600
1275
+ prose-blockquote:border-l-4 prose-blockquote:border-blue-400 prose-blockquote:pl-4 prose-blockquote:py-2 prose-blockquote:bg-blue-50 prose-blockquote:rounded-r-md prose-blockquote:my-3 prose-blockquote:italic
1276
+ prose-table:w-full prose-table:border prose-table:border-gray-200 prose-table:rounded-md prose-table:my-4
1277
+ prose-th:px-3 prose-th:py-2 prose-th:text-left prose-th:font-semibold prose-th:bg-gray-50 prose-th:border prose-th:border-gray-200
1278
+ prose-td:px-3 prose-td:py-2 prose-td:border prose-td:border-gray-200 prose-td:text-gray-700
1279
+ prose-li:my-1 prose-li:text-gray-700
1280
+ prose-hr:my-4 prose-hr:border-gray-200
1281
+ ${planMode ? '!prose-p:text-primary-foreground !prose-li:text-primary-foreground !prose-strong:text-primary-foreground !prose-em:text-primary-foreground !prose-a:text-primary-foreground' : '!prose-p:text-gray-900 !prose-li:text-gray-900 !prose-strong:text-gray-900 !prose-em:text-gray-700 !prose-a:text-blue-600'}`;
1282
+ const userHtmlClasses = `${USER_MESSAGE_TEXT_SIZE_CLASS} prose ${USER_MESSAGE_PROSE_SIZE_CLASS} max-w-none leading-relaxed break-words
1283
+ prose-p:my-2 prose-p:text-gray-700
1284
+ prose-pre:my-2 prose-pre:bg-gray-50 prose-pre:border prose-pre:border-gray-200 prose-pre:rounded prose-pre:p-3
1285
+ prose-ul:my-2 prose-ol:my-2
1286
+ prose-pre:whitespace-pre-wrap prose-pre:break-words prose-pre:overflow-x-auto
1287
+ prose-code:break-words prose-code:bg-gray-50 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded prose-code:text-sm prose-code:font-mono
1288
+ prose-headings:font-semibold prose-headings:text-gray-900 prose-headings:mt-4 prose-headings:mb-2
1289
+ prose-h1:text-2xl prose-h1:border-b prose-h1:border-gray-200 prose-h1:pb-2
1290
+ prose-h2:text-xl prose-h2:border-b prose-h2:border-gray-200 prose-h2:pb-1
1291
+ prose-h3:text-lg
1292
+ prose-h4:text-base
1293
+ prose-a:text-blue-600 prose-a:no-underline hover:prose-a:underline
1294
+ prose-strong:font-semibold prose-strong:text-gray-900
1295
+ prose-em:text-gray-600
1296
+ prose-blockquote:border-l-4 prose-blockquote:border-blue-400 prose-blockquote:pl-4 prose-blockquote:py-2 prose-blockquote:bg-blue-50 prose-blockquote:rounded-r-md prose-blockquote:my-3 prose-blockquote:italic
1297
+ prose-table:w-full prose-table:border prose-table:border-gray-200 prose-table:rounded-md prose-table:my-4
1298
+ prose-th:px-3 prose-th:py-2 prose-th:text-left prose-th:font-semibold prose-th:bg-gray-50 prose-th:border prose-th:border-gray-200
1299
+ prose-td:px-3 prose-td:py-2 prose-td:border prose-td:border-gray-200 prose-td:text-gray-700
1300
+ prose-li:my-1 prose-li:text-gray-700
1301
+ prose-hr:my-4 prose-hr:border-gray-200
1302
+ ${planMode ? '!prose-p:text-primary-foreground !prose-li:text-primary-foreground !prose-strong:text-primary-foreground !prose-em:text-primary-foreground !prose-a:text-primary-foreground' : '!prose-p:text-gray-900 !prose-li:text-gray-900 !prose-strong:text-gray-900 !prose-em:text-gray-700 !prose-a:text-blue-600'}`;
1303
+ const markdownComponents = useMemo(() => {
1304
+ const isUserVariant = variant === 'user';
1305
+ const userTextClass = planMode ? 'text-primary-foreground' : 'text-gray-900';
1306
+ const userMutedTextClass = planMode ? 'text-primary-foreground' : 'text-gray-700';
1307
+ const userLinkClass = planMode ? 'text-primary-foreground underline underline-offset-2' : 'text-blue-600 hover:underline underline-offset-2';
1308
+ return {
1309
+ p: ({
1310
+ children
1311
+ }) => isUserVariant ? jsx("p", {
1312
+ className: `break-words mb-4 last:mb-0 leading-relaxed ${userTextClass}`,
1313
+ children: children
1314
+ }) : jsx("p", {
1315
+ children: children
1316
+ }),
1317
+ h1: ({
1318
+ children
1319
+ }) => isUserVariant ? jsx("h1", {
1320
+ className: `text-2xl font-semibold mt-6 mb-3 first:mt-0 ${userTextClass}`,
1321
+ children: children
1322
+ }) : jsx("h1", {
1323
+ children: children
1324
+ }),
1325
+ h2: ({
1326
+ children
1327
+ }) => isUserVariant ? jsx("h2", {
1328
+ className: `text-xl font-semibold mt-5 mb-3 first:mt-0 ${userTextClass}`,
1329
+ children: children
1330
+ }) : jsx("h2", {
1331
+ children: children
1332
+ }),
1333
+ h3: ({
1334
+ children
1335
+ }) => isUserVariant ? jsx("h3", {
1336
+ className: `text-lg font-semibold mt-4 mb-2 first:mt-0 ${userTextClass}`,
1337
+ children: children
1338
+ }) : jsx("h3", {
1339
+ children: children
1340
+ }),
1341
+ h4: ({
1342
+ children
1343
+ }) => isUserVariant ? jsx("h4", {
1344
+ className: `text-base font-semibold mt-4 mb-2 first:mt-0 ${userTextClass}`,
1345
+ children: children
1346
+ }) : jsx("h4", {
1347
+ children: children
1348
+ }),
1349
+ blockquote: ({
1350
+ children
1351
+ }) => isUserVariant ? jsx("blockquote", {
1352
+ className: `border-l-4 border-blue-400 pl-4 py-2 bg-blue-50 rounded-r-md my-3 italic ${userMutedTextClass}`,
1353
+ children: children
1354
+ }) : jsx("blockquote", {
1355
+ children: children
1356
+ }),
1357
+ hr: () => isUserVariant ? jsx("hr", {
1358
+ className: "my-4 border-gray-200"
1359
+ }) : jsx("hr", {}),
1360
+ ul: ({
1361
+ children
1362
+ }) => jsx("ul", {
1363
+ className: isUserVariant ? `list-disc pl-5 mb-4 space-y-1.5 marker:text-gray-500 [&_ul]:mt-1.5 [&_ol]:mt-1.5 ${userTextClass}` : 'list-disc pl-5',
1364
+ children: children
1365
+ }),
1366
+ ol: ({
1367
+ children
1368
+ }) => jsx("ol", {
1369
+ className: isUserVariant ? `list-decimal pl-5 mb-4 space-y-1.5 marker:text-gray-500 [&_ul]:mt-1.5 [&_ol]:mt-1.5 ${userTextClass}` : 'list-decimal pl-5',
1370
+ children: children
1371
+ }),
1372
+ li: ({
1373
+ children
1374
+ }) => jsx("li", {
1375
+ className: isUserVariant ? `break-words whitespace-pre-wrap leading-relaxed [&>ul]:whitespace-normal [&>ol]:whitespace-normal ${userMutedTextClass}` : 'break-words whitespace-pre-wrap leading-relaxed [&>ul]:whitespace-normal [&>ol]:whitespace-normal',
1376
+ children: children
1377
+ }),
1378
+ a: ({
1379
+ href,
1380
+ children
1381
+ }) => isUserVariant ? jsx("a", {
1382
+ href: href,
1383
+ className: userLinkClass,
1384
+ children: children
1385
+ }) : jsx("a", {
1386
+ href: href,
1387
+ children: children
1388
+ }),
1389
+ table: ({
1390
+ children,
1391
+ ...props
1392
+ }) => isUserVariant ? jsx("div", {
1393
+ className: "overflow-x-auto my-4 w-full rounded-md border border-gray-200",
1394
+ children: jsx("table", {
1395
+ className: "w-full text-sm",
1396
+ ...props,
1397
+ children: children
1398
+ })
1399
+ }) : jsx("div", {
1400
+ className: "overflow-x-auto my-4 w-full",
1401
+ children: jsx("table", {
1402
+ ...props,
1403
+ children: children
1404
+ })
1405
+ }),
1406
+ thead: ({
1407
+ children
1408
+ }) => isUserVariant ? jsx("thead", {
1409
+ className: "bg-gray-50 text-left",
1410
+ children: children
1411
+ }) : jsx("thead", {
1412
+ children: children
1413
+ }),
1414
+ th: ({
1415
+ children
1416
+ }) => isUserVariant ? jsx("th", {
1417
+ className: `px-3 py-2 text-left font-semibold ${userTextClass}`,
1418
+ children: children
1419
+ }) : jsx("th", {
1420
+ children: children
1421
+ }),
1422
+ td: ({
1423
+ children
1424
+ }) => isUserVariant ? jsx("td", {
1425
+ className: `px-3 py-2 ${userMutedTextClass}`,
1426
+ children: children
1427
+ }) : jsx("td", {
1428
+ children: children
1429
+ }),
1430
+ pre: ({
1431
+ children
1432
+ }) => jsx(Fragment, {
1433
+ children: children
1434
+ }),
1435
+ code: ({
1436
+ className,
1437
+ children,
1438
+ ...rest
1439
+ }) => {
1440
+ const match = /language-(\w+)/.exec(className || '');
1441
+ const text = String(children).replace(/\n$/, '');
1442
+ if (match) {
1443
+ return jsx("div", {
1444
+ className: "not-prose max-w-none",
1445
+ children: jsx(CodeBlockWithActions, {
1446
+ content: text,
1447
+ language: match[1],
1448
+ showCopyDownload: showCopyDownloadOnCodeBlock
1449
+ })
1450
+ });
1451
+ }
1452
+ if (text.includes('\n')) {
1453
+ return jsx("div", {
1454
+ className: "not-prose max-w-none",
1455
+ children: jsx(CodeBlockWithActions, {
1456
+ content: text,
1457
+ language: "code",
1458
+ showCopyDownload: showCopyDownloadOnCodeBlock
1459
+ })
1460
+ });
1461
+ }
1462
+ return jsx("code", {
1463
+ className: isUserVariant ? `rounded px-1.5 py-0.5 text-sm font-mono bg-gray-50 break-words ${className || ''}` : `llm-inline-code break-words ${className || ''}`,
1464
+ ...rest,
1465
+ children: children
1466
+ });
1467
+ }
1468
+ };
1469
+ }, [planMode, showCopyDownloadOnCodeBlock, variant]);
1470
+ // Fenced code copy/download needs React tree (not marked HTML)
1471
+ if (showCopyDownloadOnCodeBlock && markdownSource) {
1472
+ if (variant === 'user') {
1473
+ return jsx("div", {
1474
+ className: userMarkdownClasses,
1475
+ children: jsx(ReactMarkdown, {
1476
+ remarkPlugins: [remarkGfm, remarkBreaks],
1477
+ components: markdownComponents,
1478
+ children: markdownSource
1479
+ })
1480
+ });
1481
+ }
1482
+ return jsx(AssistantResponseWrapper, {
1483
+ children: jsx("div", {
1484
+ className: getAssistantResponseClasses('min-w-0'),
1485
+ children: jsx(ReactMarkdown, {
1486
+ remarkPlugins: [remarkGfm, remarkBreaks],
1487
+ components: markdownComponents,
1488
+ children: markdownSource
1489
+ })
1490
+ })
1491
+ });
1492
+ }
1493
+ // If markdown was parsed, render as HTML with prose styling
1494
+ if (htmlContent) {
1495
+ if (variant === 'user') {
1496
+ return jsx("div", {
1497
+ className: userHtmlClasses,
1498
+ dangerouslySetInnerHTML: {
1499
+ __html: htmlContent
1500
+ }
1501
+ });
1502
+ }
1503
+ return jsx(AssistantResponseWrapper, {
1504
+ children: jsx("div", {
1505
+ className: getAssistantResponseClasses('min-w-0'),
1506
+ dangerouslySetInnerHTML: {
1507
+ __html: htmlContent
1508
+ }
1509
+ })
1510
+ });
1511
+ }
1512
+ // For plain text content
1513
+ return variant === 'user' ? jsx("p", {
1514
+ className: `${USER_MESSAGE_TEXT_SIZE_CLASS} leading-relaxed whitespace-pre-wrap break-words ${planMode ? 'text-primary-foreground' : 'text-gray-900'}`,
1515
+ children: content
1516
+ }) : jsx(AssistantResponseWrapper, {
1517
+ children: jsx("div", {
1518
+ className: getAssistantResponseClasses(),
1519
+ children: jsx("p", {
1520
+ className: "leading-relaxed whitespace-pre-wrap break-words",
1521
+ children: content
1522
+ })
1523
+ })
1524
+ });
1525
+ };
1526
+ // Detect predominant text direction (RTL/LTR) for international content
1527
+ const detectTextDirection = text => {
1528
+ if (!text) return 'ltr';
1529
+ const rtlRanges = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/g; // Hebrew, Arabic, etc.
1530
+ const ltrRanges = /[A-Za-z\u00C0-\u024F\u1E00-\u1EFF]/g;
1531
+ const rtlCount = (text.match(rtlRanges) || []).length;
1532
+ const ltrCount = (text.match(ltrRanges) || []).length;
1533
+ return rtlCount > ltrCount ? 'rtl' : 'ltr';
1534
+ };
1535
+ // Lightweight language heuristic for lang attribute
1536
+ const detectLanguageTag = text => {
1537
+ if (!text) return 'en';
1538
+ if (/\p{Script=Arabic}/u.test(text)) return 'ar';
1539
+ if (/[\u0590-\u05FF]/.test(text)) return 'he';
1540
+ if (/[\u0600-\u06FF]/.test(text)) return 'fa';
1541
+ if (/[\u0900-\u097F]/.test(text)) return 'hi';
1542
+ if (/[\u4E00-\u9FFF]/.test(text)) return 'zh';
1543
+ if (/[\u3040-\u309F\u30A0-\u30FF]/.test(text)) return 'ja';
1544
+ if (/[\uAC00-\uD7AF]/.test(text)) return 'ko';
1545
+ if (/[\u0400-\u04FF]/.test(text)) return 'ru';
1546
+ if (/[\u0E00-\u0E7F]/.test(text)) return 'th';
1547
+ return 'en';
1548
+ };
1549
+ // Attempt to parse Builder.io-like JSON blocks embedded as message string
1550
+ const tryExtractBuilderBlocks = content => {
1551
+ if (!content || content.length < 2) return null;
1552
+ try {
1553
+ const trimmed = content.trim();
1554
+ const jsonMatch = trimmed.match(/([\[{][\s\S]*[\]}])/);
1555
+ const jsonText = jsonMatch ? jsonMatch[1] : trimmed;
1556
+ const parsed = JSON.parse(jsonText);
1557
+ if (Array.isArray(parsed)) {
1558
+ if (parsed.length && (parsed[0]['@type'] || parsed[0].type)) return parsed;
1559
+ } else if (parsed && typeof parsed === 'object') {
1560
+ if (Array.isArray(parsed.blocks)) return parsed.blocks;
1561
+ if (Array.isArray(parsed?.data?.blocks)) return parsed.data.blocks;
1562
+ }
1563
+ return null;
1564
+ } catch {
1565
+ return null;
1566
+ }
1567
+ };
1568
+ const ModernMessageGroup = ({
1569
+ author,
1570
+ messages,
1571
+ currentUser,
1572
+ showAuthorName = false,
1573
+ showAvatar = false,
1574
+ showTimestamp = false,
1575
+ onOpen,
1576
+ onMessageClick,
1577
+ isDesktopView = false,
1578
+ isSmallScreen = false,
1579
+ showCopyDownloadOnCodeBlock = false
1580
+ }) => {
1581
+ // Inject CSS styles for HTML content
1582
+ useInjectStyles();
1583
+ // Preserve server / store order for posts (TOOLUSE, THINKING, etc.); no client-side reordering
1584
+ const displayMessages = messages;
1585
+ //const isOwnMessage = author?.id === currentUser?.id;
1586
+ //const isOwnMessage = messages.some((message: any) => (message as any)?.propsConfiguration?.contents?.role === AiAgentMessageRole.User);
1587
+ const isOwnMessage = displayMessages.some(message => message?.propsConfiguration?.contents?.role === AiAgentMessageRole.User) || Array.isArray(author?.alias) && typeof currentUser?.authUserId === 'string' && author.alias.some(alias => alias?.toLowerCase() === currentUser.authUserId.toLowerCase());
1588
+ author?.givenName && author?.familyName ? `${author.givenName} ${author.familyName}` : author?.username || 'Unknown User';
1589
+ displayMessages[0];
1590
+ const formatTime = timestamp => {
1591
+ const date = new Date(timestamp);
1592
+ return format(date, 'h:mm a');
1593
+ };
1594
+ // Determine if this is an AI/system message for special styling
1595
+ const isSystemMessage = author?.username?.toLowerCase().includes('ai') || author?.username?.toLowerCase().includes('assistant') || author?.username?.toLowerCase().includes('system');
1596
+ // const isSystemMessage = messages.some(
1597
+ // (message: any) => (message as any)?.propsConfiguration?.contents?.role === AiAgentMessageRole.Assistant,
1598
+ // );
1599
+ // For user messages, don't show group header, just individual messages with avatars
1600
+ if (isOwnMessage) {
1601
+ return jsx("div", {
1602
+ className: `min-h-fit ${isDesktopView ? 'space-y-1 mb-3' : isSmallScreen ? 'space-y-0.5 mb-2' : 'space-y-0.5 mb-2'}`,
1603
+ children: displayMessages.map((message, index) => jsx(ModernMessageBubble, {
1604
+ message: message,
1605
+ isOwnMessage: isOwnMessage,
1606
+ isSystemMessage: isSystemMessage,
1607
+ isFirstInGroup: index === 0,
1608
+ isLastInGroup: index === displayMessages.length - 1,
1609
+ onMessageClick: onMessageClick,
1610
+ formatTime: formatTime,
1611
+ showAuthorName: showAuthorName,
1612
+ showAvatar: showAvatar,
1613
+ showTimestamp: showTimestamp,
1614
+ showCopyDownloadOnCodeBlock: showCopyDownloadOnCodeBlock
1615
+ }, message.id))
1616
+ });
1617
+ }
1618
+ // For other messages (non-user), show the traditional group layout
1619
+ return jsx("div", {
1620
+ className: `group transition-all duration-300 ease-in-out ${isDesktopView ? 'mb-4 px-4 py-3' : isSmallScreen ? 'mb-2 px-2 py-1' : 'mb-3 px-3 py-1'} ${isSystemMessage ? 'bg-white rounded-lg' : ' rounded-lg'}`,
1621
+ children: jsx("div", {
1622
+ className: `flex items-start ${isDesktopView ? 'space-x-4' : isSmallScreen ? 'space-x-2' : 'space-x-3'}`,
1623
+ children: jsx("div", {
1624
+ className: "flex-1 min-w-0 overflow-hidden",
1625
+ children: jsx("div", {
1626
+ className: "space-y-1",
1627
+ children: displayMessages.map((message, index) => jsx(ModernMessageBubble, {
1628
+ message: message,
1629
+ isOwnMessage: isOwnMessage,
1630
+ isSystemMessage: isSystemMessage,
1631
+ isFirstInGroup: index === 0,
1632
+ isLastInGroup: index === displayMessages.length - 1,
1633
+ onMessageClick: onMessageClick,
1634
+ formatTime: formatTime,
1635
+ showTimestamp: showTimestamp,
1636
+ showCopyDownloadOnCodeBlock: showCopyDownloadOnCodeBlock
1637
+ }, message.id))
1638
+ })
1639
+ })
1640
+ })
1641
+ });
1642
+ };
1643
+ const ModernMessageBubble = ({
1644
+ showAuthorName = false,
1645
+ showAvatar = false,
1646
+ showTimestamp = false,
1647
+ message,
1648
+ isOwnMessage,
1649
+ isSystemMessage,
1650
+ isFirstInGroup,
1651
+ isLastInGroup,
1652
+ onMessageClick,
1653
+ formatTime,
1654
+ showCopyDownloadOnCodeBlock = false
1655
+ }) => {
1656
+ const handleClick = () => {
1657
+ onMessageClick?.(message);
1658
+ };
1659
+ const isAssistantRole = message?.propsConfiguration?.contents?.role === AiAgentMessageRole.Assistant;
1660
+ const messageMode = message?.propsConfiguration?.contents?.mode ?? message?.propsConfiguration?.content?.mode ?? message?.props?.mode;
1661
+ const isPlanMode = messageMode === 'plan';
1662
+ // For user messages, create a right-aligned layout with avatar and name
1663
+ if (isOwnMessage) {
1664
+ const authorName = message.author?.givenName && message.author?.familyName ? `${message.author.givenName} ${message.author.familyName}` : message.author?.username || 'You';
1665
+ return jsx("div", {
1666
+ className: "py-1 px-1 sm:px-2 -mx-1 sm:-mx-2 group",
1667
+ children: jsxs("div", {
1668
+ className: "flex items-start justify-end gap-2",
1669
+ children: [jsxs("div", {
1670
+ className: "flex flex-col items-end max-w-[85%]",
1671
+ children: [showAuthorName && jsx("div", {
1672
+ className: "flex items-end space-x-2 mb-0.5",
1673
+ children: jsx("span", {
1674
+ className: "text-sm font-semibold text-gray-900",
1675
+ children: authorName
1676
+ })
1677
+ }), jsxs("div", {
1678
+ className: `${isAssistantRole ? 'text-sm' : USER_MESSAGE_TEXT_SIZE_CLASS} ${isAssistantRole ? 'cursor-pointer rounded-2xl border border-gray-200 bg-gray-50 text-gray-900 px-4 py-2.5' :
1679
+ // : 'user-message rounded-[22px] rounded-br-[4px] px-4 py-3 bg-primary text-primary-foreground shadow-sm'
1680
+ isPlanMode ? 'user-message rounded-[22px] rounded-br-[4px] px-4 py-3 bg-primary text-primary-foreground shadow-sm' : 'user-message rounded-[22px] rounded-br-[4px] px-4 py-3 '}`,
1681
+ onClick: isAssistantRole ? handleClick : undefined,
1682
+ dir: detectTextDirection(message?.message || ''),
1683
+ lang: detectLanguageTag(message?.message || ''),
1684
+ children: [message.message && jsx("div", {
1685
+ className: "max-w-none",
1686
+ children: (() => {
1687
+ const contentType = detectContentType(message.message);
1688
+ if (contentType === 'code') {
1689
+ return jsx(CodeFormatter, {
1690
+ content: message.message,
1691
+ showCopyDownload: showCopyDownloadOnCodeBlock,
1692
+ variant: isAssistantRole ? 'default' : 'user',
1693
+ planMode: !isAssistantRole && isPlanMode
1694
+ });
1695
+ } else if (contentType === 'html') {
1696
+ if (isAssistantRole) {
1697
+ return jsx(AssistantResponseWrapper, {
1698
+ children: jsx("div", {
1699
+ className: getAssistantResponseClasses('min-w-0'),
1700
+ dangerouslySetInnerHTML: {
1701
+ __html: prettifyHtmlContent(sanitizeHtml(message.message))
1702
+ }
1703
+ })
1704
+ });
1705
+ }
1706
+ return jsx("div", {
1707
+ className: isPlanMode ? '!text-primary-foreground html-content' : 'text-gray-900 html-content',
1708
+ dangerouslySetInnerHTML: {
1709
+ __html: prettifyHtmlContent(sanitizeHtml(message.message))
1710
+ }
1711
+ });
1712
+ } else {
1713
+ return jsx(FormattedMessageContent, {
1714
+ content: message.message,
1715
+ variant: isAssistantRole ? 'default' : 'user',
1716
+ planMode: !isAssistantRole && isPlanMode,
1717
+ showCopyDownloadOnCodeBlock: showCopyDownloadOnCodeBlock
1718
+ });
1719
+ }
1720
+ })()
1721
+ }), tryExtractBuilderBlocks(message?.message)?.length ? jsx("div", {
1722
+ className: "mt-2",
1723
+ children: jsx(BuilderLikeRenderer, {
1724
+ blocks: tryExtractBuilderBlocks(message?.message)
1725
+ })
1726
+ }) : null, message?.props?.blocks?.length ? jsx("div", {
1727
+ className: "mt-2",
1728
+ children: jsx(BuilderLikeRenderer, {
1729
+ blocks: message.props.blocks
1730
+ })
1731
+ }) : null, message.files?.totalCount > 0 && jsx("div", {
1732
+ className: "mt-2",
1733
+ children: jsx(FilesList, {
1734
+ uploaded: true,
1735
+ files: message.files.data
1736
+ })
1737
+ }), showTimestamp ? jsx("div", {
1738
+ className: `text-[10px] mt-1 text-right ${isAssistantRole ? 'text-gray-500' : isPlanMode ? 'text-primary-foreground/80' : 'text-gray-500'}`,
1739
+ children: formatTime(message.createdAt)
1740
+ }) : null]
1741
+ })]
1742
+ }), showAvatar && jsx("div", {
1743
+ className: "flex-shrink-0 mt-0.5",
1744
+ children: jsx("img", {
1745
+ className: `w-8 h-8 sm:w-10 sm:h-10 rounded-lg ${isAssistantRole ? '' : 'cursor-pointer hover:opacity-80'} transition-opacity object-cover`,
1746
+ src: message.author?.picture || '/default-avatar.svg',
1747
+ alt: authorName,
1748
+ //onClick={isAssistantRole ? undefined : handleClick}
1749
+ onClick: isAssistantRole ? handleClick : undefined,
1750
+ onError: e => {
1751
+ e.currentTarget.src = '/default-avatar.svg';
1752
+ }
1753
+ })
1754
+ })]
1755
+ })
1756
+ });
1757
+ }
1758
+ // For other messages (left-aligned) — use Slot for streaming types (THINKING, TOOLUSE, etc.) like Kimi
1759
+ const attachment = message?.props?.attachment ?? message?.propsConfiguration?.contents?.attachment;
1760
+ const slotName = getMessageSlotName(message?.type);
1761
+ // Don't show THINKING slot when attachment has no meaningful content or when dismissed
1762
+ const isThinkingSlot = slotName === MessengerSlotFillNameEnum.THINKING_NAME;
1763
+ const shouldShowThinkingSlot = !!attachment && attachment.dismissed !== true && (attachment.isThinking === true || attachment.thought != null && String(attachment.thought).trim() !== '');
1764
+ const shouldShowSlot = isThinkingSlot ? shouldShowThinkingSlot : !!attachment;
1765
+ const hasStreamingSlot = !!slotName && shouldShowSlot;
1766
+ return jsxs("div", {
1767
+ className: `group/message w-full min-w-0 px-0 py-2 ${isSystemMessage || isAssistantRole ? 'cursor-pointer' : ''}`,
1768
+ onClick: isSystemMessage || isAssistantRole ? handleClick : undefined,
1769
+ children: [jsx("div", {
1770
+ className: "flex items-start justify-between gap-3",
1771
+ children: jsxs("div", {
1772
+ className: "flex-1 min-w-0",
1773
+ children: [slotName && shouldShowSlot && jsx(Slot, {
1774
+ name: slotName,
1775
+ fillProps: {
1776
+ active: true,
1777
+ message,
1778
+ index: 0,
1779
+ onMessageClick
1780
+ }
1781
+ }), !hasStreamingSlot && message.message && jsx("div", {
1782
+ className: `min-w-0 relative max-w-full w-full ${isSystemMessage ? 'text-gray-800' : 'text-gray-900'}`,
1783
+ dir: detectTextDirection(message?.message || ''),
1784
+ lang: detectLanguageTag(message?.message || ''),
1785
+ children: (() => {
1786
+ const contentType = detectContentType(message.message);
1787
+ // Markdown and text both go through FormattedMessageContent
1788
+ // which will intelligently detect and render markdown
1789
+ if (contentType === 'code') {
1790
+ return jsx(CodeFormatter, {
1791
+ content: message.message,
1792
+ showCopyDownload: showCopyDownloadOnCodeBlock
1793
+ });
1794
+ } else if (contentType === 'html') {
1795
+ return jsx(AssistantResponseWrapper, {
1796
+ children: jsx("div", {
1797
+ className: getAssistantResponseClasses('min-w-0 relative max-w-full w-full'),
1798
+ dangerouslySetInnerHTML: {
1799
+ __html: prettifyHtmlContent(sanitizeHtml(message.message))
1800
+ }
1801
+ })
1802
+ });
1803
+ } else {
1804
+ // Handle both 'markdown' and 'text' - FormattedMessageContent
1805
+ // will detect markdown patterns and render accordingly
1806
+ return jsx(FormattedMessageContent, {
1807
+ content: message.message,
1808
+ showCopyDownloadOnCodeBlock: showCopyDownloadOnCodeBlock
1809
+ });
1810
+ }
1811
+ })()
1812
+ }), !hasStreamingSlot && tryExtractBuilderBlocks(message?.message)?.length ? jsx("div", {
1813
+ className: "mt-3 p-3 bg-gray-50 rounded-lg border border-gray-200",
1814
+ children: jsx(BuilderLikeRenderer, {
1815
+ blocks: tryExtractBuilderBlocks(message?.message)
1816
+ })
1817
+ }) : null, !hasStreamingSlot && message?.props?.blocks?.length ? jsx("div", {
1818
+ className: "mt-3 p-3 bg-gray-50 rounded-lg border border-gray-200",
1819
+ children: jsx(BuilderLikeRenderer, {
1820
+ blocks: message.props.blocks
1821
+ })
1822
+ }) : null, !hasStreamingSlot && message.files?.totalCount > 0 ? jsx("div", {
1823
+ className: "mt-3 p-3 bg-gray-50 rounded-lg border border-gray-200",
1824
+ children: jsx(FilesList, {
1825
+ uploaded: true,
1826
+ files: message.files.data
1827
+ })
1828
+ }) : null]
1829
+ })
1830
+ }), showTimestamp ? jsx("div", {
1831
+ className: "text-[10px] text-gray-500 mt-1 text-right",
1832
+ children: formatTime(message.createdAt)
1833
+ }) : null]
1834
+ });
1835
+ };
1836
+ const ModernMessageGroupComponent = ({
1837
+ messages,
1838
+ currentUser,
1839
+ showAuthorName = false,
1840
+ showAvatar = false,
1841
+ showTimestamp = false,
1842
+ onOpen,
1843
+ onMessageClick,
1844
+ isDesktopView = false,
1845
+ isSmallScreen = false,
1846
+ sandboxErrors = [],
1847
+ currentFiles = {},
1848
+ onFixError,
1849
+ showCopyDownloadOnCodeBlock = false
1850
+ }) => {
1851
+ // Inject CSS styles for HTML content
1852
+ useInjectStyles();
1853
+ // Filter out non-message items (like date strings)
1854
+ const actualMessages = messages.filter(msg => typeof msg !== 'string');
1855
+ // Group messages by user and time
1856
+ const messageGroups = groupMessagesByUserAndTime(actualMessages);
1857
+ return jsxs("div", {
1858
+ className: `min-h-fit ${isDesktopView ? 'space-y-6' : isSmallScreen ? 'space-y-4' : 'space-y-5'}`,
1859
+ children: [messageGroups.map((group, groupIndex) => {
1860
+ const author = group[0]?.author;
1861
+ return jsx(ModernMessageGroup, {
1862
+ author: author,
1863
+ messages: group,
1864
+ currentUser: currentUser,
1865
+ onOpen: onOpen,
1866
+ onMessageClick: onMessageClick,
1867
+ isDesktopView: isDesktopView,
1868
+ isSmallScreen: isSmallScreen,
1869
+ showAuthorName: showAuthorName,
1870
+ showAvatar: showAvatar,
1871
+ showTimestamp: showTimestamp,
1872
+ showCopyDownloadOnCodeBlock: showCopyDownloadOnCodeBlock
1873
+ }, `group-${groupIndex}-${group[0]?.id}`);
1874
+ }), sandboxErrors?.map(error => jsx("div", {
1875
+ className: "px-2",
1876
+ children: jsx(ErrorFixCard, {
1877
+ error: error,
1878
+ onFixError: onFixError || (() => Promise.resolve()),
1879
+ currentFiles: currentFiles,
1880
+ isFixing: false
1881
+ })
1882
+ }, error.id))]
1883
+ });
1884
+ };export{AssistantResponseWrapper,CodeFormatter,FormattedMessageContent,ModernMessageGroupComponent,detectContentType,groupMessagesByUserAndTime};//# sourceMappingURL=ModernMessageGroup.js.map