iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🗂

Building a terminal to manipulate local files from the browser

に公開

As part of my Golden Week activities as a "frontend development should be completed within the frontend" extremist, I created a prototype for a terminal that reads and writes local files in the browser.

https://web-shell.netlify.app/

Here is a view of mounting and manipulating local files.

Source code

mizchi/web-shell

How it works

  • Implement FS API using the File System Access API
  • Implement several Unix-like commands on xterm.js that interact with the FS
  • Pass the contents of open <file> to Monaco Editor, and write the content saved via Cmd-S back to the FS

The initial storage opened is the temporary storage from navigator.storage.getDirectory(), which is volatile depending on the browser's state (as specified in the documentation). The main intended use is to use mount to operate on local files.

Since the File System Access API is currently only supported by Chrome, it only works in Chrome.

https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API

Basic Usage

Unix-like commands such as cd, ls, cp, mkdir, and rm have been implemented. Tab completion works for items within the same directory. I'll implement deep path completion when I feel like it.

Items not yet implemented: Pipe processing like |, >, and *, standard I/O, and glob pattern enumeration are currently in progress.

WebShell-Specific Commands

  • The mount command mounts a specified directory to /<dirname>.
  • The open <file> command allows for editing within the built-in editor.
  • The bundle <file> command builds a specified file using Rollup. This is also reflected in the local file system.
  • The exec <file> command bundles and executes a file on a Web Worker. It also supports TypeScript.

Here is a sample:

# demo bundle on temporal storage
/workspace
# run
$ exec main.ts
# bundle to ./dist
$ bundle main.ts
gen > /workspace/dist/main.js

$ mount
# choose your local directory
# ...now I choose fs-test directory
/fs-test
# create empty file
$ touch test
$ ls
test

# open file on left editor(here)
$ open main.ts
# edit and (Ctrl or Cmd)+S
$ ls
test
main.ts
$ bundle main.ts
> gen > /fs-test/dist/main.ts

Future Plans

Since there's no sign of WebContainers becoming open-source, I decided to build something based on the same concept myself.

What is WebContainers?

Actually, I built this while deciphering https://wasi.rreverser.com/. That project runs WASI-built coreutils and connects I/O to WASI, whereas web-shell is just a "fake" that looks similar. I've implemented the mechanism for standard I/O, but I haven't yet built the mechanism for piping...

I actually started this to study WASI, and I intend to connect it to a WASI adapter from here. My goal is to make it so that commands written in Rust can run. Also, I wanted to create a place where I could try out hand-written WebAssembly text format.

The novelty at this point is probably the integration of Monaco Editor with the open command. Additionally, for things like Rollup + TS + Terser that naturally run on the web, I've implemented them directly as commands.

The significance for me is that by building it from scratch and understanding the architecture, it has become a foundation that I can expand in any way I want.

Discussion