feat: pass contents of directory to front end

This mainly focuses on passing directory info to the front end. It isn't
a complete version of that, but it's enough to plan out some details of
the styling
This commit is contained in:
Sylvia Crowe 2024-05-20 11:39:23 -07:00
parent 501b05a3e3
commit 72dbf94f9a
5 changed files with 61 additions and 2 deletions

View File

@ -53,6 +53,18 @@ function StreamingPreview({ fileInfo }: { fileInfo: FileInfo }) {
return <CenteredDiv>Preview Not Supported</CenteredDiv>;
}
function DirectoryPreview({ contentAtom }: { contentAtom: jotai.Atom<Promise<string>> }) {
const contentText = jotai.useAtomValue(contentAtom);
let content: FileInfo[] = JSON.parse(contentText);
return (
<div className="view-preview view-preview-directory">
{content.map((finfo) => (
<span>{finfo.path}</span>
))}
</div>
);
}
function PreviewView({ blockId }: { blockId: string }) {
const blockDataAtom: jotai.Atom<BlockData> = blockDataMap.get(blockId);
const fileNameAtom = useBlockAtom(blockId, "preview:filename", () =>
@ -118,6 +130,9 @@ function PreviewView({ blockId }: { blockId: string }) {
</div>
);
}
if (mimeType === "directory") {
return <DirectoryPreview contentAtom={fileContentAtom} />;
}
return (
<div className="view-preview">
<div>Preview ({mimeType})</div>

View File

@ -63,4 +63,8 @@
object-fit: contain;
}
}
&.view-preview-directory {
flex-direction: column;
align-items: start;
}
}

View File

@ -92,6 +92,9 @@ function Widgets() {
<div className="widget" onClick={() => clickPreview("build/appicon.png")}>
<i className="fa fa-solid fa-files fa-fw" />
</div>
<div className="widget" onClick={() => clickPreview("~")}>
<i className="fa fa-solid fa-files fa-fw" />
</div>
<div className="widget" onClick={() => clickPlot()}>
<i className="fa fa-solid fa-chart-simple fa-fw" />
</div>

View File

@ -5,6 +5,7 @@ package fileservice
import (
"encoding/base64"
"encoding/json"
"fmt"
"os"
"path/filepath"
@ -41,9 +42,9 @@ func (fs *FileService) StatFile(path string) (*FileInfo, error) {
if err != nil {
return nil, fmt.Errorf("cannot stat file %q: %w", path, err)
}
mimeType := utilfn.DetectMimeType(path)
mimeType := utilfn.DetectMimeType(cleanedPath)
return &FileInfo{
Path: wavebase.ReplaceHomeDir(path),
Path: cleanedPath,
Size: finfo.Size(),
Mode: finfo.Mode(),
ModTime: finfo.ModTime().UnixMilli(),
@ -63,6 +64,35 @@ func (fs *FileService) ReadFile(path string) (*FullFile, error) {
if finfo.Size > MaxFileSize {
return nil, fmt.Errorf("file %q is too large to read, use /wave/stream-file", path)
}
if finfo.IsDir {
innerFilesEntries, err := os.ReadDir(finfo.Path)
if err != nil {
return nil, fmt.Errorf("unable to parse directory %s", finfo.Path)
}
var innerFilesInfo []FileInfo
for _, innerFileEntry := range innerFilesEntries {
innerFileInfoInt, _ := innerFileEntry.Info()
innerFileInfo := FileInfo{
Path: innerFileInfoInt.Name(),
Size: innerFileInfoInt.Size(),
Mode: innerFileInfoInt.Mode(),
ModTime: innerFileInfoInt.ModTime().UnixMilli(),
IsDir: innerFileInfoInt.IsDir(),
MimeType: "",
}
innerFilesInfo = append(innerFilesInfo, innerFileInfo)
}
filesSerialized, err := json.Marshal(innerFilesInfo)
if err != nil {
return nil, fmt.Errorf("unable to serialize files %s", finfo.Path)
}
return &FullFile{
Info: finfo,
Data64: base64.StdEncoding.EncodeToString(filesSerialized),
}, nil
}
cleanedPath := filepath.Clean(wavebase.ExpandHomeDir(path))
barr, err := os.ReadFile(cleanedPath)
if err != nil {

View File

@ -634,6 +634,13 @@ func DetectMimeType(path string) string {
if mimeType := mime.TypeByExtension(ext); mimeType != "" {
return mimeType
}
stats, err := os.Stat(path)
if err != nil {
return ""
}
if stats.IsDir() {
return "directory"
}
fd, err := os.Open(path)
if err != nil {
return ""