Loading...
-
{error}
+
+
+ Preview not available for this file type
+
);
}
// Check if file is markdown
- const lowerFileName = fileName?.toLowerCase() ?? '';
- const isMarkdown = lowerFileName.endsWith('.md') || lowerFileName.endsWith('.markdown');
-
+ const lowerFileName = fileName?.toLowerCase() ?? "";
+ const isMarkdown =
+ lowerFileName.endsWith(".md") || lowerFileName.endsWith(".markdown");
+
// Limit preview size for performance (10MB - matches upload limit)
const MAX_PREVIEW_SIZE = 10 * 1024 * 1024;
- const contentToShow = content && content.length > MAX_PREVIEW_SIZE
- ? content.substring(0, MAX_PREVIEW_SIZE) + '\n\n...(content truncated for preview)'
- : content;
+ const contentToShow =
+ content && content.length > MAX_PREVIEW_SIZE
+ ? content.substring(0, MAX_PREVIEW_SIZE) +
+ "\n\n...(content truncated for preview)"
+ : content;
return (
{isMarkdown ? (
-
-
+ (
- )
+ ),
}}
>
{contentToShow || ""}
@@ -83,4 +108,3 @@ export default function FilePreview({ content, loading, error, isTextFile, fileN
);
}
-
diff --git a/components/Files/FilePreviewSkeleton.tsx b/components/Files/FilePreviewSkeleton.tsx
new file mode 100644
index 000000000..1fd80d776
--- /dev/null
+++ b/components/Files/FilePreviewSkeleton.tsx
@@ -0,0 +1,15 @@
+import { Skeleton } from "@/components/ui/skeleton";
+
+export default function FilePreviewSkeleton() {
+ return (
+
+ );
+}
diff --git a/components/Sandboxes/SandboxFilePreview.tsx b/components/Sandboxes/SandboxFilePreview.tsx
index add33c973..55507da9f 100644
--- a/components/Sandboxes/SandboxFilePreview.tsx
+++ b/components/Sandboxes/SandboxFilePreview.tsx
@@ -6,6 +6,7 @@ import { isTextFile } from "@/utils/isTextFile";
interface SandboxFilePreviewProps {
selectedPath: string;
content: string | null;
+ imageUrl: string | null;
loading: boolean;
error: string | null;
}
@@ -13,6 +14,7 @@ interface SandboxFilePreviewProps {
export default function SandboxFilePreview({
selectedPath,
content,
+ imageUrl,
loading,
error,
}: SandboxFilePreviewProps) {
@@ -27,6 +29,7 @@ export default function SandboxFilePreview({
error={error}
isTextFile={isTextFile(fileName)}
fileName={fileName}
+ imageUrl={imageUrl ?? undefined}
/>
);
diff --git a/components/Sandboxes/SandboxFileTree.tsx b/components/Sandboxes/SandboxFileTree.tsx
index 2346e4e45..b6794387c 100644
--- a/components/Sandboxes/SandboxFileTree.tsx
+++ b/components/Sandboxes/SandboxFileTree.tsx
@@ -65,6 +65,7 @@ export default function SandboxFileTree() {
diff --git a/hooks/useSandboxFileContent.ts b/hooks/useSandboxFileContent.ts
index 1f820cf93..7835ea88b 100644
--- a/hooks/useSandboxFileContent.ts
+++ b/hooks/useSandboxFileContent.ts
@@ -6,6 +6,7 @@ import { getFileContents } from "@/lib/sandboxes/getFileContents";
interface UseSandboxFileContentReturn {
selectedPath: string | undefined;
content: string | null;
+ imageUrl: string | null;
loading: boolean;
error: string | null;
select: (path: string) => void;
@@ -21,6 +22,7 @@ export default function useSandboxFileContent(): UseSandboxFileContentReturn {
if (!accessToken) {
throw new Error("Please sign in to view file contents");
}
+
return getFileContents(accessToken, path);
},
});
@@ -36,6 +38,7 @@ export default function useSandboxFileContent(): UseSandboxFileContentReturn {
return {
selectedPath,
content: mutation.data?.content ?? null,
+ imageUrl: mutation.data?.imageUrl ?? null,
loading: mutation.isPending,
error: mutation.error?.message ?? null,
select,
diff --git a/hooks/useSaveKnowledgeEdit.ts b/hooks/useSaveKnowledgeEdit.ts
index a12bcfcff..9c035ef16 100644
--- a/hooks/useSaveKnowledgeEdit.ts
+++ b/hooks/useSaveKnowledgeEdit.ts
@@ -1,5 +1,5 @@
import { useArtistProvider } from "@/providers/ArtistProvider";
-import getMimeFromPath from "@/utils/getMimeFromPath";
+import getMimeFromPath from "@/lib/files/getMimeFromPath";
import { uploadFile } from "@/lib/arweave/uploadFile";
import { useQueryClient } from "@tanstack/react-query";
import { toast } from "react-toastify";
diff --git a/utils/getMimeFromPath.ts b/lib/files/getMimeFromPath.ts
similarity index 75%
rename from utils/getMimeFromPath.ts
rename to lib/files/getMimeFromPath.ts
index 841394c3d..8557d4dff 100644
--- a/utils/getMimeFromPath.ts
+++ b/lib/files/getMimeFromPath.ts
@@ -7,6 +7,12 @@ const mimeByExt: Record
= {
xml: "application/xml",
yml: "application/x-yaml",
yaml: "application/x-yaml",
+ png: "image/png",
+ jpg: "image/jpeg",
+ jpeg: "image/jpeg",
+ gif: "image/gif",
+ webp: "image/webp",
+ svg: "image/svg+xml",
};
export const getMimeFromPath = (path: string): string => {
@@ -15,4 +21,4 @@ export const getMimeFromPath = (path: string): string => {
return (ext && mimeByExt[ext]) || "text/plain";
};
-export default getMimeFromPath;
\ No newline at end of file
+export default getMimeFromPath;
diff --git a/lib/sandboxes/getFileContents.ts b/lib/sandboxes/getFileContents.ts
index 97bd6654c..04bb69cf8 100644
--- a/lib/sandboxes/getFileContents.ts
+++ b/lib/sandboxes/getFileContents.ts
@@ -1,15 +1,27 @@
import { NEW_API_BASE_URL } from "@/lib/consts";
+import { getMimeFromPath } from "@/lib/files/getMimeFromPath";
interface GetFileContentsResponse {
status: "success" | "error";
content?: string;
+ encoding?: "base64";
error?: string;
}
+interface FileContentsResult {
+ content: string | null;
+ imageUrl: string | null;
+}
+
+/**
+ * Fetches file content from the sandbox file API.
+ * The API auto-detects binary files and returns base64-encoded content.
+ * When base64 content is returned, this builds a data URL for image preview.
+ */
export async function getFileContents(
accessToken: string,
path: string,
-): Promise<{ content: string }> {
+): Promise {
const response = await fetch(
`${NEW_API_BASE_URL}/api/sandboxes/file?path=${encodeURIComponent(path)}`,
{
@@ -22,9 +34,17 @@ export async function getFileContents(
const data: GetFileContentsResponse = await response.json();
- if (!response.ok || data.status === "error") {
+ if (!response.ok || data.status === "error" || !data.content) {
throw new Error(data.error || "Failed to fetch file contents");
}
- return { content: data.content || "" };
+ if (data.encoding === "base64") {
+ const mimeType = getMimeFromPath(path);
+ return {
+ content: null,
+ imageUrl: `data:${mimeType};base64,${data.content}`,
+ };
+ }
+
+ return { content: data.content, imageUrl: null };
}