Skip to content

Commit

Permalink
Support displaying images/videos/pdfs in the workspace (#4898)
Browse files Browse the repository at this point in the history
  • Loading branch information
malhotra5 authored Nov 11, 2024
1 parent bf8ccc8 commit 8c00d96
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 5 deletions.
38 changes: 33 additions & 5 deletions frontend/src/routes/_oh.app._index/code-editor-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ function CodeEditorCompoonent({
if (selectedPath && value) modifyFileContent(selectedPath, value);
};

const isBase64Image = (content: string) => content.startsWith("data:image/");
const isPDF = (content: string) => content.startsWith("data:application/pdf");
const isVideo = (content: string) => content.startsWith("data:video/");

React.useEffect(() => {
const handleSave = async (event: KeyboardEvent) => {
if (selectedPath && event.metaKey && event.key === "s") {
Expand Down Expand Up @@ -62,16 +66,40 @@ function CodeEditorCompoonent({
);
}

const fileContent = modifiedFiles[selectedPath] || files[selectedPath];

if (isBase64Image(fileContent)) {
return (
<section className="flex flex-col relative items-center overflow-auto h-[90%]">
<img src={fileContent} alt={selectedPath} className="object-contain" />
</section>
);
}

if (isPDF(fileContent)) {
return (
<iframe
src={fileContent}
title={selectedPath}
width="100%"
height="100%"
/>
);
}

if (isVideo(fileContent)) {
return (
<video controls src={fileContent} width="100%" height="100%">
<track kind="captions" label="English captions" />
</video>
);
}
return (
<Editor
data-testid="code-editor"
path={selectedPath ?? undefined}
defaultValue=""
value={
selectedPath
? modifiedFiles[selectedPath] || files[selectedPath]
: undefined
}
value={selectedPath ? fileContent : undefined}
onMount={onMount}
onChange={handleEditorChange}
options={{ readOnly: isReadOnly }}
Expand Down
29 changes: 29 additions & 0 deletions openhands/runtime/action_execution_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

import argparse
import asyncio
import base64
import io
import mimetypes
import os
import shutil
import tempfile
Expand Down Expand Up @@ -217,6 +219,33 @@ async def read(self, action: FileReadAction) -> Observation:
working_dir = self.bash_session.workdir
filepath = self._resolve_path(action.path, working_dir)
try:
if filepath.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):
with open(filepath, 'rb') as file:
image_data = file.read()
encoded_image = base64.b64encode(image_data).decode('utf-8')
mime_type, _ = mimetypes.guess_type(filepath)
if mime_type is None:
mime_type = 'image/png' # default to PNG if mime type cannot be determined
encoded_image = f'data:{mime_type};base64,{encoded_image}'

return FileReadObservation(path=filepath, content=encoded_image)
elif filepath.lower().endswith('.pdf'):
with open(filepath, 'rb') as file:
pdf_data = file.read()
encoded_pdf = base64.b64encode(pdf_data).decode('utf-8')
encoded_pdf = f'data:application/pdf;base64,{encoded_pdf}'
return FileReadObservation(path=filepath, content=encoded_pdf)
elif filepath.lower().endswith(('.mp4', '.webm', '.ogg')):
with open(filepath, 'rb') as file:
video_data = file.read()
encoded_video = base64.b64encode(video_data).decode('utf-8')
mime_type, _ = mimetypes.guess_type(filepath)
if mime_type is None:
mime_type = 'video/mp4' # default to MP4 if MIME type cannot be determined
encoded_video = f'data:{mime_type};base64,{encoded_video}'

return FileReadObservation(path=filepath, content=encoded_video)

with open(filepath, 'r', encoding='utf-8') as file:
lines = read_lines(file.readlines(), action.start, action.end)
except FileNotFoundError:
Expand Down

0 comments on commit 8c00d96

Please sign in to comment.