@messenger-box/tailwind-ui-inbox 10.0.3-alpha.176 → 10.0.3-alpha.178
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/lib/components/AIAgent/AIAgent.d.ts.map +1 -1
- package/lib/components/AIAgent/AIAgent.js +4 -0
- package/lib/components/AIAgent/AIAgent.js.map +1 -1
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +142 -170
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -1
- package/lib/components/slot-fill/chat-message-filler.js +1 -1
- package/lib/components/slot-fill/chat-message-filler.js.map +1 -1
- package/lib/container/AiLandingInput.js +1 -1
- package/lib/container/AiLandingInput.js.map +1 -1
- package/lib/container/Inbox.js +1 -1
- package/lib/container/Inbox.js.map +1 -1
- package/lib/container/ServiceInbox.js +1 -1
- package/lib/container/ServiceInbox.js.map +1 -1
- package/lib/container/TestInboxWithAiLoader.d.ts +1 -1
- package/lib/container/TestInboxWithAiLoader.d.ts.map +1 -1
- package/lib/container/TestInboxWithAiLoader.js +65 -8
- package/lib/container/TestInboxWithAiLoader.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/index.js +1 -1
- package/package.json +5 -4
- package/src/components/AIAgent/AIAgent.tsx +5 -0
- package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +150 -150
- package/src/container/TestInboxWithAiLoader.tsx +69 -5
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import React, { useEffect } from 'react';
|
|
1
|
+
import React, { useEffect, useMemo } from 'react';
|
|
2
2
|
import { format, differenceInMinutes } from 'date-fns';
|
|
3
3
|
import { AiAgentMessageRole, IAuthUser, IPost, PostTypeEnum } from 'common';
|
|
4
4
|
import { FilesList } from '../../inbox';
|
|
5
5
|
import { ErrorFixCard } from './ErrorFixCard';
|
|
6
|
-
import
|
|
7
|
-
|
|
6
|
+
import { marked } from 'marked';
|
|
7
|
+
|
|
8
|
+
// Configure marked for better markdown rendering
|
|
9
|
+
marked.setOptions({
|
|
10
|
+
breaks: true, // Convert line breaks to <br>
|
|
11
|
+
gfm: true, // GitHub Flavored Markdown
|
|
12
|
+
headerIds: false, // Disable header IDs for cleaner HTML
|
|
13
|
+
mangle: false, // Don't mangle email addresses
|
|
14
|
+
});
|
|
8
15
|
|
|
9
16
|
// Enhanced CSS styles for HTML content rendering with prettification
|
|
10
17
|
const htmlContentStyles = `
|
|
@@ -369,6 +376,30 @@ const htmlContentStyles = `
|
|
|
369
376
|
padding: 1rem;
|
|
370
377
|
overflow-x: auto;
|
|
371
378
|
}
|
|
379
|
+
|
|
380
|
+
/* Enhanced prose styling for markdown content */
|
|
381
|
+
.prose pre {
|
|
382
|
+
background-color: #f9fafb;
|
|
383
|
+
border: 1px solid #e5e7eb;
|
|
384
|
+
border-radius: 0.5rem;
|
|
385
|
+
padding: 1rem;
|
|
386
|
+
overflow-x: auto;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.prose code {
|
|
390
|
+
background-color: #f3f4f6;
|
|
391
|
+
padding: 0.125rem 0.375rem;
|
|
392
|
+
border-radius: 0.25rem;
|
|
393
|
+
font-size: 0.875rem;
|
|
394
|
+
border: 1px solid #e5e7eb;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.prose pre code {
|
|
398
|
+
background-color: transparent;
|
|
399
|
+
padding: 0;
|
|
400
|
+
border: none;
|
|
401
|
+
color:#000;
|
|
402
|
+
}
|
|
372
403
|
|
|
373
404
|
.hover\:bg-gray-100:hover {
|
|
374
405
|
background-color: transparent !important;
|
|
@@ -469,47 +500,57 @@ const detectContentType = (value: string): 'html' | 'markdown' | 'code' | 'text'
|
|
|
469
500
|
|
|
470
501
|
const trimmed = value.trim();
|
|
471
502
|
|
|
472
|
-
// Check for
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
503
|
+
// Check for markdown patterns FIRST (before code, as markdown can contain code blocks)
|
|
504
|
+
const markdownPatterns = [
|
|
505
|
+
/^#{1,6}\s+.+$/gm, // Headers (must have text after #)
|
|
506
|
+
/^\s*[-*+]\s+.+$/gm, // Unordered lists
|
|
507
|
+
/^\s*\d+\.\s+.+$/gm, // Numbered lists
|
|
508
|
+
/\[.+?\]\(.+?\)/g, // Links
|
|
509
|
+
/!\[.+?\]\(.+?\)/g, // Images
|
|
510
|
+
/```[\s\S]*?```/g, // Fenced code blocks (markdown feature)
|
|
511
|
+
/^\s*>\s+/gm, // Blockquotes
|
|
512
|
+
/\*\*.*?\*\*/g, // Bold
|
|
513
|
+
/\*[^*].*?\*/g, // Italic (not just asterisks)
|
|
514
|
+
/^\s*\|.+\|/gm, // Tables
|
|
483
515
|
];
|
|
484
516
|
|
|
485
|
-
|
|
486
|
-
|
|
517
|
+
// Count markdown patterns - if we have multiple, it's likely markdown
|
|
518
|
+
const markdownMatches = markdownPatterns.filter((pattern) => {
|
|
519
|
+
const matches = trimmed.match(pattern);
|
|
520
|
+
return matches && matches.length > 0;
|
|
521
|
+
}).length;
|
|
487
522
|
|
|
488
|
-
//
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
523
|
+
// If we have 2+ markdown patterns, it's definitely markdown
|
|
524
|
+
if (markdownMatches >= 2) return 'markdown';
|
|
525
|
+
|
|
526
|
+
// If we have headers or lists, it's likely markdown
|
|
527
|
+
if (/^#{1,6}\s+/gm.test(trimmed) || /^\s*[-*+]\s+/gm.test(trimmed) || /^\s*\d+\.\s+/gm.test(trimmed)) {
|
|
528
|
+
return 'markdown';
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Check for pure code patterns (without markdown context)
|
|
532
|
+
const pureCodePatterns = [
|
|
533
|
+
/^---\s*\w+\.\w+\s*---$/gm, // File separators (standalone)
|
|
534
|
+
/^@tailwind\s+\w+/gm, // Tailwind directives at start
|
|
535
|
+
/^export\s+default\s*\{/gm, // JS/TS exports at start
|
|
536
|
+
/^import\s+.*\s+from\s+['"]/gm, // Import statements at start
|
|
497
537
|
];
|
|
498
538
|
|
|
499
|
-
const
|
|
500
|
-
if (
|
|
539
|
+
const hasPureCodePatterns = pureCodePatterns.some((pattern) => pattern.test(trimmed));
|
|
540
|
+
if (hasPureCodePatterns && markdownMatches === 0) return 'code';
|
|
501
541
|
|
|
502
542
|
// Check for HTML patterns
|
|
503
543
|
const htmlPatterns = [
|
|
504
|
-
|
|
544
|
+
/<[a-z][\s\S]*?>/i, // HTML tags
|
|
505
545
|
/&[a-z0-9#]+;/i, // HTML entities
|
|
506
|
-
/\s+[a-z-]+\s*=\s*["'][^"']*["']/i, // HTML attributes
|
|
507
546
|
/<[^>]+>[^<]*<\/[^>]+>/i, // HTML structure
|
|
508
547
|
];
|
|
509
548
|
|
|
510
549
|
const hasHtmlPatterns = htmlPatterns.some((pattern) => pattern.test(trimmed));
|
|
511
|
-
|
|
550
|
+
// Only return HTML if it's clearly HTML and not markdown
|
|
551
|
+
if (hasHtmlPatterns && markdownMatches === 0) return 'html';
|
|
512
552
|
|
|
553
|
+
// Default to text, but FormattedMessageContent will still try to parse as markdown
|
|
513
554
|
return 'text';
|
|
514
555
|
};
|
|
515
556
|
|
|
@@ -724,7 +765,10 @@ const CodeFormatter: React.FC<{ content: string }> = ({ content }) => {
|
|
|
724
765
|
|
|
725
766
|
// Regular text content
|
|
726
767
|
return (
|
|
727
|
-
<div
|
|
768
|
+
<div
|
|
769
|
+
key={index}
|
|
770
|
+
className="text-sm text-gray-700 leading-relaxed whitespace-pre-wrap break-words"
|
|
771
|
+
>
|
|
728
772
|
{section.trim()}
|
|
729
773
|
</div>
|
|
730
774
|
);
|
|
@@ -734,130 +778,82 @@ const CodeFormatter: React.FC<{ content: string }> = ({ content }) => {
|
|
|
734
778
|
);
|
|
735
779
|
};
|
|
736
780
|
|
|
737
|
-
// Enhanced markdown renderer using
|
|
781
|
+
// Enhanced markdown renderer using marked (similar to MessageBubble.tsx)
|
|
738
782
|
const FormattedMessageContent: React.FC<{ content: string }> = ({ content }) => {
|
|
739
783
|
if (!content) return null;
|
|
740
784
|
|
|
741
|
-
//
|
|
742
|
-
const
|
|
785
|
+
// Parse markdown using marked - be more aggressive in detection
|
|
786
|
+
const htmlContent = useMemo(() => {
|
|
787
|
+
try {
|
|
788
|
+
const trimmed = content.trim();
|
|
789
|
+
|
|
790
|
+
// Comprehensive markdown detection patterns
|
|
791
|
+
const markdownIndicators = [
|
|
792
|
+
/^#{1,6}\s+.+$/gm, // Headers with text
|
|
793
|
+
/^\s*[-*+]\s+.+$/gm, // Unordered lists
|
|
794
|
+
/^\s*\d+\.\s+.+$/gm, // Numbered lists
|
|
795
|
+
/\[.+?\]\(.+?\)/g, // Links
|
|
796
|
+
/!\[.+?\]\(.+?\)/g, // Images
|
|
797
|
+
/```[\s\S]*?```/g, // Fenced code blocks
|
|
798
|
+
/^\s*>\s+/gm, // Blockquotes
|
|
799
|
+
/\*\*[^*]+\*\*/g, // Bold text
|
|
800
|
+
/\*[^*\n]+\*/g, // Italic text (not just asterisks)
|
|
801
|
+
/^\s*\|.+\|/gm, // Tables
|
|
802
|
+
/~~.+~~/g, // Strikethrough
|
|
803
|
+
];
|
|
804
|
+
|
|
805
|
+
// Check if content has markdown patterns
|
|
806
|
+
const hasMarkdown = markdownIndicators.some((pattern) => {
|
|
807
|
+
const matches = trimmed.match(pattern);
|
|
808
|
+
return matches && matches.length > 0;
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
// Also check for common markdown characters that suggest markdown content
|
|
812
|
+
const hasMarkdownChars = /[#*`>|\[\]()]/.test(trimmed) && trimmed.length > 10;
|
|
813
|
+
|
|
814
|
+
// If we detect markdown, parse it
|
|
815
|
+
if (hasMarkdown || hasMarkdownChars) {
|
|
816
|
+
const parsed = marked.parse(trimmed, { async: false }) as string;
|
|
817
|
+
// Only return if parsing actually produced HTML (not just the same text)
|
|
818
|
+
if (parsed && parsed.trim() !== trimmed && parsed.includes('<')) {
|
|
819
|
+
return parsed;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
return null;
|
|
824
|
+
} catch (error) {
|
|
825
|
+
console.error('Error parsing markdown:', error);
|
|
826
|
+
return null;
|
|
827
|
+
}
|
|
828
|
+
}, [content]);
|
|
743
829
|
|
|
744
|
-
|
|
830
|
+
// If markdown was parsed, render as HTML with prose styling
|
|
831
|
+
if (htmlContent) {
|
|
745
832
|
return (
|
|
746
833
|
<div className="message-container">
|
|
747
|
-
<div
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
>
|
|
771
|
-
{children}
|
|
772
|
-
</code>
|
|
773
|
-
);
|
|
774
|
-
},
|
|
775
|
-
// Custom styling for headings
|
|
776
|
-
h1: ({ children, ...props }) => (
|
|
777
|
-
<h1 className="text-xl font-bold text-gray-900 mb-3" {...props}>
|
|
778
|
-
{children}
|
|
779
|
-
</h1>
|
|
780
|
-
),
|
|
781
|
-
h2: ({ children, ...props }) => (
|
|
782
|
-
<h2 className="text-lg font-semibold text-gray-800 mb-2" {...props}>
|
|
783
|
-
{children}
|
|
784
|
-
</h2>
|
|
785
|
-
),
|
|
786
|
-
h3: ({ children, ...props }) => (
|
|
787
|
-
<h3 className="text-base font-medium text-gray-700 mb-2" {...props}>
|
|
788
|
-
{children}
|
|
789
|
-
</h3>
|
|
790
|
-
),
|
|
791
|
-
// Custom styling for lists
|
|
792
|
-
ul: ({ children, ...props }) => (
|
|
793
|
-
<ul className="space-y-1" {...props}>
|
|
794
|
-
{children}
|
|
795
|
-
</ul>
|
|
796
|
-
),
|
|
797
|
-
ol: ({ children, ...props }) => (
|
|
798
|
-
<ol className="space-y-1" {...props}>
|
|
799
|
-
{children}
|
|
800
|
-
</ol>
|
|
801
|
-
),
|
|
802
|
-
li: ({ children, ...props }) => (
|
|
803
|
-
<li className="text-gray-700 leading-relaxed" {...props}>
|
|
804
|
-
{children}
|
|
805
|
-
</li>
|
|
806
|
-
),
|
|
807
|
-
// Custom styling for blockquotes
|
|
808
|
-
blockquote: ({ children, ...props }) => (
|
|
809
|
-
<blockquote
|
|
810
|
-
className="border-l-4 border-blue-400 pl-3 py-2 bg-blue-50 rounded-r-md my-3"
|
|
811
|
-
{...props}
|
|
812
|
-
>
|
|
813
|
-
{children}
|
|
814
|
-
</blockquote>
|
|
815
|
-
),
|
|
816
|
-
// Custom styling for tables
|
|
817
|
-
table: ({ children, ...props }) => (
|
|
818
|
-
<div className="overflow-x-auto my-3">
|
|
819
|
-
<table
|
|
820
|
-
className="min-w-full border border-gray-200 rounded-md overflow-hidden"
|
|
821
|
-
{...props}
|
|
822
|
-
>
|
|
823
|
-
{children}
|
|
824
|
-
</table>
|
|
825
|
-
</div>
|
|
826
|
-
),
|
|
827
|
-
th: ({ children, ...props }) => (
|
|
828
|
-
<th
|
|
829
|
-
className="px-3 py-2 text-left font-medium text-gray-700 border border-gray-200"
|
|
830
|
-
{...props}
|
|
831
|
-
>
|
|
832
|
-
{children}
|
|
833
|
-
</th>
|
|
834
|
-
),
|
|
835
|
-
td: ({ children, ...props }) => (
|
|
836
|
-
<td className="px-3 py-2 border border-gray-200 text-gray-700" {...props}>
|
|
837
|
-
{children}
|
|
838
|
-
</td>
|
|
839
|
-
),
|
|
840
|
-
// Custom styling for task lists (from remark-gfm)
|
|
841
|
-
input: ({ checked, ...props }: any) => (
|
|
842
|
-
<input
|
|
843
|
-
type="checkbox"
|
|
844
|
-
checked={checked}
|
|
845
|
-
readOnly
|
|
846
|
-
className="mr-2 mt-1 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
|
847
|
-
{...props}
|
|
848
|
-
/>
|
|
849
|
-
),
|
|
850
|
-
// Custom styling for strikethrough (from remark-gfm)
|
|
851
|
-
del: ({ children, ...props }) => (
|
|
852
|
-
<del className="text-gray-500 line-through" {...props}>
|
|
853
|
-
{children}
|
|
854
|
-
</del>
|
|
855
|
-
),
|
|
856
|
-
}}
|
|
857
|
-
>
|
|
858
|
-
{content}
|
|
859
|
-
</ReactMarkdown>
|
|
860
|
-
</div>
|
|
834
|
+
<div
|
|
835
|
+
className="text-sm prose prose-sm max-w-none leading-relaxed break-words
|
|
836
|
+
prose-p:my-2 prose-p:text-gray-700
|
|
837
|
+
prose-pre:my-2 prose-pre:bg-gray-50 prose-pre:border prose-pre:border-gray-200 prose-pre:rounded prose-pre:p-3
|
|
838
|
+
prose-ul:my-2 prose-ol:my-2
|
|
839
|
+
prose-pre:whitespace-pre-wrap prose-pre:break-words prose-pre:overflow-x-auto
|
|
840
|
+
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
|
|
841
|
+
prose-headings:font-semibold prose-headings:text-gray-900 prose-headings:mt-4 prose-headings:mb-2
|
|
842
|
+
prose-h1:text-2xl prose-h1:border-b prose-h1:border-gray-200 prose-h1:pb-2
|
|
843
|
+
prose-h2:text-xl prose-h2:border-b prose-h2:border-gray-200 prose-h2:pb-1
|
|
844
|
+
prose-h3:text-lg
|
|
845
|
+
prose-h4:text-base
|
|
846
|
+
prose-a:text-blue-600 prose-a:no-underline hover:prose-a:underline
|
|
847
|
+
prose-strong:font-semibold prose-strong:text-gray-900
|
|
848
|
+
prose-em:text-gray-600
|
|
849
|
+
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
|
|
850
|
+
prose-table:w-full prose-table:border prose-table:border-gray-200 prose-table:rounded-md prose-table:my-4
|
|
851
|
+
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
|
|
852
|
+
prose-td:px-3 prose-td:py-2 prose-td:border prose-td:border-gray-200 prose-td:text-gray-700
|
|
853
|
+
prose-li:my-1 prose-li:text-gray-700
|
|
854
|
+
prose-hr:my-4 prose-hr:border-gray-200"
|
|
855
|
+
dangerouslySetInnerHTML={{ __html: htmlContent }}
|
|
856
|
+
/>
|
|
861
857
|
</div>
|
|
862
858
|
);
|
|
863
859
|
}
|
|
@@ -865,7 +861,7 @@ const FormattedMessageContent: React.FC<{ content: string }> = ({ content }) =>
|
|
|
865
861
|
// For plain text content
|
|
866
862
|
return (
|
|
867
863
|
<div className="message-container">
|
|
868
|
-
<p className="text-gray-700 leading-relaxed whitespace-pre-wrap">{content}</p>
|
|
864
|
+
<p className="text-sm text-gray-700 leading-relaxed whitespace-pre-wrap break-words">{content}</p>
|
|
869
865
|
</div>
|
|
870
866
|
);
|
|
871
867
|
};
|
|
@@ -1199,6 +1195,8 @@ const ModernMessageBubble: React.FC<ModernMessageBubbleProps> = ({
|
|
|
1199
1195
|
{(() => {
|
|
1200
1196
|
const contentType = detectContentType(message.message);
|
|
1201
1197
|
|
|
1198
|
+
// Markdown and text both go through FormattedMessageContent
|
|
1199
|
+
// which will intelligently detect and render markdown
|
|
1202
1200
|
if (contentType === 'code') {
|
|
1203
1201
|
return <CodeFormatter content={message.message} />;
|
|
1204
1202
|
} else if (contentType === 'html') {
|
|
@@ -1211,6 +1209,8 @@ const ModernMessageBubble: React.FC<ModernMessageBubbleProps> = ({
|
|
|
1211
1209
|
/>
|
|
1212
1210
|
);
|
|
1213
1211
|
} else {
|
|
1212
|
+
// Handle both 'markdown' and 'text' - FormattedMessageContent
|
|
1213
|
+
// will detect markdown patterns and render accordingly
|
|
1214
1214
|
return <FormattedMessageContent content={message.message} />;
|
|
1215
1215
|
}
|
|
1216
1216
|
})()}
|
|
@@ -1,11 +1,75 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
import InboxWithAiLoader from './InboxWithAiLoader';
|
|
3
3
|
import { useParams } from '@remix-run/react';
|
|
4
|
+
import { useReCreateSandboxEnvironmentMutation } from 'common/graphql';
|
|
5
|
+
import { usePersistentModelConfig } from '../hooks/usePersistentModelConfig';
|
|
4
6
|
const TestInboxWithAiLoaderOutlet = () => {
|
|
5
|
-
const { orgName }
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const params = useParams<{ orgName: string }>();
|
|
8
|
+
const orgName = params.orgName as string;
|
|
9
|
+
const { modelConfig } = usePersistentModelConfig();
|
|
10
|
+
|
|
11
|
+
const [reCreateSandboxEnvironment] = useReCreateSandboxEnvironmentMutation();
|
|
12
|
+
|
|
13
|
+
// Track active recreation calls to prevent duplicates
|
|
14
|
+
const activeRecreationsRef = React.useRef<Map<string, Promise<any>>>(new Map());
|
|
15
|
+
// Track last recreation time to add debounce
|
|
16
|
+
const lastRecreationTimeRef = React.useRef<Map<string, number>>(new Map());
|
|
17
|
+
|
|
18
|
+
const handleRecreateSandbox = React.useCallback(
|
|
19
|
+
async (postId: string) => {
|
|
20
|
+
console.log('🔄 handleRecreateSandbox called for postId:', postId);
|
|
21
|
+
|
|
22
|
+
// Check if we're already recreating this postId
|
|
23
|
+
const existingPromise = activeRecreationsRef.current.get(postId);
|
|
24
|
+
if (existingPromise) {
|
|
25
|
+
console.log('⏭️ Already recreating sandbox for postId:', postId, '- waiting for existing call');
|
|
26
|
+
try {
|
|
27
|
+
return await existingPromise;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.log('⚠️ Existing recreation call failed, will retry:', error);
|
|
30
|
+
activeRecreationsRef.current.delete(postId);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Debounce: If we just recreated this postId recently (within 2 seconds), skip
|
|
35
|
+
const lastRecreationTime = lastRecreationTimeRef.current.get(postId);
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
if (lastRecreationTime && now - lastRecreationTime < 2000) {
|
|
38
|
+
console.log('⏭️ Skipping recreation - too soon after last recreation:', {
|
|
39
|
+
postId,
|
|
40
|
+
timeSinceLastRecreation: now - lastRecreationTime,
|
|
41
|
+
});
|
|
42
|
+
return { data: { reCreateSandboxEnvironment: { success: true, message: 'Skipped - duplicate call' } } };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Update last recreation time
|
|
46
|
+
lastRecreationTimeRef.current.set(postId, now);
|
|
47
|
+
|
|
48
|
+
// Create the mutation promise
|
|
49
|
+
const mutationPromise = (async () => {
|
|
50
|
+
try {
|
|
51
|
+
console.log('📞 Calling reCreateSandboxEnvironment mutation for postId:', postId);
|
|
52
|
+
const response = await reCreateSandboxEnvironment({
|
|
53
|
+
variables: {
|
|
54
|
+
postId: postId,
|
|
55
|
+
metadata: modelConfig?.metadata,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
console.log('✅ reCreateSandboxEnvironment mutation completed:', response);
|
|
59
|
+
return response;
|
|
60
|
+
} finally {
|
|
61
|
+
// Clean up the promise reference when done
|
|
62
|
+
activeRecreationsRef.current.delete(postId);
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
|
|
66
|
+
// Store the promise IMMEDIATELY (synchronously) before any async work
|
|
67
|
+
activeRecreationsRef.current.set(postId, mutationPromise);
|
|
68
|
+
|
|
69
|
+
return await mutationPromise;
|
|
70
|
+
},
|
|
71
|
+
[reCreateSandboxEnvironment, modelConfig?.metadata],
|
|
72
|
+
);
|
|
9
73
|
return (
|
|
10
74
|
<InboxWithAiLoader
|
|
11
75
|
orgName={orgName}
|