Hello All!
I’d like to describe the setup for an Elm desktop app that I have been experimenting with. It gives the app list, read, and write access to designated directory on the user’s computer. Authors like to have ownership of their work!
The current solution is quite clunky, so I’d very much like some feedback on whether (a) this is a feasible approach, (b) how to make it more robust, and [c] how to improve it (a totally different approach is OK).
The app in question bundles a pure Elm text editor with compilers to Html for an extended version of Markdown and MiniLaTeX, which is a subset of LaTeX. The extended version of Markdown permits one to render LaTeX-style formulas, SVG images, and poetry. See Extended Markdown Live to see how this flavor of Markdown works. Also, see screenshots of the app at the end.
The command-line script for the app uses Velociraptor, and it is invoked by saying vr app
. Velociraptor runs shell scripts as well as Deno scripts and programs. Deno is a successor to Node which is written by Ryan Dahl, the developer of Node. Running vr app
does two things. The first is to launch the Elm app using the Deno program file_server
. The second is to launch a web server written in Deno. Such a server is written in Typescript and makes use of the Deno standard library. The Elm app then talks to the server via Http. As a result, the Elm app can list files in the designated director, read them into memory, and write them back to disk as they are are edited. The same setup can be use to publish the files on the web via the same server program hosted remotely.
Installation, part 1
Now comes the sticky points. How can one install and run such an app?
The very poor solution I have at the moment is to extract the needed files and directories from the development project and store them in a directory, say app
. To deploy the app, one must copy this directory to one’s computer. (It could be bundled into a tar file, for example). Here is the contents of that directory:
.
├── Main.js
├── assets
├── data
├── index.html
├── scripts.yaml
└── server
3 directories, 3 files
You see some familiar characters: Main.js
is the compiled Elm app which is referenced in index.html
. The index.html
file imports various resources from assets
, e.g, MathJax.js
, which is used to render mathematical formulas. The data
directory is a default directory for the user’s files. Finally, server
contains the Typescript files for the web server.
Installation, part 2
Once the directory has been copied/extracted, one has to set up Deno and Velociraptor, e.g.,
$ curl -fsSL https://deno.land/x/install/install.sh | sh
$ deno install -qA -n vr https://deno.land/x/velociraptor/cli.ts
Running the app
Then, to run the app, cd
into app
and say vr app
. When one does this, vr
finds the first scripts.yaml
in the current directory of a parent, then runs that script with the given argument. Here is the full script:
env:
APP_ROOT: /Users/jxxcarlson/dev/elm/projects/muEdit
DATA_ROOT: /Users/jxxcarlson/Documents/mudocs
scripts:
app:
desc: run editor-app & start the server
cmd:
pll:
- file_server ${APP_ROOT}
- cmd: ${APP_ROOT}/server/server.ts
allow:
- net
- env
- write
- read
- open -a /Applications/Google\ Chrome.app/ http://0.0.0.0:4507/index.html
Note that the user must configure APP_ROOT
, which is where the files Main.js
etc. live, as well as DATA_ROOT
, which is where the user’s files will be stored. The commands listed under pll
will be executed in parallel. The allow
clause gives Deno permission to access to the file system, the net, and the environment.
Robustness
Accepting for the moment the extreme hackiness of this solution, there are at least two serious problems:
-
Latency. The server is recompiled each time one invokes
vr app
. That takes some small number of seconds. As a result, one may see an empty file list if one lists the files too quickly. This can be solved by precompiling the server, but I haven’t done that yet. -
Shutdown. It can happen that the processes for
file_server
and/orserver
are still running when the user invokesvr app
at a future date. Then the user will get an error. Of course you can useps aux | grep WHATEVER
andkill -9
to fix things, but who wants to do that?
One can use vr export app
to compile a version of the needed script invocation, then copy it from ./bin
to ~/.deno/bin/
so that the the app can be invoked from anywhere by saying app
. Of course one has to put ~/.deno/bin/
in ones PATH
. A small but useful improvement.
Conclusions and self-criticisms
Ugh! This approach works, but installation requires many steps. Too much!! And too much user configuration. While this approach does give a way for Elm apps to have list, read, and write access to the file system, it is an exercise in extreme hackery, useful for at most one person in the universe.
There must be a better way. One approach, which I have not studied yet, but which may be the way to go, is embodied in Aaron vonderHaar’s elm-desktop-app. As I understand it, it accesses the user’s file system using ports to persist state, and so could likely be adapted to the use case I envisage. What is needed is the ability to read and write files in in designated directory and also the ability to read, parse, and write a file manifest.yaml
which lists file names and other metadata. No Json here, because one design goal is for everything to be (easily) human-readable.
Anyway, I look forward to your suggestions, both for improving the current approach and to moving to a better one. The ultimate goal is for a simple one-step installation process that non-developers can use (studens, teachers, math & science professors, etc.)
As a side note, I would like to say that I have very much enjoyed working with Deno and Velociraptor. Clean and elegant! (Here is a blog post on Deno).