nandi/frqpublic Fork 0
297564c
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Merge remote-tracking branch 'origin/main' into claude/nick-in-title-cd90e2

nandi committed 2026-08-30T18:50:13-07:00 Browse files
297564c parents: b9725a2 bab5e3c
modified android/build-jolt-boot.sh +6 -1
@@ -9,7 +9,12 @@ VIDYA="${VIDYA:-$(cd "$ROOT/../vidya" && pwd)}"
99 # has to be named here.
1010 GLIMMER="${GLIMMER:-$HOME/.jolt/gitlibs/https___github.com_jolt-lang_glimmer/5581c331c51aff989259b9e8e92ec920fe5e6741/src}"
1111 OUT="${1:?usage: build-jolt-boot.sh OUTPUT_DIRECTORY}"
12-JOLT="${JOLT:-$(command -v jolt)}"
12+# The DotSlash-pinned jolt beside this script, not whatever is on PATH: an
13+# upstream jolt cannot open a TLS connection on Android — it reads the socket
14+# address out of `struct addrinfo` at glibc's offset, which is Bionic's
15+# `ai_canonname` — so a build made with one produces an APK that cannot sign in
16+# or send a picture. Override with JOLT= to use another.
17+JOLT="${JOLT:-$ROOT/scripts/jolt}"
1318 MODULE="${MODULE:-frq.app}"
1419 CHEZ_ANDROID="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"
1520 HOST_SCHEME="$CHEZ_ANDROID/ta6le/bin/ta6le/scheme"
@@ -9,7 +9,12 @@ VIDYA="${VIDYA:-$(cd "$ROOT/../vidya" && pwd)}"
9 # has to be named here.9 # has to be named here.
10 GLIMMER="${GLIMMER:-$HOME/.jolt/gitlibs/https___github.com_jolt-lang_glimmer/5581c331c51aff989259b9e8e92ec920fe5e6741/src}"10 GLIMMER="${GLIMMER:-$HOME/.jolt/gitlibs/https___github.com_jolt-lang_glimmer/5581c331c51aff989259b9e8e92ec920fe5e6741/src}"
11 OUT="${1:?usage: build-jolt-boot.sh OUTPUT_DIRECTORY}"11 OUT="${1:?usage: build-jolt-boot.sh OUTPUT_DIRECTORY}"
12-JOLT="${JOLT:-$(command -v jolt)}"12+# The DotSlash-pinned jolt beside this script, not whatever is on PATH: an
13+# upstream jolt cannot open a TLS connection on Android — it reads the socket
14+# address out of `struct addrinfo` at glibc's offset, which is Bionic's
15+# `ai_canonname` — so a build made with one produces an APK that cannot sign in
16+# or send a picture. Override with JOLT= to use another.
17+JOLT="${JOLT:-$ROOT/scripts/jolt}"
13 MODULE="${MODULE:-frq.app}"18 MODULE="${MODULE:-frq.app}"
14 CHEZ_ANDROID="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"19 CHEZ_ANDROID="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"
15 HOST_SCHEME="$CHEZ_ANDROID/ta6le/bin/ta6le/scheme"20 HOST_SCHEME="$CHEZ_ANDROID/ta6le/bin/ta6le/scheme"
added scripts/jolt +28 -0
new file mode 100755
@@ -0,0 +1,28 @@
1+#!/usr/bin/env dotslash
2+
3+// jolt with the Bionic `struct addrinfo` fix, from the fork at
4+// gitlab.com/nandithebull/jolt (commit 2b80d68d). Upstream's own release reads
5+// `ai_addr` at glibc's offset, which on Android is `ai_canonname` a null
6+// pointer so every TLS connect fails there and reports a refusal the network
7+// never made. Nothing else differs from v0.7.28.
8+//
9+// Linux x86_64 only: it is what the APK is built on. Other platforms fall back
10+// to whatever `jolt` is on PATH, which is upstream's, which is fine for
11+// everything but an Android build.
12+{
13+ "name": "jolt",
14+ "platforms": {
15+ "linux-x86_64": {
16+ "size": 15782379,
17+ "hash": "sha256",
18+ "digest": "aabb71f809aebd9d607b7a5036229f933c733ae90785cb29df899f116be9589f",
19+ "format": "tar.gz",
20+ "path": "jolt-v0.7.28-1-g2b80d68d-x86_64-linux/jolt",
21+ "providers": [
22+ {
23+ "url": "https://gitlab.com/nandithebull/jolt/-/releases/v0.7.28-android/downloads/jolt-v0.7.28-1-g2b80d68d-x86_64-linux.tar.gz"
24+ }
25+ ]
26+ }
27+ }
28+}
new file mode 100755
@@ -0,0 +1,28 @@
1+#!/usr/bin/env dotslash
2+
3+// jolt with the Bionic `struct addrinfo` fix, from the fork at
4+// gitlab.com/nandithebull/jolt (commit 2b80d68d). Upstream's own release reads
5+// `ai_addr` at glibc's offset, which on Android is `ai_canonname` a null
6+// pointer so every TLS connect fails there and reports a refusal the network
7+// never made. Nothing else differs from v0.7.28.
8+//
9+// Linux x86_64 only: it is what the APK is built on. Other platforms fall back
10+// to whatever `jolt` is on PATH, which is upstream's, which is fine for
11+// everything but an Android build.
12+{
13+ "name": "jolt",
14+ "platforms": {
15+ "linux-x86_64": {
16+ "size": 15782379,
17+ "hash": "sha256",
18+ "digest": "aabb71f809aebd9d607b7a5036229f933c733ae90785cb29df899f116be9589f",
19+ "format": "tar.gz",
20+ "path": "jolt-v0.7.28-1-g2b80d68d-x86_64-linux/jolt",
21+ "providers": [
22+ {
23+ "url": "https://gitlab.com/nandithebull/jolt/-/releases/v0.7.28-android/downloads/jolt-v0.7.28-1-g2b80d68d-x86_64-linux.tar.gz"
24+ }
25+ ]
26+ }
27+ }
28+}
modified src/frq/app.jolt +25 -20
@@ -162,27 +162,32 @@
162162 ;; lines are signed with, and the only other place it showed was Settings.
163163 [:title {:label (if (s/connected?) (str "Chats as " @s/form-nick) "Chats")}]
164164 [error-note]
165+ ;; Join and search are the same shape — a channel box with a button
166+ ;; beside it — so they read as one control block rather than a titled
167+ ;; card and a stray row: same card, same widths, same gap. The screen
168+ ;; title already says what the box is for, which is what the "Join
169+ ;; channel" header was doing.
165170 [:card {}
166- [:title-2 {:label "Join channel"}]
167- [:hbox {:spacing 8}
168- [:entry {:text @s/join-input
169- :width-request 380
170- :placeholder "#channel"
171- :on-change #(reset! s/join-input %)
172- :on-activate #(do (s/join! @s/join-input) (reset! s/join-input ""))}]
173- [:button {:label "Join"
174- :kind :primary
175- :on-click #(do (s/join! @s/join-input) (reset! s/join-input ""))}]]]
176- ;; The clear button only shows while there is something to clear: an
177- ;; empty box has nothing to undo, and a dead button beside it reads as
178- ;; one that stopped working.
179- [:hbox {:spacing 6}
180- [:entry {:text @s/search
181- :width-request 380
182- :placeholder "Search channels"
183- :on-change #(reset! s/search %)}]
184- (when (seq @s/search)
185- [:button {:label "✕" :on-click #(reset! s/search "")}])]
171+ [:vbox {:spacing 8}
172+ [:hbox {:spacing 8}
173+ [:entry {:text @s/join-input
174+ :width-request 380
175+ :placeholder "#channel"
176+ :on-change #(reset! s/join-input %)
177+ :on-activate #(do (s/join! @s/join-input) (reset! s/join-input ""))}]
178+ [:button {:label "Join"
179+ :kind :primary
180+ :on-click #(do (s/join! @s/join-input) (reset! s/join-input ""))}]]
181+ ;; The clear button only shows while there is something to clear: an
182+ ;; empty box has nothing to undo, and a dead button beside it reads as
183+ ;; one that stopped working.
184+ [:hbox {:spacing 8}
185+ [:entry {:text @s/search
186+ :width-request 380
187+ :placeholder "Search channels"
188+ :on-change #(reset! s/search %)}]
189+ (when (seq @s/search)
190+ [:button {:label "✕" :on-click #(reset! s/search "")}])]]]
186191 (if (seq buffers)
187192 (for [b buffers] [conversation-row b])
188193 [:card {} [:dim-label {:label "No conversations yet — join a channel."}]])
@@ -162,27 +162,32 @@
162 ;; lines are signed with, and the only other place it showed was Settings.162 ;; lines are signed with, and the only other place it showed was Settings.
163 [:title {:label (if (s/connected?) (str "Chats as " @s/form-nick) "Chats")}]163 [:title {:label (if (s/connected?) (str "Chats as " @s/form-nick) "Chats")}]
164 [error-note]164 [error-note]
165+ ;; Join and search are the same shape — a channel box with a button
166+ ;; beside it — so they read as one control block rather than a titled
167+ ;; card and a stray row: same card, same widths, same gap. The screen
168+ ;; title already says what the box is for, which is what the "Join
169+ ;; channel" header was doing.
165 [:card {}170 [:card {}
166- [:title-2 {:label "Join channel"}]171+ [:vbox {:spacing 8}
167- [:hbox {:spacing 8}172+ [:hbox {:spacing 8}
168- [:entry {:text @s/join-input173+ [:entry {:text @s/join-input
169- :width-request 380174+ :width-request 380
170- :placeholder "#channel"175+ :placeholder "#channel"
171- :on-change #(reset! s/join-input %)176+ :on-change #(reset! s/join-input %)
172- :on-activate #(do (s/join! @s/join-input) (reset! s/join-input ""))}]177+ :on-activate #(do (s/join! @s/join-input) (reset! s/join-input ""))}]
173- [:button {:label "Join"178+ [:button {:label "Join"
174- :kind :primary179+ :kind :primary
175- :on-click #(do (s/join! @s/join-input) (reset! s/join-input ""))}]]]180+ :on-click #(do (s/join! @s/join-input) (reset! s/join-input ""))}]]
176- ;; The clear button only shows while there is something to clear: an181+ ;; The clear button only shows while there is something to clear: an
177- ;; empty box has nothing to undo, and a dead button beside it reads as182+ ;; empty box has nothing to undo, and a dead button beside it reads as
178- ;; one that stopped working.183+ ;; one that stopped working.
179- [:hbox {:spacing 6}184+ [:hbox {:spacing 8}
180- [:entry {:text @s/search185+ [:entry {:text @s/search
181- :width-request 380186+ :width-request 380
182- :placeholder "Search channels"187+ :placeholder "Search channels"
183- :on-change #(reset! s/search %)}]188+ :on-change #(reset! s/search %)}]
184- (when (seq @s/search)189+ (when (seq @s/search)
185- [:button {:label "✕" :on-click #(reset! s/search "")}])]190+ [:button {:label "✕" :on-click #(reset! s/search "")}])]]]
186 (if (seq buffers)191 (if (seq buffers)
187 (for [b buffers] [conversation-row b])192 (for [b buffers] [conversation-row b])
188 [:card {} [:dim-label {:label "No conversations yet — join a channel."}]])193 [:card {} [:dim-label {:label "No conversations yet — join a channel."}]])