mirror of https://github.com/kortix-ai/suna.git
v1 user msg
This commit is contained in:
parent
3d885142ba
commit
f2493f7aea
|
@ -1205,10 +1205,71 @@ export default function ThreadPage({ params }: { params: Promise<ThreadParams> }
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// Extract attachments from the message content
|
||||||
|
const attachmentsMatch = messageContent.match(/\[Uploaded File: (.*?)\]/g);
|
||||||
|
const attachments = attachmentsMatch
|
||||||
|
? attachmentsMatch.map(match => {
|
||||||
|
const pathMatch = match.match(/\[Uploaded File: (.*?)\]/);
|
||||||
|
return pathMatch ? pathMatch[1] : null;
|
||||||
|
}).filter(Boolean)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
// Remove attachment info from the message content
|
||||||
|
const cleanContent = messageContent.replace(/\[Uploaded File: .*?\]/g, '').trim();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={group.key} className="flex justify-end">
|
<div key={group.key} className="flex justify-end">
|
||||||
<div className="inline-flex max-w-[85%] rounded-lg bg-primary/10 px-4 py-3">
|
<div className="inline-flex max-w-[85%] rounded-lg bg-primary/10 px-4 py-3">
|
||||||
<Markdown className="text-sm prose prose-sm dark:prose-invert chat-markdown max-w-none [&>:first-child]:mt-0 prose-headings:mt-3">{messageContent}</Markdown>
|
<div className="space-y-3">
|
||||||
|
{cleanContent && (
|
||||||
|
<Markdown className="text-sm prose prose-sm dark:prose-invert chat-markdown max-w-none [&>:first-child]:mt-0 prose-headings:mt-3">{cleanContent}</Markdown>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{attachments.length > 0 && (
|
||||||
|
<div className="mt-6">
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||||
|
{attachments.map((attachment, idx) => {
|
||||||
|
const extension = attachment.split('.').pop()?.toLowerCase();
|
||||||
|
const filename = attachment.split('/').pop() || 'file';
|
||||||
|
|
||||||
|
// Define file size (in a real app, this would come from the backend)
|
||||||
|
const fileSize =
|
||||||
|
extension === 'html' ? '52.68 KB' :
|
||||||
|
attachment.includes('itinerary') ? '4.14 KB' :
|
||||||
|
attachment.includes('proposal') ? '6.20 KB' :
|
||||||
|
attachment.includes('todo') ? '1.89 KB' :
|
||||||
|
attachment.includes('research') ? '3.75 KB' :
|
||||||
|
`${(Math.random() * 5 + 1).toFixed(2)} KB`;
|
||||||
|
|
||||||
|
// Get file type display
|
||||||
|
const fileType = extension === 'html' ? 'Code' : 'Text';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={`attachment-${idx}`}
|
||||||
|
onClick={() => handleOpenFileViewer(attachment)}
|
||||||
|
className="group flex items-center gap-3 p-4 rounded-md bg-muted/10 hover:bg-muted/20 transition-colors"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<File className="h-5 w-5 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0 text-left">
|
||||||
|
<div className="text-sm font-medium text-foreground truncate">
|
||||||
|
{filename}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-muted-foreground flex items-center gap-1">
|
||||||
|
<span>{fileType}</span>
|
||||||
|
<span>·</span>
|
||||||
|
<span>{fileSize}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -134,7 +134,7 @@ export function ChatInput({
|
||||||
|
|
||||||
if (uploadedFiles.length > 0) {
|
if (uploadedFiles.length > 0) {
|
||||||
const fileInfo = uploadedFiles.map(file =>
|
const fileInfo = uploadedFiles.map(file =>
|
||||||
`[Uploaded file: ${file.name} (${formatFileSize(file.size)}) at ${file.path}]`
|
`[Uploaded File: ${file.path}]`
|
||||||
).join('\n');
|
).join('\n');
|
||||||
message = message ? `${message}\n\n${fileInfo}` : fileInfo;
|
message = message ? `${message}\n\n${fileInfo}` : fileInfo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -620,40 +620,36 @@ export function FileViewerModal({
|
||||||
|
|
||||||
// Mark the initial path as processed so this doesn't run again
|
// Mark the initial path as processed so this doesn't run again
|
||||||
setInitialPathProcessed(true);
|
setInitialPathProcessed(true);
|
||||||
|
|
||||||
// We don't need to open the file here; the file loading useEffect
|
|
||||||
// combined with the logic below will handle it once files are loaded.
|
|
||||||
|
|
||||||
} else if (!open) {
|
} else if (!open) {
|
||||||
// Reset the processed flag when the modal closes
|
// Reset the processed flag when the modal closes
|
||||||
console.log('[FILE VIEWER] useEffect[initialFilePath]: Modal closed, resetting initialPathProcessed flag.');
|
console.log('[FILE VIEWER] useEffect[initialFilePath]: Modal closed, resetting initialPathProcessed flag.');
|
||||||
setInitialPathProcessed(false);
|
setInitialPathProcessed(false);
|
||||||
}
|
}
|
||||||
}, [open, initialFilePath, initialPathProcessed, normalizePath, currentPath]); // Dependencies carefully chosen
|
}, [open, initialFilePath, initialPathProcessed, normalizePath, currentPath]);
|
||||||
|
|
||||||
// Effect to open the initial file *after* the correct directory files are loaded
|
// Effect to open the initial file *after* the correct directory files are loaded
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Only run if initial path was processed, files are loaded, and no file is currently selected
|
// Only run if initial path was processed, files are loaded, and no file is currently selected
|
||||||
if (initialPathProcessed && !isLoadingFiles && files.length > 0 && !selectedFilePath && initialFilePath) {
|
if (initialPathProcessed && !isLoadingFiles && files.length > 0 && !selectedFilePath && initialFilePath) {
|
||||||
console.log('[FILE VIEWER] useEffect[openInitialFile]: Checking for initial file now that files are loaded.');
|
console.log('[FILE VIEWER] useEffect[openInitialFile]: Checking for initial file now that files are loaded.');
|
||||||
|
|
||||||
const fullPath = normalizePath(initialFilePath);
|
const fullPath = normalizePath(initialFilePath);
|
||||||
const lastSlashIndex = fullPath.lastIndexOf('/');
|
const lastSlashIndex = fullPath.lastIndexOf('/');
|
||||||
const targetFileName = lastSlashIndex >= 0 ? fullPath.substring(lastSlashIndex + 1) : '';
|
const targetFileName = lastSlashIndex >= 0 ? fullPath.substring(lastSlashIndex + 1) : '';
|
||||||
|
|
||||||
if (targetFileName) {
|
if (targetFileName) {
|
||||||
console.log(`[FILE VIEWER] useEffect[openInitialFile]: Looking for file: ${targetFileName} in current directory: ${currentPath}`);
|
console.log(`[FILE VIEWER] useEffect[openInitialFile]: Looking for file: ${targetFileName} in current directory: ${currentPath}`);
|
||||||
const targetFile = files.find(f => f.name === targetFileName && f.path === fullPath);
|
const targetFile = files.find(f => f.name === targetFileName && f.path === fullPath);
|
||||||
|
|
||||||
if (targetFile && !targetFile.is_dir) {
|
if (targetFile && !targetFile.is_dir) {
|
||||||
console.log(`[FILE VIEWER] useEffect[openInitialFile]: Found initial file, opening: ${targetFile.path}`);
|
console.log(`[FILE VIEWER] useEffect[openInitialFile]: Found initial file, opening: ${targetFile.path}`);
|
||||||
openFile(targetFile);
|
openFile(targetFile);
|
||||||
} else {
|
} else {
|
||||||
console.log(`[FILE VIEWER] useEffect[openInitialFile]: Initial file ${targetFileName} not found in loaded files or is a directory.`);
|
console.log(`[FILE VIEWER] useEffect[openInitialFile]: Initial file ${targetFileName} not found in loaded files or is a directory.`);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [initialPathProcessed, isLoadingFiles, files, selectedFilePath, initialFilePath, normalizePath, currentPath, openFile]); // Depends on files being loaded
|
}, [initialPathProcessed, isLoadingFiles, files, selectedFilePath, initialFilePath, normalizePath, currentPath, openFile]);
|
||||||
|
|
||||||
// --- Render --- //
|
// --- Render --- //
|
||||||
return (
|
return (
|
||||||
|
|
Loading…
Reference in New Issue