- Overview
- Quickstart
- Language/library references
- Getting up and running
- Webserial / web bluetooth considerations
- Build cheatsheet
- Repl cheatsheet
A my-first-cljs-project implementing the following completely unrelated functions:
- A webserial terminal (implemented using the clj-statecharts library)
- A persistent bi-temporal database in the browser using datascript backed by indexedDB for transaction log storage
- local file storage using OPFS (the Origin Private File System)
- Plotly.js plot integration
It includes shadow-cljs builds demonstrating the above using two different frontend libraries/approaches:
The re-frame
branch of this repository includes an earlier incarnation of much of the above using re-frame and UiX2.
The app includes a janky time-travelling TODO mvc view demoing the bi-temporal datascript implementation, and a similarly nasty page allowing files to be created and viewed.
There are historic bits of integration with supabase in varying states of repair, but I have yet to revisit this since switching from re-frame to replicant.
The OPFS file storage implementation uses a metadata index stored in indexedDB. This is fairly redundant as yet, but the additional metadata will be useful for a future sync implementation to S3 or whatever.pp
The persistent datascript domain database is embedded in the app view db/atom. Idea being that we differentiate between transient/disposable view data and more durable domain data that we want to persist and for which we want a strong audit trail (hence transaction log). I’ll probably migrate this to an independent atom in the future and just store relevant views of the data in the app db.
All this stuff is playground/POC level — immature and not thoroughly tested.
This repo includes three app entry points:
- ./src/stack/examples/app.cljs
- The replicant example app
- ./src/stack/examples/re_frame/app.cljs
- The re-frame example app
- ./src/cljserial/app.cljs
- The development app
The last of these just duplicates the replicant example for now. It’s intended to diverge – I find it useful to keep general purpose / re-usable code in the `stack` namespace and app/domain specifics in their own namespace.
It also includes unit test, css and portfolio (component driven development) builds/watches.
Other relevant entry points....
- ./src/stack/example/model.cljs
- ./src/stack/examples/dispatch.cljs
- ./src/stack/examples/re_frame/model.cljs
bb watch:replicant
This will serve the following:
- Example app
- http://localhost:8083
- Portfolio
- http://localhost:8082
- Unit test runner
- http://localhost:8081
bb watch:local
This will serve the following:
- App
- http://localhost:8080
- Portfolio
- http://localhost:8082
- Unit test runner
- http://localhost:8081
Babashka tasks
bb tasks
The app targets include both re-frame-10x (at the right of the app window) and cljs-react-devtools (at the foot) To show/hide the re-frame-10x panel, use Ctrl+Shift+x. To show/hide the cljs-react-dev-tools panel, use Ctrl+Shift+Meta+R.
shadow-cljs includes a browser based interface allowing the runtime (function defs and data) to be inspected and modified – effectively a GUI for the REPL. Accessible at http://localhost:9630
This includes some quick and dirty wiring for the excellent flow-storm debugger.
The provided nix flake also installs the required dependencies. Babashka tasks have been added to launch the debugger for each of the build targets. Start your watch as usual with bb watch:replicant
or whatever, then in another window, bb flow:replicant
.
There are definitely more ergonomic ways to wire it in and launch it (thinking particularly in the context of doing a release build without flowstorm), but this is as far as I’ve gotten…
(Don’t judge me, I’m a C programmer)
- Clojure style guide
- Wot it says on the tin....
- from bash to babashka
- Useful blog post, pointing at some handy FS-related libraries embedded in babashka
Pretty console logging requires custom formatters to have been enabled for your browser dev tools window – see: https://github.com/binaryage/cljs-devtools/blob/master/docs/installation.md
Without this config, the log entries will contain lots of less useful js data structure.
All of the core functionality in this initial prototype is suitable for deployment as a static site without additional backend infrastructure. However this repository is a testbed for a project that will need auth, storage etc., and to that end we’ve been kicking the tires of a couple of options there. Initial work was with AWS (some historic info retained in a subsection below), but since switched to supabase for a number of reasons:
- Offline-first development workflow
- Supabase CLI makes it very easy to stand up a complete local development environment in Docker containers.
- Portability
- Supabase provide PAAS subscriptions, however also allow you to self-host elsewhere.
- Ease of use
- Supabase is a more tightly integrated product than the extensive AWS ecosystem of services, which should reduce the complexity / cognitive overload potential for a relatively small dev team.
Essential commands
supabase start
supabase status
supabase stop
When running in docker, open the supabase UI at http://localhost:54323 Detailed notes can be found here: ./supabase.org
Historic notes on AWS integration can be found here:
As of Feb 2025, the Web Serial and Web Bluetooth APIs are supported only on chrome/chromium, edge and opera. See the Mozilla Developer Network pages on Web Bluetooth and Web Serial for more info.
On chrome/chromium use of the web bluetooth API requires a couple of optional flags to be enabled as follows
- Enter ‘chrome://flags’ in the URL bar
- Type ‘web bluetooth’ in the search field
- Ensure the following options are all enabled:
- Web Bluetooth
- Use the new permissions backend for Web Bluetooth
- Web Bluetooth confirm pairing support
N.B. On linux (well, Arch derivatives anyway), access to Bluetooth and serial port services as an privileged user require you to be a member of the ‘lp’ and ‘uucp’ groups, respectively. To set this, enter the following command in your terminal:
sudo usermod -aG lp,uucp <your-username>
You’ll need to logout and login again for the new membership privileges to take effect.
In addition, many distributions (including Manjaro) install laptop-mode-tools by default, putting certain USB devices to sleep when running on battery power.
To disable this for the legacy MBT CD, you need to add the USB device ID for its integral FTDI module to a blacklist.
In /etc/laptop-mode/conf.d/runtime-pm.conf
, locate the line beginning AUTOSUSPEND_RUNTIME_DEVID_BLACKLIST
and amend it to include 0403:6015
(the USB device ID for the FTDI module).
AUTOSUSPEND_RUNTIME_DEVID_BLACKLIST="0403:6015"
Similar measures may be required for the USB interface to the MBX CD hardware.
See the Laptop Mode Tools section of the Arch linux WIKI for further information.
The majority of these are handled by via:
In addition, fontawesome is required at build time to unpack svg for icons. This can be downloaded into your source tree by executing the snippet below (from https://github.com/cjohansen/fontawesome-clj):
clojure -Sdeps "{:deps {no.cjohansen/fontawesome-clj {:mvn/version \"2024.01.22\"} \
clj-http/clj-http {:mvn/version \"3.12.3\"} \
hickory/hickory {:mvn/version \"0.7.1\"}}}" \
-M -m fontawesome.import :download resources 6.4.2
See https://shadow-cljs.github.io/docs/UsersGuide.html#_clojurescript_repl
To open a repl session from a shell prompt
npx shadow-cljs cljs-repl app
Currently using Emacs/Cider. The docs provided for cider are excellent. Read them.