Draw frq.app's own connect screen on the phone
One file, rendered by jolt on the desktop and ClojureDart on the phone, in the COSMIC theme read out of cosmic-config. The TUI renders byte-identical either side of this, which is the only proof that it is really one file. What was blanking it was frq.hiccup ignoring :width-request. The connect screen puts the host and the port side by side in an :hbox and gives each a width; a TextField takes its width from its parent and a Row offers unbounded width, so that is a hard layout error. jolt-cosmic already says this — an entry is Length::Fixed(w) from width_request unless :hexpand asks for the rest of the row — and the renderer simply had not grown it. The shape of the failure is worth naming. A layout error happens after the build, so no try/catch sees it, Flutter paints nothing rather than its red box, and every sibling in the same children vector disappears with it. The screen was blank and the log was empty. Four theories went past before the bisect: Center in an unbounded height, fn* as a binding name, qualified symbols in :watch, a Builder error boundary — each one a three-minute deploy, none of them it. What worked was a harness rendering one candidate at a time with a labelled marker between them, so the last label standing says where it died. It walked down to server-fields in three passes. That harness also proved the rest: frq.cells compiles under both compilers and holds its values on the device, frq.screens.connect loads there, and every tag and component renders on its own. And it found a second bug behind the first. Two :entry nodes with no :key shared one TextEditingController, so the host field showed the port — glimmer matches children by position when there is no key, and a backend that holds a controller per field needs a name for it. Every entry in the shared screen carries one now, which changes nothing on the desktop. mode-tabs, server-fields and connect-action are public rather than private: a bisect cannot reach a private fn, and they are worth reaching. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
0bc64b5 parent: 39459ed modified
common/frq/screens/connect.cljc +20 -9 | @@ -30,7 +30,7 @@ | ||
| 30 | 30 | |
| 31 | 31 | ;; ---------------------------------------------------------------- connect |
| 32 | 32 | |
| 33 | -(defn- mode-tabs [] | |
| 33 | +(defn mode-tabs [] | |
| 34 | 34 | [:hbox {:spacing 8} |
| 35 | 35 | (for [[k label] [[:guest "Guest"] [:bluesky "Bluesky"] [:app-password "App password"]]] |
| 36 | 36 | [:button {:key k |
| @@ -38,15 +38,22 @@ | ||
| 38 | 38 | :kind (if (= k @cells/auth-mode) :primary :default) |
| 39 | 39 | :on-click #(reset! cells/auth-mode k)}])]) |
| 40 | 40 | |
| 41 | -(defn- server-fields [] | |
| 41 | +(defn server-fields [] | |
| 42 | 42 | [:vbox {:spacing 6} |
| 43 | + ;; Every :entry carries a :key. glimmer matches children by position when | |
| 44 | + ;; one is absent, so this changes nothing on the desktop — but a backend | |
| 45 | + ;; that keeps a text controller per field needs a stable name for it, and | |
| 46 | + ;; without one the host and the port shared a controller and both showed | |
| 47 | + ;; the port. See frq.hiccup. | |
| 43 | 48 | [:label {:label "Server"}] |
| 44 | 49 | [:hbox {:spacing 8} |
| 45 | - [:entry {:text @cells/form-host | |
| 50 | + [:entry {:key :host | |
| 51 | + :text @cells/form-host | |
| 46 | 52 | :width-request 220 |
| 47 | 53 | :placeholder "host" |
| 48 | 54 | :on-change #(reset! cells/form-host %)}] |
| 49 | - [:entry {:text @cells/form-port | |
| 55 | + [:entry {:key :port | |
| 56 | + :text @cells/form-port | |
| 50 | 57 | :width-request 90 |
| 51 | 58 | :placeholder "6697" |
| 52 | 59 | :on-change #(reset! cells/form-port %)}]] |
| @@ -56,7 +63,7 @@ | ||
| 56 | 63 | (reset! cells/form-port |
| 57 | 64 | (if @cells/form-tls? "6697" "6667")))}]]) |
| 58 | 65 | |
| 59 | -(defn- connect-action [] | |
| 66 | +(defn connect-action [] | |
| 60 | 67 | (if @cells/connecting? |
| 61 | 68 | [:hbox {:spacing 8} |
| 62 | 69 | [:spinner {}] |
| @@ -78,7 +85,8 @@ | ||
| 78 | 85 | [:title-2 {:label "Sign in with Bluesky"}] |
| 79 | 86 | [:dim-label {:label "Opens your browser for AT Protocol OAuth. freeq's broker hands back a token; no password passes through frq."}] |
| 80 | 87 | [:label {:label "Handle"}] |
| 81 | - [:entry {:text @cells/form-handle | |
| 88 | + [:entry {:key :handle | |
| 89 | + :text @cells/form-handle | |
| 82 | 90 | :width-request 320 |
| 83 | 91 | :placeholder "alice.bsky.social" |
| 84 | 92 | :on-change #(reset! cells/form-handle %)}] |
| @@ -98,12 +106,14 @@ | ||
| 98 | 106 | [:title-2 {:label "Sign in with an app password"}] |
| 99 | 107 | [:dim-label {:label "No browser. Your app password goes to your own PDS; freeq is handed the session it mints."}] |
| 100 | 108 | [:label {:label "Handle"}] |
| 101 | - [:entry {:text @cells/form-handle | |
| 109 | + [:entry {:key :handle | |
| 110 | + :text @cells/form-handle | |
| 102 | 111 | :width-request 320 |
| 103 | 112 | :placeholder "alice.bsky.social" |
| 104 | 113 | :on-change #(reset! cells/form-handle %)}] |
| 105 | 114 | [:label {:label "App password"}] |
| 106 | - [:entry {:text @cells/form-app-password | |
| 115 | + [:entry {:key :app-password | |
| 116 | + :text @cells/form-app-password | |
| 107 | 117 | :width-request 320 |
| 108 | 118 | :placeholder "xxxx-xxxx-xxxx-xxxx" |
| 109 | 119 | :on-change #(reset! cells/form-app-password %)}] |
| @@ -112,7 +122,8 @@ | ||
| 112 | 122 | [:vbox {:spacing 6} |
| 113 | 123 | [:title-2 {:label "Connect as guest"}] |
| 114 | 124 | [:label {:label "Nick"}] |
| 115 | - [:entry {:text @cells/form-nick | |
| 125 | + [:entry {:key :nick | |
| 126 | + :text @cells/form-nick | |
| 116 | 127 | :width-request 320 |
| 117 | 128 | :placeholder "your nick" |
| 118 | 129 | :on-change #(reset! cells/form-nick %)}]]) |
| @@ -30,7 +30,7 @@ | |||
| 30 | 30 | ||
| 31 | ;; ---------------------------------------------------------------- connect | 31 | ;; ---------------------------------------------------------------- connect |
| 32 | 32 | ||
| 33 | -(defn- mode-tabs [] | 33 | +(defn mode-tabs [] |
| 34 | [:hbox {:spacing 8} | 34 | [:hbox {:spacing 8} |
| 35 | (for [[k label] [[:guest "Guest"] [:bluesky "Bluesky"] [:app-password "App password"]]] | 35 | (for [[k label] [[:guest "Guest"] [:bluesky "Bluesky"] [:app-password "App password"]]] |
| 36 | [:button {:key k | 36 | [:button {:key k |
| @@ -38,15 +38,22 @@ | |||
| 38 | :kind (if (= k @cells/auth-mode) :primary :default) | 38 | :kind (if (= k @cells/auth-mode) :primary :default) |
| 39 | :on-click #(reset! cells/auth-mode k)}])]) | 39 | :on-click #(reset! cells/auth-mode k)}])]) |
| 40 | 40 | ||
| 41 | -(defn- server-fields [] | 41 | +(defn server-fields [] |
| 42 | [:vbox {:spacing 6} | 42 | [:vbox {:spacing 6} |
| 43 | + ;; Every :entry carries a :key. glimmer matches children by position when | ||
| 44 | + ;; one is absent, so this changes nothing on the desktop — but a backend | ||
| 45 | + ;; that keeps a text controller per field needs a stable name for it, and | ||
| 46 | + ;; without one the host and the port shared a controller and both showed | ||
| 47 | + ;; the port. See frq.hiccup. | ||
| 43 | [:label {:label "Server"}] | 48 | [:label {:label "Server"}] |
| 44 | [:hbox {:spacing 8} | 49 | [:hbox {:spacing 8} |
| 45 | - [:entry {:text @cells/form-host | 50 | + [:entry {:key :host |
| 51 | + :text @cells/form-host | ||
| 46 | :width-request 220 | 52 | :width-request 220 |
| 47 | :placeholder "host" | 53 | :placeholder "host" |
| 48 | :on-change #(reset! cells/form-host %)}] | 54 | :on-change #(reset! cells/form-host %)}] |
| 49 | - [:entry {:text @cells/form-port | 55 | + [:entry {:key :port |
| 56 | + :text @cells/form-port | ||
| 50 | :width-request 90 | 57 | :width-request 90 |
| 51 | :placeholder "6697" | 58 | :placeholder "6697" |
| 52 | :on-change #(reset! cells/form-port %)}]] | 59 | :on-change #(reset! cells/form-port %)}]] |
| @@ -56,7 +63,7 @@ | |||
| 56 | (reset! cells/form-port | 63 | (reset! cells/form-port |
| 57 | (if @cells/form-tls? "6697" "6667")))}]]) | 64 | (if @cells/form-tls? "6697" "6667")))}]]) |
| 58 | 65 | ||
| 59 | -(defn- connect-action [] | 66 | +(defn connect-action [] |
| 60 | (if @cells/connecting? | 67 | (if @cells/connecting? |
| 61 | [:hbox {:spacing 8} | 68 | [:hbox {:spacing 8} |
| 62 | [:spinner {}] | 69 | [:spinner {}] |
| @@ -78,7 +85,8 @@ | |||
| 78 | [:title-2 {:label "Sign in with Bluesky"}] | 85 | [:title-2 {:label "Sign in with Bluesky"}] |
| 79 | [:dim-label {:label "Opens your browser for AT Protocol OAuth. freeq's broker hands back a token; no password passes through frq."}] | 86 | [:dim-label {:label "Opens your browser for AT Protocol OAuth. freeq's broker hands back a token; no password passes through frq."}] |
| 80 | [:label {:label "Handle"}] | 87 | [:label {:label "Handle"}] |
| 81 | - [:entry {:text @cells/form-handle | 88 | + [:entry {:key :handle |
| 89 | + :text @cells/form-handle | ||
| 82 | :width-request 320 | 90 | :width-request 320 |
| 83 | :placeholder "alice.bsky.social" | 91 | :placeholder "alice.bsky.social" |
| 84 | :on-change #(reset! cells/form-handle %)}] | 92 | :on-change #(reset! cells/form-handle %)}] |
| @@ -98,12 +106,14 @@ | |||
| 98 | [:title-2 {:label "Sign in with an app password"}] | 106 | [:title-2 {:label "Sign in with an app password"}] |
| 99 | [:dim-label {:label "No browser. Your app password goes to your own PDS; freeq is handed the session it mints."}] | 107 | [:dim-label {:label "No browser. Your app password goes to your own PDS; freeq is handed the session it mints."}] |
| 100 | [:label {:label "Handle"}] | 108 | [:label {:label "Handle"}] |
| 101 | - [:entry {:text @cells/form-handle | 109 | + [:entry {:key :handle |
| 110 | + :text @cells/form-handle | ||
| 102 | :width-request 320 | 111 | :width-request 320 |
| 103 | :placeholder "alice.bsky.social" | 112 | :placeholder "alice.bsky.social" |
| 104 | :on-change #(reset! cells/form-handle %)}] | 113 | :on-change #(reset! cells/form-handle %)}] |
| 105 | [:label {:label "App password"}] | 114 | [:label {:label "App password"}] |
| 106 | - [:entry {:text @cells/form-app-password | 115 | + [:entry {:key :app-password |
| 116 | + :text @cells/form-app-password | ||
| 107 | :width-request 320 | 117 | :width-request 320 |
| 108 | :placeholder "xxxx-xxxx-xxxx-xxxx" | 118 | :placeholder "xxxx-xxxx-xxxx-xxxx" |
| 109 | :on-change #(reset! cells/form-app-password %)}] | 119 | :on-change #(reset! cells/form-app-password %)}] |
| @@ -112,7 +122,8 @@ | |||
| 112 | [:vbox {:spacing 6} | 122 | [:vbox {:spacing 6} |
| 113 | [:title-2 {:label "Connect as guest"}] | 123 | [:title-2 {:label "Connect as guest"}] |
| 114 | [:label {:label "Nick"}] | 124 | [:label {:label "Nick"}] |
| 115 | - [:entry {:text @cells/form-nick | 125 | + [:entry {:key :nick |
| 126 | + :text @cells/form-nick | ||
| 116 | :width-request 320 | 127 | :width-request 320 |
| 117 | :placeholder "your nick" | 128 | :placeholder "your nick" |
| 118 | :on-change #(reset! cells/form-nick %)}]]) | 129 | :on-change #(reset! cells/form-nick %)}]]) |
modified
flutter/README.md +25 -4 | @@ -130,10 +130,10 @@ What `frq.hiccup` does not do is glimmer's reconciliation: Flutter rebuilds | ||
| 130 | 130 | from the top and diffs its own element tree, so a cell firing rebuilds the |
| 131 | 131 | screen rather than the subtree that read it. Fine at this size. |
| 132 | 132 | |
| 133 | -`frq.main` still paints a hand-written tree rather than `frq.app`'s own. Not | |
| 134 | -because the screens need changing — because requiring them pulls `frq.state`, | |
| 135 | -which pulls `frq.irc`, which reaches for jolt.host. The tree it paints uses | |
| 136 | -only tags `frq.app` uses, so it is a test of the backend and nothing more. | |
| 133 | +`frq.main` paints `frq.app`'s own connect screen, out of | |
| 134 | +`common/frq/screens/connect.cljc` — the same file the desktop renders. What it | |
| 135 | +reads is `frq.cells` and what it calls is `frq.actions`, and each platform | |
| 136 | +fills those in: `frq.state`'s reducers on the desktop, dart:io here. | |
| 137 | 137 | |
| 138 | 138 | ## The order to do the rest in |
| 139 | 139 | |
| @@ -165,3 +165,24 @@ only tags `frq.app` uses, so it is a test of the backend and nothing more. | ||
| 165 | 165 | platform by reader conditional. |
| 166 | 166 | 6. **`frq.app`** follows it, and the tags it uses that `frq.hiccup` does not |
| 167 | 167 | cover yet paint as an orange `?tag` until they do. |
| 168 | + | |
| 169 | +## What a missing tag property looks like | |
| 170 | + | |
| 171 | +Worth writing down, because it cost an evening. `frq.hiccup` ignored | |
| 172 | +`:width-request`, and the connect screen puts two entries side by side in an | |
| 173 | +`:hbox` with one. A TextField takes its width from its parent and a Row offers | |
| 174 | +unbounded width, so that is a hard layout error — and a layout error happens | |
| 175 | +after the build, so it is not an exception anything can catch, paints nothing | |
| 176 | +at all rather than Flutter's red box, and takes every sibling in the same | |
| 177 | +`children` vector down with it. The screen was blank and the log was empty. | |
| 178 | + | |
| 179 | +The way through was a harness that renders each candidate in turn with a | |
| 180 | +labelled marker between them, so the last label standing says where it died. | |
| 181 | +Not guesswork: four wrong theories went past before that — `Center` in an | |
| 182 | +unbounded height, `fn*` as a binding name, qualified symbols in `:watch`, a | |
| 183 | +`Builder` boundary — each one a three-minute deploy. | |
| 184 | + | |
| 185 | +It also found that two `:entry` nodes with no `:key` shared one | |
| 186 | +TextEditingController, so the host field showed the port. glimmer matches | |
| 187 | +children by position when there is no key; a backend holding a controller per | |
| 188 | +field needs a name for it. | |
| @@ -130,10 +130,10 @@ What `frq.hiccup` does not do is glimmer's reconciliation: Flutter rebuilds | |||
| 130 | from the top and diffs its own element tree, so a cell firing rebuilds the | 130 | from the top and diffs its own element tree, so a cell firing rebuilds the |
| 131 | screen rather than the subtree that read it. Fine at this size. | 131 | screen rather than the subtree that read it. Fine at this size. |
| 132 | 132 | ||
| 133 | -`frq.main` still paints a hand-written tree rather than `frq.app`'s own. Not | 133 | +`frq.main` paints `frq.app`'s own connect screen, out of |
| 134 | -because the screens need changing — because requiring them pulls `frq.state`, | 134 | +`common/frq/screens/connect.cljc` — the same file the desktop renders. What it |
| 135 | -which pulls `frq.irc`, which reaches for jolt.host. The tree it paints uses | 135 | +reads is `frq.cells` and what it calls is `frq.actions`, and each platform |
| 136 | -only tags `frq.app` uses, so it is a test of the backend and nothing more. | 136 | +fills those in: `frq.state`'s reducers on the desktop, dart:io here. |
| 137 | 137 | ||
| 138 | ## The order to do the rest in | 138 | ## The order to do the rest in |
| 139 | 139 | ||
| @@ -165,3 +165,24 @@ only tags `frq.app` uses, so it is a test of the backend and nothing more. | |||
| 165 | platform by reader conditional. | 165 | platform by reader conditional. |
| 166 | 6. **`frq.app`** follows it, and the tags it uses that `frq.hiccup` does not | 166 | 6. **`frq.app`** follows it, and the tags it uses that `frq.hiccup` does not |
| 167 | cover yet paint as an orange `?tag` until they do. | 167 | cover yet paint as an orange `?tag` until they do. |
| 168 | + | ||
| 169 | +## What a missing tag property looks like | ||
| 170 | + | ||
| 171 | +Worth writing down, because it cost an evening. `frq.hiccup` ignored | ||
| 172 | +`:width-request`, and the connect screen puts two entries side by side in an | ||
| 173 | +`:hbox` with one. A TextField takes its width from its parent and a Row offers | ||
| 174 | +unbounded width, so that is a hard layout error — and a layout error happens | ||
| 175 | +after the build, so it is not an exception anything can catch, paints nothing | ||
| 176 | +at all rather than Flutter's red box, and takes every sibling in the same | ||
| 177 | +`children` vector down with it. The screen was blank and the log was empty. | ||
| 178 | + | ||
| 179 | +The way through was a harness that renders each candidate in turn with a | ||
| 180 | +labelled marker between them, so the last label standing says where it died. | ||
| 181 | +Not guesswork: four wrong theories went past before that — `Center` in an | ||
| 182 | +unbounded height, `fn*` as a binding name, qualified symbols in `:watch`, a | ||
| 183 | +`Builder` boundary — each one a three-minute deploy. | ||
| 184 | + | ||
| 185 | +It also found that two `:entry` nodes with no `:key` shared one | ||
| 186 | +TextEditingController, so the host field showed the port. glimmer matches | ||
| 187 | +children by position when there is no key; a backend holding a controller per | ||
| 188 | +field needs a name for it. | ||
modified
flutter/src/frq/hiccup.cljd +20 -3 | @@ -245,11 +245,20 @@ | ||
| 245 | 245 | |
| 246 | 246 | ;; text_input: a filled rounded field, no outline. COSMIC entries sit in |
| 247 | 247 | ;; the component colour rather than behind a border. |
| 248 | + ;; An entry sizes itself, as it does in jolt-cosmic: Fixed(w) from | |
| 249 | + ;; :width-request unless :hexpand asks for the rest of the row. | |
| 250 | + ;; | |
| 251 | + ;; Not optional. A TextField takes its width from its parent, and a Row | |
| 252 | + ;; offers its children unbounded width — so two entries side by side in | |
| 253 | + ;; an :hbox, which is exactly what the connect screen's host and port | |
| 254 | + ;; are, is a hard layout error. That is what painted the whole screen | |
| 255 | + ;; blank with nothing in the log. | |
| 248 | 256 | :entry |
| 249 | 257 | (let [on-change (:on-change p) |
| 250 | 258 | on-activate (:on-activate p) |
| 251 | - rows (:rows p)] | |
| 252 | - (m/TextField | |
| 259 | + rows (:rows p) | |
| 260 | + w (:width-request p) | |
| 261 | + field (m/TextField | |
| 253 | 262 | .controller (controller-for (:key p) (:text p)) |
| 254 | 263 | .onChanged (when on-change #(on-change %)) |
| 255 | 264 | .onSubmitted (when on-activate (fn [_] (on-activate))) |
| @@ -275,7 +284,15 @@ | ||
| 275 | 284 | .focusedBorder (m/OutlineInputBorder |
| 276 | 285 | .borderRadius (m/BorderRadius.circular t/radius-s) |
| 277 | 286 | .borderSide (m/BorderSide .color t/accent |
| 278 | - .width 1.0))))) | |
| 287 | + .width 1.0))))] | |
| 288 | + ;; No Expanded when there is no width: Expanded in a Column expands | |
| 289 | + ;; along the main axis, which is vertical, and an entry in a card | |
| 290 | + ;; would grow to fill the card. A bare field is right there — a | |
| 291 | + ;; Column hands its children bounded width — and a row wants the | |
| 292 | + ;; width-request the caller already writes. | |
| 293 | + (if w | |
| 294 | + (m/SizedBox .width (dbl w 200.0) .child field) | |
| 295 | + field)) | |
| 279 | 296 | |
| 280 | 297 | :emoji |
| 281 | 298 | (m/Text (str (:emoji p "")) .style (m/TextStyle .fontSize (dbl (:size p) 16.0))) |
| @@ -245,11 +245,20 @@ | |||
| 245 | 245 | ||
| 246 | ;; text_input: a filled rounded field, no outline. COSMIC entries sit in | 246 | ;; text_input: a filled rounded field, no outline. COSMIC entries sit in |
| 247 | ;; the component colour rather than behind a border. | 247 | ;; the component colour rather than behind a border. |
| 248 | + ;; An entry sizes itself, as it does in jolt-cosmic: Fixed(w) from | ||
| 249 | + ;; :width-request unless :hexpand asks for the rest of the row. | ||
| 250 | + ;; | ||
| 251 | + ;; Not optional. A TextField takes its width from its parent, and a Row | ||
| 252 | + ;; offers its children unbounded width — so two entries side by side in | ||
| 253 | + ;; an :hbox, which is exactly what the connect screen's host and port | ||
| 254 | + ;; are, is a hard layout error. That is what painted the whole screen | ||
| 255 | + ;; blank with nothing in the log. | ||
| 248 | :entry | 256 | :entry |
| 249 | (let [on-change (:on-change p) | 257 | (let [on-change (:on-change p) |
| 250 | on-activate (:on-activate p) | 258 | on-activate (:on-activate p) |
| 251 | - rows (:rows p)] | 259 | + rows (:rows p) |
| 252 | - (m/TextField | 260 | + w (:width-request p) |
| 261 | + field (m/TextField | ||
| 253 | .controller (controller-for (:key p) (:text p)) | 262 | .controller (controller-for (:key p) (:text p)) |
| 254 | .onChanged (when on-change #(on-change %)) | 263 | .onChanged (when on-change #(on-change %)) |
| 255 | .onSubmitted (when on-activate (fn [_] (on-activate))) | 264 | .onSubmitted (when on-activate (fn [_] (on-activate))) |
| @@ -275,7 +284,15 @@ | |||
| 275 | .focusedBorder (m/OutlineInputBorder | 284 | .focusedBorder (m/OutlineInputBorder |
| 276 | .borderRadius (m/BorderRadius.circular t/radius-s) | 285 | .borderRadius (m/BorderRadius.circular t/radius-s) |
| 277 | .borderSide (m/BorderSide .color t/accent | 286 | .borderSide (m/BorderSide .color t/accent |
| 278 | - .width 1.0))))) | 287 | + .width 1.0))))] |
| 288 | + ;; No Expanded when there is no width: Expanded in a Column expands | ||
| 289 | + ;; along the main axis, which is vertical, and an entry in a card | ||
| 290 | + ;; would grow to fill the card. A bare field is right there — a | ||
| 291 | + ;; Column hands its children bounded width — and a row wants the | ||
| 292 | + ;; width-request the caller already writes. | ||
| 293 | + (if w | ||
| 294 | + (m/SizedBox .width (dbl w 200.0) .child field) | ||
| 295 | + field)) | ||
| 279 | 296 | ||
| 280 | :emoji | 297 | :emoji |
| 281 | (m/Text (str (:emoji p "")) .style (m/TextStyle .fontSize (dbl (:size p) 16.0))) | 298 | (m/Text (str (:emoji p "")) .style (m/TextStyle .fontSize (dbl (:size p) 16.0))) |
modified
flutter/src/frq/main.cljd +18 -2 | @@ -161,6 +161,22 @@ | ||
| 161 | 161 | (m/Scaffold .appBar (m/AppBar .title (m/Text "frq"))) |
| 162 | 162 | .body |
| 163 | 163 | (f/widget |
| 164 | - :watch [st status ls lines io identity-out hh handle] | |
| 164 | + :let [c-status cells/status | |
| 165 | + c-error cells/error | |
| 166 | + c-connecting cells/connecting? | |
| 167 | + c-mode cells/auth-mode | |
| 168 | + c-handle cells/form-handle | |
| 169 | + c-nick cells/form-nick | |
| 170 | + c-host cells/form-host | |
| 171 | + c-port cells/form-port | |
| 172 | + c-tls cells/form-tls? | |
| 173 | + c-apppw cells/form-app-password | |
| 174 | + c-broker cells/broker-token | |
| 175 | + c-login cells/login-url] | |
| 176 | + :watch [st c-status er c-error cn c-connecting am c-mode | |
| 177 | + fh c-handle nk c-nick hs c-host pt c-port tl c-tls | |
| 178 | + ap c-apppw bt c-broker lu c-login ls lines] | |
| 165 | 179 | (m/SingleChildScrollView |
| 166 | - .child (h/render (screen))))))) | |
| 180 | + ;; frq.app's own connect screen, out of common/ — the same file the | |
| 181 | + ;; desktop renders, painted by frq.hiccup in COSMIC's theme. | |
| 182 | + .child (h/render [connect/connect-screen])))))) | |
| @@ -161,6 +161,22 @@ | |||
| 161 | (m/Scaffold .appBar (m/AppBar .title (m/Text "frq"))) | 161 | (m/Scaffold .appBar (m/AppBar .title (m/Text "frq"))) |
| 162 | .body | 162 | .body |
| 163 | (f/widget | 163 | (f/widget |
| 164 | - :watch [st status ls lines io identity-out hh handle] | 164 | + :let [c-status cells/status |
| 165 | + c-error cells/error | ||
| 166 | + c-connecting cells/connecting? | ||
| 167 | + c-mode cells/auth-mode | ||
| 168 | + c-handle cells/form-handle | ||
| 169 | + c-nick cells/form-nick | ||
| 170 | + c-host cells/form-host | ||
| 171 | + c-port cells/form-port | ||
| 172 | + c-tls cells/form-tls? | ||
| 173 | + c-apppw cells/form-app-password | ||
| 174 | + c-broker cells/broker-token | ||
| 175 | + c-login cells/login-url] | ||
| 176 | + :watch [st c-status er c-error cn c-connecting am c-mode | ||
| 177 | + fh c-handle nk c-nick hs c-host pt c-port tl c-tls | ||
| 178 | + ap c-apppw bt c-broker lu c-login ls lines] | ||
| 165 | (m/SingleChildScrollView | 179 | (m/SingleChildScrollView |
| 166 | - .child (h/render (screen))))))) | 180 | + ;; frq.app's own connect screen, out of common/ — the same file the |
| 181 | + ;; desktop renders, painted by frq.hiccup in COSMIC's theme. | ||
| 182 | + .child (h/render [connect/connect-screen])))))) | ||