| Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago | 1 | # Working in this repo |
| 2 | |
| Six verbs, and the last of the nix 2e24e64 nandi 7h ago | 3 | ## The toolchain, and where builds happen |
| Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago | 4 | |
| The last of the Clojure 284b59c nandi 27m ago | 5 | There is no nix in the build. `tools/toolchain.sh` fetches Flutter (which |
| 6 | carries Dart) and Nim as sha256-pinned tarballs into `.toolchain/`, and every |
| 7 | `just` recipe runs inside the environment that script prints. The host brings |
| 8 | a C compiler, OpenSSL, git, curl, unzip and python3 — and GTK with the usual |
| 9 | CMake/Ninja/pkg-config for the Linux target. |
| 10 | |
| 11 | It used to carry a JDK, the Clojure CLI, a maven repo and an Android SDK as |
| 12 | well. Those were ClojureDart's and the APK's, and both are gone. |
| Six verbs, and the last of the nix 2e24e64 nandi 7h ago | 13 | |
| 14 | **Prefer Modal for a long build.** A cold Flutter toolchain plus a full |
| 15 | compile is a lot of laptop, and the containers in `.modal/` do it on a real |
| 16 | machine: |
| Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago | 17 | |
| 18 | ```bash |
| Six verbs, and the last of the nix 2e24e64 nandi 7h ago | 19 | just modal dev # the incremental Flutter loop |
| Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago | 20 | ``` |
| 21 | |
| One frontend where there were three, and a core that is not Clojure 438b247 nandi 11h ago | 22 | The containers in `.modal/` are not a third source tree: they are CI config |
| Six verbs, and the last of the nix 2e24e64 nandi 7h ago | 23 | that happens to live here, the way `.github/` would be. They run the very same |
| 24 | `tools/toolchain.sh`, which is why a plain Debian image is enough. |
| Build the window somewhere with room for it 1451151 nandi 5d ago | 25 | |
| 26 | `modal app logs` is no substitute for watching that command: it resolves |
| 27 | deployed apps by name, not the ephemeral one a `modal run` creates, and carries |
| 28 | nothing until the Sandbox starts — the image build streams to the client and |
| 29 | nowhere else. |
| 30 | |
| Six verbs, and the last of the nix 2e24e64 nandi 7h ago | 31 | The containers run as Modal **Sandboxes on a real VM** rather than under |
| 32 | gVisor: a real kernel, a working pty, and memory that is exactly what |
| 33 | `[resources] memory` asks for. |
| Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago | 34 | |
| Follow the xorg renames, and stop wrapping nix in distrobox a400a00 nandi 16d ago | 35 | One thing this container is *not* representative of: `/etc/localtime` is a |
| 36 | regular file here rather than a symlink, so anything that reads the zone out |
| Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago | 37 | of its path sees nothing. That is a real deployment shape, not an artefact — |
| 38 | frq.clock handles it. |
| Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago | 39 | |
| Build the window somewhere with room for it 1451151 nandi 5d ago | 40 | ## Never pipe a long task through `tail` |
| 41 | |
| 42 | `tail` and `head` do not emit anything until their input ends, so a build, a |
| 43 | test run or a deploy piped through one shows nothing at all until it is over — |
| 44 | and if it is killed or times out first, its output is lost with it. That is the |
| 45 | opposite of what you want from the commands that take longest. |
| 46 | |
| 47 | Let them write to the terminal, or `tee` them if you want a copy to grep |
| 48 | afterwards: |
| 49 | |
| 50 | ```bash |
| Six verbs, and the last of the nix 2e24e64 nandi 7h ago | 51 | modal run .modal/web/container.py 2>&1 | tee /tmp/frq-build.log |
| Build the window somewhere with room for it 1451151 nandi 5d ago | 52 | ``` |
| 53 | |
| 54 | Trim afterwards, on the file, where the whole run is still there to re-read. |
| 55 | The same goes for `grep` and `awk` in a live pipeline: they buffer when their |
| 56 | output is not a terminal, so pass `--line-buffered` / `fflush()` or watch the |
| 57 | file instead. |
| 58 | |
| One frontend where there were three, and a core that is not Clojure 438b247 nandi 11h ago | 59 | ## The Nim core |
| 60 | |
| The last of the Clojure 284b59c nandi 27m ago | 61 | `nim/` is the program. It owns the state, the screens, the IRC connection and |
| 62 | the signing; Flutter is a renderer over the widget tree it emits. Read |
| 63 | `nim/README.md` before touching it. |
| One frontend where there were three, and a core that is not Clojure 438b247 nandi 11h ago | 64 | |
| 65 | Two rules the ABI has, both of which cost a segfault to rediscover: |
| 66 | |
| 67 | * Every string the core returns is the **caller's** to free, with `frq_free`. |
| 68 | Nim's allocator is not Dart's. |
| 69 | * `frq_init` runs once before anything else. |
| 70 | |
| The last of the Clojure 284b59c nandi 27m ago | 71 | `dart/frq_core` is the other half of that seam. It is a plain Dart package and |
| 72 | not a Flutter one, deliberately: `flutter/pubspec.yaml` depends on the Flutter |
| 73 | SDK, so anything living there needs a Flutter toolchain to check one assertion |
| 74 | about a string, where this resolves and tests on its own. |
| 75 | |
| 76 | The rule that used to be here said new Dart-side code is written in Dart |
| 77 | rather than ClojureDart. There is no ClojureDart left for it to rule against, |
| 78 | but the reasoning it rested on still holds for the next thing: logic goes in |
| 79 | Nim, the platform goes in Dart, and neither is written in a third language |
| 80 | because it is already open. |
| The binding is Dart, and it works f7aea3b nandi 11h ago | 81 | |
| Six verbs, and the last of the nix 2e24e64 nandi 7h ago | 82 | `just test nim` and `just test dart` need no Flutter, which is most of the |
| The binding is Dart, and it works f7aea3b nandi 11h ago | 83 | point: the whole boundary is checkable in about a second. |
| One frontend where there were three, and a core that is not Clojure 438b247 nandi 11h ago | 84 | |
| The last of the Clojure 284b59c nandi 27m ago | 85 | There is no `common/` any more, and that rule went with it. It said a module |
| 86 | stays until there is a wasm build of the core, because a browser has no |
| 87 | dart:ffi — which was true, and the web target is gone rather than the rule |
| 88 | being wrong. Bringing it back means compiling the core to wasm, not restoring |
| 89 | ClojureDart. |
| One frontend where there were three, and a core that is not Clojure 438b247 nandi 11h ago | 90 | |
| The last of the Clojure 284b59c nandi 27m ago | 91 | ## The source trees |
| Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago | 92 | |
| 93 | ``` |
| The last of the Clojure 284b59c nandi 27m ago | 94 | nim/ the program: state, screens, IRC, signing |
| 95 | dart/frq_core the FFI binding — plain Dart, not a Flutter package |
| 96 | flutter/lib the renderer, and the app's entry point |
| Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago | 97 | ``` |
| 98 | |
| The last of the Clojure 284b59c nandi 27m ago | 99 | `nim/src/frq/ui.nim` builds a widget tree; `frq_core` carries it across the |
| 100 | FFI as JSON; `flutter/lib/nim_renderer.dart` walks it into Flutter widgets. |
| 101 | The renderer knows the tag vocabulary and nothing else — no screens, no state, |
| 102 | no idea what "connect" means. If a feature ever needs a change on both sides, |
| 103 | the boundary is in the wrong place. |
| 104 | |
| 105 | There used to be two more trees. `src/` was jolt and libcosmic; `common/` and |
| 106 | `flutter/src/` were ClojureDart, compiled for Android, Linux and the web. Both |
| 107 | are gone, and with the second went the APK and the web target: a browser has |
| 108 | no `dart:ffi`, and the APK wants `libfrqcore.so` cross-compiled for Android's |
| 109 | ABIs. What is left builds one thing, `just build desktop`. |
| 110 | |
| 111 | Two modules were never ported and are gone rather than moved: `frq.profile` |
| 112 | (the Bluesky profile behind a nick) and `frq.replies` (asking freeq what a |
| 113 | collapsed msgid was). Neither had a screen in the Nim app to appear on. |
| 114 | |