Skip to content

Commit

Permalink
feat(terminal): Add support for running applications inside the files…
Browse files Browse the repository at this point in the history
…ystem
  • Loading branch information
FranklinWaller committed Oct 31, 2019
1 parent 96ec755 commit f6e8fef
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 15 deletions.
4 changes: 4 additions & 0 deletions src/js/apps/Explorer/Explorer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
// justify-content: space-evenly;
flex-flow: wrap;
}

.toolbarButton {
color: white;
}
32 changes: 29 additions & 3 deletions src/js/apps/Explorer/Explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import * as React from 'react';
import AppHeader from '../../components/molecules/AppHeader';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import IconButton from '@material-ui/core/IconButton';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import FolderIcon from '@material-ui/icons/Folder';
import CreateNewFolderOutlinedIcon from '@material-ui/icons/CreateNewFolderOutlined';
import ArrowUpwardIcon from '@material-ui/icons/ArrowUpward';
import ListItemText from '@material-ui/core/ListItemText';
import Collapse from '@material-ui/core/Collapse';
import ExpandLess from '@material-ui/icons/ExpandLess';
Expand All @@ -18,11 +21,12 @@ const styles = require('./Explorer.scss');
export default function Explorer() {
const [isLocationsOpen, setLocationsOpen] = React.useState(true);
const [files, setFiles] = React.useState<Dirent[]>([]);
const [path, setPath] = React.useState('/');
const [path, setPath] = React.useState('');

React.useEffect(() => {
const wasmFs = InstanceBag.get<WasmFs>('fs');
const filesAndDirectories: any = wasmFs.fs.readdirSync(path, {
const newPath = path === '' ? '/' : path;
const filesAndDirectories: any = wasmFs.fs.readdirSync(newPath, {
encoding: "utf8",
withFileTypes: true,
});
Expand All @@ -37,9 +41,31 @@ export default function Explorer() {
}
}

function handleFolderUpButtonClick() {
const splittedPath = path.split('/');
splittedPath.pop();

const newPath = splittedPath.join('/');
setPath(newPath);
}

const currentPathFolder = path.split('/').pop();
const appHeaderTitle = currentPathFolder === '' ? 'Files' : currentPathFolder;

return (
<>
<AppHeader title="Files" menu={
<AppHeader title={appHeaderTitle} toolbar={
<>
<IconButton>
<CreateNewFolderOutlinedIcon className={styles.toolbarButton} />
</IconButton>
<IconButton onClick={handleFolderUpButtonClick}>
<ArrowUpwardIcon className={styles.toolbarButton} />
</IconButton>
</>
}

menu={
<List>
<ListItem button onClick={() => setLocationsOpen(!isLocationsOpen)}>
<ListItemText primary="Locations" />
Expand Down
7 changes: 6 additions & 1 deletion src/js/apps/Explorer/components/File/File.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
width: 25%;
}

.icon {
.iconWrapper {
width: 100%;
height: 60%;
}

.icon {
width: 100%;
height: 100%;
color: #94a9b3;
cursor: pointer;

Expand Down
17 changes: 16 additions & 1 deletion src/js/apps/Explorer/components/File/File.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
import * as React from 'react';
import Dirent from 'memfs/lib/Dirent';
import InsertDriveFileOutlinedIcon from '@material-ui/icons/InsertDriveFileOutlined';
import WebIcon from '@material-ui/icons/Web';
const styles = require('./File.scss');

interface Props {
file: Dirent;
onClick?: (folder: Dirent) => void;
}

function getFileIcon(file: Dirent): any {
const fileNameSplitted = file.name.toString().split('.');
const fileExtension = fileNameSplitted.pop();

switch(fileExtension) {
case 'wasm':
return <WebIcon className={styles.icon}/>
default:
return <InsertDriveFileOutlinedIcon className={styles.icon}/>
}
}

export default function File(props: Props) {
return (
<div className={styles.file}>
<InsertDriveFileOutlinedIcon className={styles.icon} onClick={() => props.onClick(props.file)} />
<div className={styles.iconWrapper} onClick={() => props.onClick(props.file)}>
{getFileIcon(props.file)}
</div>
<span>{props.file.name}</span>
</div>
);
Expand Down
4 changes: 4 additions & 0 deletions src/js/components/molecules/AppHeader/AppHeader.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ $drawer-width: 240px;
height: 100%;
}

.title {
flex-grow: 1;
}

.menuButton {
color: white !important;
}
Expand Down
6 changes: 5 additions & 1 deletion src/js/components/molecules/AppHeader/AppHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const styles = require('./AppHeader.scss');
interface Props {
title: string;
menu: React.ReactElement;
toolbar?: React.ReactNode;
children: React.ReactNode;
headerBackground?: string;
headerTextColor?: string;
Expand All @@ -27,9 +28,12 @@ function AppHeader(props: Props) {
{/* <IconButton edge="start" aria-label="open drawer" className={styles.menuButton}>
<MenuIcon />
</IconButton> */}
<Typography variant="h6" noWrap style={{ color: headerTextColor }}>
<Typography variant="h6" noWrap style={{ color: headerTextColor }} className={styles.title}>
{props.title}
</Typography>
<div>
{props.toolbar}
</div>
</Toolbar>
</AppBar>

Expand Down
5 changes: 3 additions & 2 deletions src/js/components/organims/AppWindow/AppTitleBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ function AppTitleBar(props: Props) {

return (
<header className={styles.appBar} style={{ backgroundColor: props.process.app.theme_color }}>
<IconButton aria-label="Back" style={{ color: textColor }}>
<div></div>
{/* <IconButton aria-label="Back" style={{ color: textColor }}>
<ArrowBackIcon />
</IconButton>
</IconButton> */}
{isDesktop && <span style={{ color: textColor }}>{props.process.app.short_name}</span>}
{!isDesktop && <Time style={{ color: textColor }} />}
<IconButton aria-label="Close" onClick={handleCloseClick} style={{ color: textColor }}>
Expand Down
4 changes: 2 additions & 2 deletions src/js/pages/HomePage/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ class HomePage extends React.Component<Props, State> {
isLoaded: false,
}

componentDidMount() {
async componentDidMount() {
const keys = KeyService.keysFromStorage();
bootup();
await bootup();

if (!this.props.user.info.fullName && keys) {
this.props.dispatch(loadUserInfo());
Expand Down
4 changes: 2 additions & 2 deletions src/js/services/BootService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import InstanceBag from "../InstanceBag";
import { createFs } from "./FileSystemService";

export function bootup() {
InstanceBag.set('fs', createFs());
export async function bootup() {
InstanceBag.set('fs', await createFs());
}
23 changes: 20 additions & 3 deletions src/js/services/FileSystemService.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// @ts-ignore
import WasmFs from "@wasmer/wasmfs/lib/index.esm";
import WasmFsType from '@wasmer/wasmfs';
import { IFs } from "memfs";
import { TFileId, IReadFileOptions } from "memfs/lib/volume";
import { TDataOut } from "memfs/lib/encoding";

export function createFs() {
export async function createFs() {
const envContents = '$PWD=/\n$SHELL=/\nPWD=/\nSHELL=/\n$PATH=/\nPATH=/\nUSER=franklinwaller\n$USER=franklinwaller';
const wasmFs = new WasmFs();
const wasmFs: WasmFsType = new WasmFs();

console.log('[] wasmFs -> ', wasmFs);
const wasm = await (await fetch('/wasm/cowsay.wasm')).arrayBuffer();

wasmFs.fs.mkdirSync('/Applications/');
wasmFs.fs.mkdirSync('/Library/');
Expand All @@ -14,10 +18,23 @@ export function createFs() {
recursive: true,
});

wasmFs.fs.writeFileSync('/Applications/cowsay.wasm', new Uint8Array(wasm));
wasmFs.fs.writeFileSync('/dev/null', envContents);
// wasmFs.fs.writeFileSync('.env', envContents);
wasmFs.fs.writeFileSync('/Users/.env', envContents);
wasmFs.fs.writeFileSync('/Users/franklinwaller/.env', envContents);

return wasmFs;
}

export function readFileAsync(fs: IFs, id: TFileId, options?: string | IReadFileOptions): Promise<TDataOut> {
return new Promise((resolve, reject) => {
fs.readFile(id, options, (error, data) => {
if (error) {
reject(error);
} else {
resolve(data);
}
})
});
}
8 changes: 8 additions & 0 deletions src/js/services/TerminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import WasmFs from "@wasmer/wasmfs";
import { fetchCommandFromWAPM } from "@wasmer/wasm-terminal/lib/unoptimized/wasm-terminal.esm";
// @ts-ignore
import { lowerI64Imports } from "@wasmer/wasm-transformer/lib/unoptimized/wasm-transformer.esm";
import { readFileAsync } from "./FileSystemService";

export default class TerminalService {
wasmFs: WasmFs;
Expand All @@ -22,8 +23,15 @@ export default class TerminalService {
}

async handleCommand(commandName: string, args: string[], envEntriest: any[]) {
console.log('[] commandName -> ', commandName);
console.log('[] args, envEntriest -> ', args, envEntriest);

// We are trying to execute a binary inside the system
if (commandName.endsWith('.wasm')) {
// Fetch the binary from the file system and return it
return readFileAsync(this.wasmFs.fs, commandName);
}

// if (commandName === "pwd") {
// const callbackCommand = async (args: string[], stdin: string) => {
// console.log('[] this -> ', this);
Expand Down

0 comments on commit f6e8fef

Please sign in to comment.