Hold the list still around what moves
The chats screen was one page, so the title, the join and search boxes and the tab bar all scrolled with the conversations — the two things you act with were the first and last to leave. Three bands instead: controls at the top, tabs at the bottom, conversations scrolling between them. The same bargain the chat screen makes, where the compose bar holds still under a backlog that moves. What goes with the page is its centring at 620, on a window between that and the split view's 900. The list is full-bleed there now, which is what the conversation beside it has always been. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bccb134 parent: 35e7155 modified
src/frq/app.jolt +27 -4 | @@ -175,9 +175,25 @@ | ||
| 175 | 175 | [] |
| 176 | 176 | (if (s/wide?) (- sidebar-width 130) 380)) |
| 177 | 177 | |
| 178 | +(defn- below-list [] | |
| 179 | + ;; What the strip under the list needs, counted: the gap after the list, the | |
| 180 | + ;; separator, the gap under it, the tab bar's row of buttons and the margin | |
| 181 | + ;; below them. Short by any of it and the tabs go off the bottom edge — which | |
| 182 | + ;; is the thing this layout exists to stop. | |
| 183 | + (+ 8 8 8 34 12)) | |
| 184 | + | |
| 178 | 185 | (defn chats-screen [] |
| 179 | 186 | (let [buffers (s/channel-list)] |
| 180 | - [:page {:max-width 620} | |
| 187 | + ;; Three bands rather than a page: the title and the two boxes at the top, | |
| 188 | + ;; the tabs at the bottom, and the conversations scrolling between them. | |
| 189 | + ;; Everything you act with stays where you last saw it — the same bargain | |
| 190 | + ;; the chat screen makes, where the compose bar holds still under a backlog | |
| 191 | + ;; that moves. | |
| 192 | + ;; | |
| 193 | + ;; What goes with the page is its centring at 620, on a window between that | |
| 194 | + ;; and the split view's 900: the list is full-bleed there now, which is | |
| 195 | + ;; what the conversation beside it has always been. | |
| 196 | + [:vbox {:spacing 8 :margin 12} | |
| 181 | 197 | ;; Your nick in the title, where "Chats" alone said nothing you did not |
| 182 | 198 | ;; already know. It is what every channel calls you and what your own |
| 183 | 199 | ;; lines are signed with, and the only other place it showed was Settings. |
| @@ -211,9 +227,16 @@ | ||
| 211 | 227 | :on-change #(reset! s/search %)}] |
| 212 | 228 | (when (seq @s/search) |
| 213 | 229 | [:button {:label "✕" :on-click #(reset! s/search "")}])]]] |
| 214 | - (if (seq buffers) | |
| 215 | - (for [b buffers] [conversation-row b]) | |
| 216 | - [:card {} [:dim-label {:label "No conversations yet — join a channel."}]]) | |
| 230 | + ;; `:fill-height` so the scroll inside is handed the rest of the window | |
| 231 | + ;; rather than the one card it starts out holding, and `:reserve` to keep | |
| 232 | + ;; the tabs' strip out of what it may take. | |
| 233 | + ;; Named, so it is the same list — and the same scroll position — when the | |
| 234 | + ;; reader comes back from a conversation. | |
| 235 | + [:vbox {:key :list :fill-height true :reserve (below-list)} | |
| 236 | + [:scroll {:scroll-key "chats-list" :orientation :vertical} | |
| 237 | + (if (seq buffers) | |
| 238 | + (for [b buffers] [conversation-row b]) | |
| 239 | + [:card {} [:dim-label {:label "No conversations yet — join a channel."}]])]] | |
| 217 | 240 | [:separator {}] |
| 218 | 241 | [tab-bar]])) |
| 219 | 242 | |
| @@ -175,9 +175,25 @@ | |||
| 175 | [] | 175 | [] |
| 176 | (if (s/wide?) (- sidebar-width 130) 380)) | 176 | (if (s/wide?) (- sidebar-width 130) 380)) |
| 177 | 177 | ||
| 178 | +(defn- below-list [] | ||
| 179 | + ;; What the strip under the list needs, counted: the gap after the list, the | ||
| 180 | + ;; separator, the gap under it, the tab bar's row of buttons and the margin | ||
| 181 | + ;; below them. Short by any of it and the tabs go off the bottom edge — which | ||
| 182 | + ;; is the thing this layout exists to stop. | ||
| 183 | + (+ 8 8 8 34 12)) | ||
| 184 | + | ||
| 178 | (defn chats-screen [] | 185 | (defn chats-screen [] |
| 179 | (let [buffers (s/channel-list)] | 186 | (let [buffers (s/channel-list)] |
| 180 | - [:page {:max-width 620} | 187 | + ;; Three bands rather than a page: the title and the two boxes at the top, |
| 188 | + ;; the tabs at the bottom, and the conversations scrolling between them. | ||
| 189 | + ;; Everything you act with stays where you last saw it — the same bargain | ||
| 190 | + ;; the chat screen makes, where the compose bar holds still under a backlog | ||
| 191 | + ;; that moves. | ||
| 192 | + ;; | ||
| 193 | + ;; What goes with the page is its centring at 620, on a window between that | ||
| 194 | + ;; and the split view's 900: the list is full-bleed there now, which is | ||
| 195 | + ;; what the conversation beside it has always been. | ||
| 196 | + [:vbox {:spacing 8 :margin 12} | ||
| 181 | ;; Your nick in the title, where "Chats" alone said nothing you did not | 197 | ;; Your nick in the title, where "Chats" alone said nothing you did not |
| 182 | ;; already know. It is what every channel calls you and what your own | 198 | ;; already know. It is what every channel calls you and what your own |
| 183 | ;; lines are signed with, and the only other place it showed was Settings. | 199 | ;; lines are signed with, and the only other place it showed was Settings. |
| @@ -211,9 +227,16 @@ | |||
| 211 | :on-change #(reset! s/search %)}] | 227 | :on-change #(reset! s/search %)}] |
| 212 | (when (seq @s/search) | 228 | (when (seq @s/search) |
| 213 | [:button {:label "✕" :on-click #(reset! s/search "")}])]]] | 229 | [:button {:label "✕" :on-click #(reset! s/search "")}])]]] |
| 214 | - (if (seq buffers) | 230 | + ;; `:fill-height` so the scroll inside is handed the rest of the window |
| 215 | - (for [b buffers] [conversation-row b]) | 231 | + ;; rather than the one card it starts out holding, and `:reserve` to keep |
| 216 | - [:card {} [:dim-label {:label "No conversations yet — join a channel."}]]) | 232 | + ;; the tabs' strip out of what it may take. |
| 233 | + ;; Named, so it is the same list — and the same scroll position — when the | ||
| 234 | + ;; reader comes back from a conversation. | ||
| 235 | + [:vbox {:key :list :fill-height true :reserve (below-list)} | ||
| 236 | + [:scroll {:scroll-key "chats-list" :orientation :vertical} | ||
| 237 | + (if (seq buffers) | ||
| 238 | + (for [b buffers] [conversation-row b]) | ||
| 239 | + [:card {} [:dim-label {:label "No conversations yet — join a channel."}]])]] | ||
| 217 | [:separator {}] | 240 | [:separator {}] |
| 218 | [tab-bar]])) | 241 | [tab-bar]])) |
| 219 | 242 | ||