Skip to content

Commit

Permalink
File tree initial support
Browse files Browse the repository at this point in the history
  • Loading branch information
skryukov committed Feb 4, 2024
1 parent 681d304 commit 2590c45
Show file tree
Hide file tree
Showing 12 changed files with 1,080 additions and 236 deletions.
357 changes: 337 additions & 20 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
"@monaco-editor/react": "^4.4.4",
"@ruby/3.3-wasm-wasi": "^2.5.0",
"@ruby/wasm-wasi": "^2.5.0",
"nanoid": "^5.0.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-arborist": "^3.4.0",
"react-dom": "^18.2.0",
"react-icons": "^5.0.1"
},
"devDependencies": {
"@types/react": "^18.2.47",
Expand Down
155 changes: 3 additions & 152 deletions src/components/App/App.tsx
Original file line number Diff line number Diff line change
@@ -1,163 +1,14 @@
import { useEffect, useState } from "react";
import Editor from "@monaco-editor/react";

import { runWASI } from "../../engines/wasi";
import cs from "./styles.module.css";
import { RbValue } from "@ruby/wasm-wasi";
import { File, Directory } from "@bjorn3/browser_wasi_shim";
import { decode, encode, workDir } from "../../engines/wasi/editorFS.ts";

export default function App() {
const [loading, setLoading] = useState(true);
// TODO: get first file from workDir
const [code, setCode] = useState(decode((workDir.dir.contents["main.rb"] as File).data));
const [result, setResult] = useState("Press run...");
const [log, setLog] = useState<string[]>([]);
const [editorValueSource, setEditorValueSource] = useState<"result" | "logs">("result");
// object of gems and their versions as values
const [currentFile, setCurrentFile] = useState<File>(workDir.dir.contents["main.rb"] as File);

const runVM = (code: string, onSuccess?: (result: RbValue) => void, onError?: Function) => {
setLoading(true);
setLog([]);
setResult("");
setEditorValueSource("logs");
const setStdout = (line: string) => {
console.log(line);
setLog((old) => [...old, line]);
};
const setStderr = (line: string) => {
console.warn(line);
setLog((old) => [...old, `[error] ${line}`]);
};
// setTimeout is needed to allow the loading status to render
setTimeout(() =>
runWASI({ code, setResult, setStdout, setStderr })
.then((result) => {
setEditorValueSource("result");
onSuccess && onSuccess(result);
})
.catch((err) => {
setLog((old) => [...old, `[error] ${err}`]);
setEditorValueSource("logs");
onError && onError(err);
})
.finally(() => setLoading(false))
, 20);
};
import { Editor } from "../Editor/Editor.tsx";

const runCode = () => {
runVM(`require "bundler/setup";${code}`)
};
const bundleInstall = () => {
runVM(`require "bundler/cli";require "bundler/cli/install";Bundler::CLI::Install.new({path: './gems'}).run`,
() => {
setResult("Bundle install successful (see logs for details)")
},
() => {
setResult("Bundle install failed (see logs for details)")
}
);
}

const handleEditorChange = (value: string | undefined) => {
setCode(value || "");
};

useEffect(() => {
currentFile.data = encode(code);
}, [code])

export default function App() {
return (
<div className={cs.container}>
<div className={cs.header}>
<h1 className={cs.title}>Ruby WASI Playground</h1>
</div>
<div className={cs.menu}>
<label className={cs.menuLabel}>
Files
</label>

<div className={cs.menuFiles}>
{Object.keys(workDir.dir.contents).map((file) => (
<div className={`${cs.menuFile} ${currentFile === workDir.dir.contents[file] ? cs.menuFileActive : ''}`} key={file} onClick={() => {
const fileOrDir = workDir.dir.contents[file];
if (fileOrDir instanceof Directory) {
//
} else if (fileOrDir instanceof File) {
setCurrentFile(fileOrDir);
setCode(decode(fileOrDir.data));
}
}}>
{file}
</div>
))}
</div>
</div>

<div className={cs.editor}>
<div className={cs.editorText}>
<Editor
height="100%"
width="100%"
theme="vs-dark"
defaultLanguage="ruby"
value={code}
onChange={handleEditorChange}
onMount={() => setLoading(false)}
options={{
wordWrap: "on",
minimap: { enabled: false },
overviewRulerBorder: false,
hideCursorInOverviewRuler: true
}}
/>
</div>
<div className={cs.editorFooter}>
<div className={cs.editorLoading}>
{loading && "loading..."}
</div>
<button className={`${cs.installButton} ${loading ? cs.buttonDisabled : ""}`} disabled={loading}
onClick={() => !loading && bundleInstall()}>
Bundle install
</button>
<button className={`${cs.runButton} ${loading ? cs.buttonDisabled : ""}`} disabled={loading}
onClick={() => !loading && runCode()}>
Run code
</button>
</div>
</div>

<div className={cs.output}>
<div className={cs.switchButtons}>
<button className={`${cs.switchButton} ${editorValueSource === "result" ? cs.switchButtonActive : ""}`}
onClick={() => setEditorValueSource("result")}>
Result
</button>
<button className={`${cs.switchButton} ${editorValueSource === "logs" ? cs.switchButtonActive : ""}`}
onClick={() => setEditorValueSource("logs")}>
Logs
</button>
</div>
<div className={cs.editorText}>
<Editor
height="100%"
width="100%"
theme="vs-dark"
value={editorValueSource === "result" ? result : log.join("\n")}
language="shell"
options={{
wordWrap: "on",
lineNumbers: "off",
readOnly: true,
minimap: { enabled: false },
overviewRulerBorder: false,
renderLineHighlight: "none",
hideCursorInOverviewRuler: true
}}
/>
</div>
</div>
<Editor />
</div>
);
}
1 change: 0 additions & 1 deletion src/components/App/spinner.svg

This file was deleted.

103 changes: 42 additions & 61 deletions src/components/App/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,75 +39,33 @@
padding: 16px 8px;
}

.menuLabel {
font-size: 14px;
.menuHead {
font-size: 16px;
font-weight: bold;
color: #f1f1f1;
padding: 8px 8px 16px;
display: block;
}

.menuFile {
cursor: default;
padding: 8px 8px 8px 16px;
border-radius: 8px;
margin-bottom: 8px;
display: flex;
align-items: center;
}

.menuFileActive {
background-color: #424242;
.menuLabel {
flex: 1;
}

.menuInputButton {
white-space: nowrap;
.menuButton {
border: none;
background: transparent;
display: inline-flex;
align-items: center;
font-size: 16px;
color: #424242;
}

.menuInput {
background-color: transparent;
.menuButton:hover {
color: #f1f1f1;
padding: 8px 50px 8px 16px;
text-align: left;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin-bottom: 8px;
border: 1px solid #ffffff14;
border-radius: 8px;
}

.menuInstallButton {
transform: translateX(-100%);
background-color: #388E3C;
border: none;
color: #f1f1f1;
font-weight: bold;
padding: 8px 16px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
cursor: pointer;
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
margin-top: 8px;
}

.menuDependencies {
padding-left: 16px;
}

.menuDependency {
padding-bottom: 4px;
}

.menuSpinner {
fill: #f1f1f1;
width: 16px;
height: 16px;
margin-left: 8px;
display: inline-block;
vertical-align: middle;
}

.editor {
grid-area: editor;
display: flex;
Expand All @@ -118,6 +76,29 @@
margin: 0 4px;
}

.editorHeader {
display: flex;
flex-direction: row;
padding: 0 16px 0 16px;
margin-bottom: 16px;
align-items: center;
justify-content: space-between;
}

.editorLabel {
font-size: 16px;
color: #f1f1f1;
font-weight: bold;
padding: 8px 16px;
border-radius: 8px;
background-color: #424242;
}

.editorPlaceholder {
font-size: 16px;
padding: 8px 16px;
}

.editorText {
padding: 0 8px;
flex: 1;
Expand Down Expand Up @@ -149,7 +130,7 @@
text-decoration: none;
display: inline-block;
font-size: 14px;
cursor: pointer;
cursor: default;
border: none;
border-radius: 8px;
}
Expand All @@ -172,7 +153,7 @@
text-decoration: none;
display: inline-block;
font-size: 14px;
cursor: pointer;
cursor: default;
border-radius: 8px;
}

Expand All @@ -190,7 +171,7 @@
text-decoration: none;
display: inline-block;
font-size: 14px;
cursor: pointer;
cursor: default;
border-radius: 8px;
margin-left: 8px;
}
Expand All @@ -200,5 +181,5 @@
}

.buttonDisabled {
background-color: #424242;
background-color: #424242 !important;
}
Loading

0 comments on commit 2590c45

Please sign in to comment.