| Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago | 1 | # The ClojureDart half |
| 2 | |
| 3 | Nothing here builds yet. This is the boundary, drawn before the port rather |
| 4 | than after it, so that the question "can this file go on the phone?" has a |
| 5 | filesystem answer. |
| 6 | |
| 7 | ## The three trees |
| 8 | |
| 9 | ``` |
| 10 | common/ .cljc both compilers. No jolt, no glimmer, no dart. |
| 11 | src/ .clj jolt: glimmer, jolt.ffi, the cosmic and tui backends. |
| 12 | flutter/ .cljd ClojureDart: Flutter widgets, dart:io, dart:ffi. |
| 13 | ``` |
| 14 | |
| 15 | The extension is the boundary and the compilers enforce it. ClojureDart reads |
| 16 | `.cljd` and `.cljc` and never `.clj`, so a namespace that reaches for |
| 17 | `jolt.host` cannot accidentally end up in the APK — it is a `.clj` and the Dart |
| 18 | compiler cannot see it. jolt reads all three, which is why `common/` works at |
| 19 | all: one copy of `frq.clock`, compiled twice. |
| 20 | |
| 21 | Where both need a namespace but the answer differs, `.cljd` wins over `.cljc` |
| 22 | in ClojureDart's own resolution, so a file here shadows a shared one without |
| 23 | either side knowing. Reader conditionals work too, with one trap from |
| 24 | ClojureDart's FAQ: the `:clj` feature is always on under cljd, so `:clj` goes |
| 25 | **last** in a conditional, and macro code that wants the Clojure path during |
| 26 | host evaluation asks for `:cljd/clj-host`. |
| 27 | |
| 28 | ## What has crossed |
| 29 | |
| 30 | `frq.io` is the seam — the host's job named once, with `frq.io.jolt` answering |
| 31 | it on the desktop and `frq.io.dart` here. It carries the filesystem, the |
| 32 | environment, the config directory and the clock. |
| 33 | |
| 34 | Moved to `common/` and running under jolt today: |
| 35 | |
| 36 | | namespace | lines | note | |
| 37 | |---------------|-------|-------------------------------------------------| |
| 38 | | `frq.emoji` | 1,914 | data; nothing to port | |
| 39 | | `frq.glyphs` | 84 | data | |
| 40 | | `frq.av.dial` | 117 | already touched neither jolt nor glimmer | |
| 41 | | `frq.clock` | 93 | zone-hunting moved into the backends | |
| 42 | | `frq.store` | 122 | `install -m 600` became `write-private-file!` | |
| 43 | |
| 44 | `frq.clock` is the shape the rest should follow. It used to open with four |
| 45 | guesses at the reader's zone — `TZ`, the target of `/etc/localtime`, the file |
| 46 | itself by path, then Android's `persist.sys.timezone` — and then convert days |
| 47 | to a date by printing one with `jolt.time.local` and taking a `subs` of the |
| 48 | result. Both are gone: the guessing is a libc question and lives in |
| 49 | `frq.io.jolt`, where Dart answers it in one call instead; the conversion is |
| 50 | eleven lines of Hinnant's algorithm, checked against `java.time.LocalDate` for |
| 51 | every day from 1901 to 2052. |
| 52 | |
| 53 | ## What has not |
| 54 | |
| 55 | Roughly 4,000 lines are portable in substance and still `.clj` because the seam |
| 56 | does not reach far enough yet. In the order worth doing them: |
| 57 | |
| 58 | 1. **`frq.wire`, `frq.msgsig`** — need a crypto seam beside the io one. |
| 59 | 2. **`frq.irc`** (433) — the parser is pure; the reader is a blocking thread in |
| 60 | a `future`, and Dart has no threads. It becomes a `Stream` over |
| 61 | `SecureSocket`, which is also what makes TLS work on the phone at all. |
| 62 | 3. **`frq.atproto`** (209), **`frq.oauth`** (182) — hand-rolled HTTPS over |
| 63 | `jolt.mvn-http`'s OpenSSL bindings, which is why sign-in is desktop-only |
| 64 | today. `dart:io` has TLS in the runtime; this is the single biggest thing |
| 65 | the port buys. |
| 66 | 4. **`frq.state`** (1,930) — mostly portable logic, but its ratoms are |
| 67 | glimmer's. Needs the reactive layer decided first. |
| 68 | 5. **`frq.app`** (1,834) — not a port. Flutter brings its own reconciler, so |
| 69 | the screens are rewritten against `cljd.flutter`. |
| 70 | |
| 71 | Not coming: `frq.tui` (no terminal Flutter), `frq.cosmic` (libcosmic is |
| 72 | desktop-only), and the media plane — `moq/`, `codec/`, `capture/`, `av/`, about |
| 73 | 3,800 lines of FFI against C libraries that do not exist on Android either way. |
| 74 | `dart:ffi` does not conjure V4L2; that half wants Flutter's camera and audio |
| 75 | plugins and is its own project. |
| 76 | |
| 77 | ## To make this real |
| 78 | |
| 79 | 1. Pin `tensegritics/clojuredart` in `deps.edn` — it says `PIN-ME`. |
| 80 | 2. Get a Flutter SDK and the cljd toolchain into the flake. Neither is there. |
| 81 | 3. `clj -M:cljd init`, then `clj -M:cljd flutter`. (There is no `release` |
| 82 | subcommand — a release build is `clj -M:cljd compile` followed by |
| 83 | `flutter build`.) |
| 84 | 4. `frq.io.dart` is written from the docs and has never been compiled. Expect |
| 85 | its interop to be wrong in detail. |
| 86 | |
| 87 | There is no other APK to fall back on. The jolt one — `nix/android.nix`, the |
| 88 | `android/` manifest and Java glue, the `.#apk` outputs and `just apk` — is |
| 89 | gone, along with the jvui and Vidya backends it painted through. Until step 2 |
| 90 | exists, frq has no Android build at all, and that is the honest state. |