feat(file-viewer): preview media files inline instead of showing "binary file"

Natively renderable files (images, PDFs, audio, video) opened from the
Files tab previously fell back to the generic "binary file" placeholder.
Classify previewable extensions and render a new CodeEditorMediaPreview
component that streams the bytes through the existing authenticated content
endpoint and displays them with the right native element (<img>, <iframe>,
<video controls>, <audio controls>) via a blob: URL. Non-previewable
binaries (zip, exe, avi, mkv, fonts, ...) still show the binary message.
No backend or dependency changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
CloudCLI UI Contributors
2026-06-28 18:52:31 +00:00
parent ed4ae3114a
commit 1df336ca2d
5 changed files with 306 additions and 0 deletions

View File

@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
import { api } from '../../../utils/api';
import type { CodeEditorFile } from '../types/types';
import { isBinaryFile } from '../utils/binaryFile';
import { getPreviewKind } from '../utils/previewableFile';
type UseCodeEditorDocumentParams = {
file: CodeEditorFile;
@@ -23,6 +24,9 @@ export const useCodeEditorDocument = ({ file, projectPath }: UseCodeEditorDocume
const [saveSuccess, setSaveSuccess] = useState(false);
const [saveError, setSaveError] = useState<string | null>(null);
const [isBinary, setIsBinary] = useState(false);
// Some binaries (images, PDFs, audio, video) can be rendered natively, so the
// editor shows an inline preview instead of the generic binary placeholder.
const previewKind = getPreviewKind(file.name);
// `fileProjectId` is the DB primary key passed down from the editor sidebar;
// the fallback to `projectPath` preserves older callers that didn't yet
// propagate the identifier.
@@ -38,6 +42,13 @@ export const useCodeEditorDocument = ({ file, projectPath }: UseCodeEditorDocume
setLoading(true);
setIsBinary(false);
// Natively previewable media (image/pdf/audio/video) is rendered by
// CodeEditorMediaPreview, so there is nothing to read as text here.
if (getPreviewKind(file.name)) {
setLoading(false);
return;
}
// Check if file is binary by extension
if (isBinaryFile(file.name)) {
setIsBinary(true);
@@ -134,6 +145,8 @@ export const useCodeEditorDocument = ({ file, projectPath }: UseCodeEditorDocume
saveSuccess,
saveError,
isBinary,
previewKind,
fileProjectId,
handleSave,
handleDownload,
};