mirror of https://github.com/kortix-ai/suna.git
Merge pull request #719 from kubet/fix/filename-conversion
fix: filename conversion
This commit is contained in:
commit
e93bdab2dd
|
@ -18,6 +18,7 @@ import { Check, Clock } from 'lucide-react';
|
|||
import { BillingError } from '@/lib/api';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { agentKeys } from '@/hooks/react-query/agents/keys';
|
||||
import { normalizeFilenameToNFC } from '@/lib/utils/unicode';
|
||||
|
||||
interface AgentBuilderChatProps {
|
||||
agentId: string;
|
||||
|
@ -236,7 +237,8 @@ export const AgentBuilderChat = React.memo(function AgentBuilderChat({
|
|||
agentFormData.append('target_agent_id', agentId);
|
||||
|
||||
files.forEach((file) => {
|
||||
agentFormData.append('files', file, file.name);
|
||||
const normalizedName = normalizeFilenameToNFC(file.name);
|
||||
agentFormData.append('files', file, normalizedName);
|
||||
});
|
||||
|
||||
if (options?.model_name) agentFormData.append('model_name', options.model_name);
|
||||
|
|
|
@ -14,6 +14,7 @@ import { useAgentStream } from '@/hooks/useAgentStream';
|
|||
import { useAddUserMessageMutation } from '@/hooks/react-query/threads/use-messages';
|
||||
import { useStartAgentMutation, useStopAgentMutation } from '@/hooks/react-query/threads/use-agent-run';
|
||||
import { BillingError } from '@/lib/api';
|
||||
import { normalizeFilenameToNFC } from '@/lib/utils/unicode';
|
||||
|
||||
interface Agent {
|
||||
agent_id: string;
|
||||
|
@ -182,7 +183,8 @@ export const AgentPreview = ({ agent }: AgentPreviewProps) => {
|
|||
formData.append('agent_id', agent.agent_id);
|
||||
|
||||
files.forEach((file, index) => {
|
||||
formData.append('files', file, file.name);
|
||||
const normalizedName = normalizeFilenameToNFC(file.name);
|
||||
formData.append('files', file, normalizedName);
|
||||
});
|
||||
|
||||
if (options?.model_name) formData.append('model_name', options.model_name);
|
||||
|
|
|
@ -30,6 +30,7 @@ import { cn } from '@/lib/utils';
|
|||
import { useModal } from '@/hooks/use-modal-store';
|
||||
import { Examples } from './suggestions/examples';
|
||||
import { useThreadQuery } from '@/hooks/react-query/threads/use-threads';
|
||||
import { normalizeFilenameToNFC } from '@/lib/utils/unicode';
|
||||
|
||||
const PENDING_PROMPT_KEY = 'pendingAgentPrompt';
|
||||
|
||||
|
@ -110,7 +111,8 @@ export function DashboardContent() {
|
|||
}
|
||||
|
||||
files.forEach((file, index) => {
|
||||
formData.append('files', file, file.name);
|
||||
const normalizedName = normalizeFilenameToNFC(file.name);
|
||||
formData.append('files', file, normalizedName);
|
||||
});
|
||||
|
||||
if (options?.model_name) formData.append('model_name', options.model_name);
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip';
|
||||
import { UploadedFile } from './chat-input';
|
||||
import { normalizeFilenameToNFC } from '@/lib/utils/unicode';
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_BACKEND_URL || '';
|
||||
|
||||
|
@ -32,17 +33,23 @@ const handleLocalFiles = (
|
|||
|
||||
setPendingFiles((prevFiles) => [...prevFiles, ...filteredFiles]);
|
||||
|
||||
const newUploadedFiles: UploadedFile[] = filteredFiles.map((file) => ({
|
||||
name: file.name,
|
||||
path: `/workspace/${file.name}`,
|
||||
const newUploadedFiles: UploadedFile[] = filteredFiles.map((file) => {
|
||||
// Normalize filename to NFC
|
||||
const normalizedName = normalizeFilenameToNFC(file.name);
|
||||
|
||||
return {
|
||||
name: normalizedName,
|
||||
path: `/workspace/${normalizedName}`,
|
||||
size: file.size,
|
||||
type: file.type || 'application/octet-stream',
|
||||
localUrl: URL.createObjectURL(file)
|
||||
}));
|
||||
};
|
||||
});
|
||||
|
||||
setUploadedFiles((prev) => [...prev, ...newUploadedFiles]);
|
||||
filteredFiles.forEach((file) => {
|
||||
toast.success(`File attached: ${file.name}`);
|
||||
const normalizedName = normalizeFilenameToNFC(file.name);
|
||||
toast.success(`File attached: ${normalizedName}`);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -65,7 +72,9 @@ const uploadFiles = async (
|
|||
continue;
|
||||
}
|
||||
|
||||
const uploadPath = `/workspace/${file.name}`;
|
||||
// Normalize filename to NFC
|
||||
const normalizedName = normalizeFilenameToNFC(file.name);
|
||||
const uploadPath = `/workspace/${normalizedName}`;
|
||||
|
||||
// Check if this filename already exists in chat messages
|
||||
const isFileInChat = messages.some(message => {
|
||||
|
@ -74,7 +83,9 @@ const uploadFiles = async (
|
|||
});
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
// If the filename was normalized, append with the normalized name in the field name
|
||||
// The server will use the path parameter for the actual filename
|
||||
formData.append('file', file, normalizedName);
|
||||
formData.append('path', uploadPath);
|
||||
|
||||
const supabase = createClient();
|
||||
|
@ -116,13 +127,13 @@ const uploadFiles = async (
|
|||
}
|
||||
|
||||
newUploadedFiles.push({
|
||||
name: file.name,
|
||||
name: normalizedName,
|
||||
path: uploadPath,
|
||||
size: file.size,
|
||||
type: file.type || 'application/octet-stream',
|
||||
});
|
||||
|
||||
toast.success(`File uploaded: ${file.name}`);
|
||||
toast.success(`File uploaded: ${normalizedName}`);
|
||||
}
|
||||
|
||||
setUploadedFiles((prev) => [...prev, ...newUploadedFiles]);
|
||||
|
|
|
@ -51,6 +51,7 @@ import {
|
|||
FileCache
|
||||
} from '@/hooks/react-query/files';
|
||||
import JSZip from 'jszip';
|
||||
import { normalizeFilenameToNFC } from '@/lib/utils/unicode';
|
||||
|
||||
// Define API_URL
|
||||
const API_URL = process.env.NEXT_PUBLIC_BACKEND_URL || '';
|
||||
|
@ -1121,6 +1122,8 @@ export function FileViewerModal({
|
|||
}
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
// Process uploaded file - Define after helpers
|
||||
const processUpload = useCallback(
|
||||
async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
|
@ -1130,9 +1133,15 @@ export function FileViewerModal({
|
|||
setIsUploading(true);
|
||||
|
||||
try {
|
||||
// Normalize filename to NFC
|
||||
const normalizedName = normalizeFilenameToNFC(file.name);
|
||||
const uploadPath = `${currentPath}/${normalizedName}`;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('path', `${currentPath}/${file.name}`);
|
||||
// If the filename was normalized, append with the normalized name in the field name
|
||||
// The server will use the path parameter for the actual filename
|
||||
formData.append('file', file, normalizedName);
|
||||
formData.append('path', uploadPath);
|
||||
|
||||
const supabase = createClient();
|
||||
const {
|
||||
|
@ -1162,7 +1171,7 @@ export function FileViewerModal({
|
|||
// Reload the file list using React Query
|
||||
await refetchFiles();
|
||||
|
||||
toast.success(`Uploaded: ${file.name}`);
|
||||
toast.success(`Uploaded: ${normalizedName}`);
|
||||
} catch (error) {
|
||||
console.error('Upload failed:', error);
|
||||
toast.error(
|
||||
|
|
|
@ -3,7 +3,6 @@ import { useAuth } from '@/components/AuthProvider';
|
|||
import { fileQueryKeys } from './use-file-queries';
|
||||
import { FileCache } from '@/hooks/use-cached-file';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
// Import the normalizePath function from use-file-queries
|
||||
function normalizePath(path: string): string {
|
||||
if (!path) return '/';
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Normalize filename to NFC (Normalized Form Composed) to ensure consistent
|
||||
* Unicode representation across different systems, especially macOS which
|
||||
* can use NFD (Normalized Form Decomposed).
|
||||
*
|
||||
* @param filename The filename to normalize
|
||||
* @returns The filename normalized to NFC form
|
||||
*/
|
||||
export const normalizeFilenameToNFC = (filename: string): string => {
|
||||
try {
|
||||
// Normalize to NFC (Normalized Form Composed)
|
||||
return filename.normalize('NFC');
|
||||
} catch (error) {
|
||||
console.warn('Failed to normalize filename to NFC:', filename, error);
|
||||
return filename;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize file path to NFC (Normalized Form Composed) to ensure consistent
|
||||
* Unicode representation across different systems.
|
||||
*
|
||||
* @param path The file path to normalize
|
||||
* @returns The path with all components normalized to NFC form
|
||||
*/
|
||||
export const normalizePathToNFC = (path: string): string => {
|
||||
try {
|
||||
// Normalize to NFC (Normalized Form Composed)
|
||||
return path.normalize('NFC');
|
||||
} catch (error) {
|
||||
console.warn('Failed to normalize path to NFC:', path, error);
|
||||
return path;
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue