- 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)
It includes shadow-cljs builds demonstrating the above using two different frontend libraries/approaches:
- Replicant (+ portfolio for component-driven development)
- re-frame (with Uix2)
The replicant build 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.
The re-frame example has bits of integration with supabase in varying states of repair, but this hasn’t been a recent focus
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
In both the re-frame and replicant examples, 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).
All this stuff is playground/POC level — immature and not thoroughly tested.
This repo includes three app entry points:
- ./src/stack/examples/replicant/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/examples/replicant/model.cljs
- ./src/stack/examples/replicant/dispatch.cljs
- ./src/stack/examples/re_frame/model.cljs
bb watch:replicant
This will serve the following:
- Example app
- http://localhost:8084
- Portfolio
- http://localhost:8082
- Unit test runner
- http://localhost:8081
bb watch:re-frame
This will serve the following:
- Examples app
- http://localhost:8083
- 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
watch:local Run app (port 8080) with local dockerised supabase (test and portfolio builds enabled). watch:cloud Run app (port 8080) with cloud-hosted supabase (test and portfolio builds enabled). watch:replicant Run replicant (port 8084) with local dockerised supabase (test and portfolio builds enabled). watch:re-frame Run examples (port 8083) with local dockerised supabase (test and portfolio builds enabled). watch:css Tailwind watch task watch:postcss Tailwind watch task watch:portfolio Run portfolio component-driven test UI on http://localhost:8082 watch:test Run frontend unit test runner on http://localhost:8081 build:release Build release version of the app. build:pages Compile static version of the app for upload to gitlab pages. sb:start sb:stop sb:edn Extract environment for local dockerised supabase to edn. tailwind:edn Extract list of DaisyUI themes included via tailwind config for use in UI theme selector.
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
Very useful dev tooling for re-frame. Shows up as a sidebar for the dev build. Show/hide using Ctrl-Shift-x. See https://github.com/day8/re-frame-10x for further info.
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.