nandi/frqpublic Fork 0
131c6db
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.

Let the Flutter build split a wide window too

The split view has been in common/frq/screens/app.cljc all along — the row
with the list in one pane and the conversation in the other — and the Flutter
backend answered :wide? with a flat false, so it never appeared. A laptop
window ran the phone layout at any size: one screen at a time, with inches of
margin either side of it.

note-window-size! has been filling window-width in since the pictures needed
it, so the question the desktop asks is askable here: the same comparison
against the same threshold, which moves to frq.cells so two backends cannot
drift apart on the number.

Three things hang on it, not one. wide? is what app.cljc branches on, but
opening a room has to stop trading the list away for the conversation when
both fit, and an arriving line has to count as read while the room is the pane
beside the list — so open-room! picks its screen the way frq.state does, and
the PRIVMSG handler asks chat-visible? rather than the screen alone.

Not gated on Platform.isAndroid: a tablet held the long way has the room and a
desktop window dragged narrow has not. The width is the whole of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-09-12T08:41:25-07:00 Browse files
131c6db parent: f03729c
modified common/frq/cells.cljc +12 -0
@@ -160,6 +160,18 @@
160160 ;; list and the conversation are both on screen instead of taking turns.
161161 (defonce window-width (atom 0))
162162
163+;; Where the second pane starts paying for itself. Below this a 320pt list
164+;; beside a conversation leaves the messages narrower than the phone layout
165+;; they were written for.
166+;;
167+;; Here rather than in a backend because both of them answer `wide?` with it:
168+;; libcosmic measures its window, Flutter reads MediaQuery, and the terminal
169+;; multiplies its columns back up to points — three ways of filling
170+;; `window-width` in, one width they are all compared against. Two backends
171+;; drifting apart on the number would be two different apps out of the same
172+;; screens.
173+(def wide-width 900)
174+
163175 ;; ------------------------------------------------------------- derivation
164176
165177 (defonce ^:private derived-cells
@@ -160,6 +160,18 @@
160 ;; list and the conversation are both on screen instead of taking turns.160 ;; list and the conversation are both on screen instead of taking turns.
161 (defonce window-width (atom 0))161 (defonce window-width (atom 0))
162 162
163+;; Where the second pane starts paying for itself. Below this a 320pt list
164+;; beside a conversation leaves the messages narrower than the phone layout
165+;; they were written for.
166+;;
167+;; Here rather than in a backend because both of them answer `wide?` with it:
168+;; libcosmic measures its window, Flutter reads MediaQuery, and the terminal
169+;; multiplies its columns back up to points — three ways of filling
170+;; `window-width` in, one width they are all compared against. Two backends
171+;; drifting apart on the number would be two different apps out of the same
172+;; screens.
173+(def wide-width 900)
174+
163 ;; ------------------------------------------------------------- derivation175 ;; ------------------------------------------------------------- derivation
164 176
165 (defonce ^:private derived-cells177 (defonce ^:private derived-cells
modified flutter/src/frq/main.cljd +32 -3
@@ -197,6 +197,32 @@
197197 (net/send-line! c (str "JOIN " name))))
198198 nil)
199199
200+(defn- wide?
201+ "True while the window has room for the list and a conversation at once.
202+
203+ `note-window-size!` above is what fills `window-width` in, so this is the
204+ same question `frq.state/wide?` answers on the desktop, asked of the same
205+ number against the same threshold. It used to answer a flat false and the
206+ Flutter build had one screen at a time at every size — which on a laptop
207+ window is a phone with several inches of margin either side.
208+
209+ Not desktop-only: `Platform.isAndroid` is the wrong question here, because a
210+ tablet held the long way has the room and a desktop window dragged narrow
211+ has not. The width is the whole of it."
212+ []
213+ (>= @cells/window-width cells/wide-width))
214+
215+(defn- chat-visible?
216+ "Whether the room in `current` is on screen.
217+
218+ Narrow that is the chat screen alone; wide it is the chats screen too, since
219+ the conversation is the pane beside the list there. This and not the screen
220+ is what decides whether an arriving line counts as read."
221+ [name]
222+ (and (= name @cells/current)
223+ (or (= :chat @cells/screen)
224+ (and (wide?) (= :chats @cells/screen)))))
225+
200226 (defn- open-room!
201227 "Show a buffer, joining it on the way. A row can outlive the membership
202228 behind it — a disconnect drops every channel and the buffer stays — so
@@ -222,7 +248,10 @@
222248 #(-> (rooms/ensure-channel % name)
223249 (update name rooms/mark-read)
224250 (assoc-in [name :accessed] (swap! rooms/access-tick inc))))
225- (reset! cells/screen :chat)
251+ ;; On a wide window the conversation is the chats screen's second pane, so
252+ ;; the list stays: `:chat` is the narrow window's way of showing the room
253+ ;; *instead* of the list, and beside it there is nothing to trade away.
254+ (reset! cells/screen (if (wide?) :chats :chat))
226255 ;; Worth a write of its own: the order the list is read in is the order
227256 ;; rooms were last opened, and this is the moment it changes.
228257 (remember-rooms! true)
@@ -371,7 +400,7 @@
371400 ;; Reading a room is marking it read: a line arriving in the room
372401 ;; on screen moves the marker past itself, and one arriving
373402 ;; anywhere else is counted against the marker there.
374- viewing? (and (= :chat @cells/screen) (= name @cells/current))
403+ viewing? (chat-visible? name)
375404 settle (fn [m] (update m name (if viewing?
376405 rooms/mark-read
377406 rooms/recount)))]
@@ -1157,7 +1186,7 @@
11571186 ;; will serve again, a picture in a message is whatever host a stranger
11581187 ;; put a link to.
11591188 :image-path (fn [url] (media/path-when-ready url))
1160- :wide? (fn [] false)
1189+ :wide? wide?
11611190 ;; True where there is a pointer to hover with. Both Flutter targets
11621191 ;; compile this file, so it is asked rather than assumed: a window on the
11631192 ;; desktop has a mouse, Android has a finger.
@@ -197,6 +197,32 @@
197 (net/send-line! c (str "JOIN " name))))197 (net/send-line! c (str "JOIN " name))))
198 nil)198 nil)
199 199
200+(defn- wide?
201+ "True while the window has room for the list and a conversation at once.
202+
203+ `note-window-size!` above is what fills `window-width` in, so this is the
204+ same question `frq.state/wide?` answers on the desktop, asked of the same
205+ number against the same threshold. It used to answer a flat false and the
206+ Flutter build had one screen at a time at every size — which on a laptop
207+ window is a phone with several inches of margin either side.
208+
209+ Not desktop-only: `Platform.isAndroid` is the wrong question here, because a
210+ tablet held the long way has the room and a desktop window dragged narrow
211+ has not. The width is the whole of it."
212+ []
213+ (>= @cells/window-width cells/wide-width))
214+
215+(defn- chat-visible?
216+ "Whether the room in `current` is on screen.
217+
218+ Narrow that is the chat screen alone; wide it is the chats screen too, since
219+ the conversation is the pane beside the list there. This and not the screen
220+ is what decides whether an arriving line counts as read."
221+ [name]
222+ (and (= name @cells/current)
223+ (or (= :chat @cells/screen)
224+ (and (wide?) (= :chats @cells/screen)))))
225+
200 (defn- open-room!226 (defn- open-room!
201 "Show a buffer, joining it on the way. A row can outlive the membership227 "Show a buffer, joining it on the way. A row can outlive the membership
202 behind it — a disconnect drops every channel and the buffer stays — so228 behind it — a disconnect drops every channel and the buffer stays — so
@@ -222,7 +248,10 @@
222 #(-> (rooms/ensure-channel % name)248 #(-> (rooms/ensure-channel % name)
223 (update name rooms/mark-read)249 (update name rooms/mark-read)
224 (assoc-in [name :accessed] (swap! rooms/access-tick inc))))250 (assoc-in [name :accessed] (swap! rooms/access-tick inc))))
225- (reset! cells/screen :chat)251+ ;; On a wide window the conversation is the chats screen's second pane, so
252+ ;; the list stays: `:chat` is the narrow window's way of showing the room
253+ ;; *instead* of the list, and beside it there is nothing to trade away.
254+ (reset! cells/screen (if (wide?) :chats :chat))
226 ;; Worth a write of its own: the order the list is read in is the order255 ;; Worth a write of its own: the order the list is read in is the order
227 ;; rooms were last opened, and this is the moment it changes.256 ;; rooms were last opened, and this is the moment it changes.
228 (remember-rooms! true)257 (remember-rooms! true)
@@ -371,7 +400,7 @@
371 ;; Reading a room is marking it read: a line arriving in the room400 ;; Reading a room is marking it read: a line arriving in the room
372 ;; on screen moves the marker past itself, and one arriving401 ;; on screen moves the marker past itself, and one arriving
373 ;; anywhere else is counted against the marker there.402 ;; anywhere else is counted against the marker there.
374- viewing? (and (= :chat @cells/screen) (= name @cells/current))403+ viewing? (chat-visible? name)
375 settle (fn [m] (update m name (if viewing?404 settle (fn [m] (update m name (if viewing?
376 rooms/mark-read405 rooms/mark-read
377 rooms/recount)))]406 rooms/recount)))]
@@ -1157,7 +1186,7 @@
1157 ;; will serve again, a picture in a message is whatever host a stranger1186 ;; will serve again, a picture in a message is whatever host a stranger
1158 ;; put a link to.1187 ;; put a link to.
1159 :image-path (fn [url] (media/path-when-ready url))1188 :image-path (fn [url] (media/path-when-ready url))
1160- :wide? (fn [] false)1189+ :wide? wide?
1161 ;; True where there is a pointer to hover with. Both Flutter targets1190 ;; True where there is a pointer to hover with. Both Flutter targets
1162 ;; compile this file, so it is asked rather than assumed: a window on the1191 ;; compile this file, so it is asked rather than assumed: a window on the
1163 ;; desktop has a mouse, Android has a finger.1192 ;; desktop has a mouse, Android has a finger.
modified src/frq/state.clj +3 -4
@@ -126,10 +126,9 @@
126126
127127 (def window-height cells/window-height)
128128
129-;; Where the second pane starts paying for itself. Below this a 300pt list
130-;; beside a conversation leaves the messages narrower than the phone layout
131-;; they were written for.
132-(def wide-width 900)
129+;; Where the second pane starts paying for itself, shared with the other
130+;; backends — see `frq.cells/wide-width`.
131+(def wide-width cells/wide-width)
133132
134133 (defn wide?
135134 "True while the window has room for the list and a conversation at once."
@@ -126,10 +126,9 @@
126 126
127 (def window-height cells/window-height)127 (def window-height cells/window-height)
128 128
129-;; Where the second pane starts paying for itself. Below this a 300pt list129+;; Where the second pane starts paying for itself, shared with the other
130-;; beside a conversation leaves the messages narrower than the phone layout130+;; backends — see `frq.cells/wide-width`.
131-;; they were written for.131+(def wide-width cells/wide-width)
132-(def wide-width 900)
133 132
134 (defn wide?133 (defn wide?
135 "True while the window has room for the list and a conversation at once."134 "True while the window has room for the list and a conversation at once."