Give the sender's name and face room to be seen
The avatar was 22px and the nick a dimmed caption — the two things you scan a column of chat for were the smallest, faintest marks on it. The face is 32px and the name is set at body size in the plain text colour. Space above a new speaker and none inside their run groups the column into blocks rather than one evenly spaced list, and the reply chip is a link rather than a filled button: it points back at a line, it is not an action, and a pill above every answer was the loudest thing on screen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3cc5331 parent: a16dfae modified
src/frq/app.jolt +25 -15 | @@ -235,19 +235,22 @@ | ||
| 235 | 235 | [channel id] |
| 236 | 236 | [:vbox {:key :reply} |
| 237 | 237 | (if-let [target (s/message-by-id channel id)] |
| 238 | - [:button {:label (str "↩ " (:from target) ": " (summarise target 48)) | |
| 239 | - :on-click #(do (reset! s/jump-to id) | |
| 240 | - (reset! s/highlight id) | |
| 241 | - ;; Off again once the frame that scrolled has | |
| 242 | - ;; been painted, so the reader keeps the view. | |
| 243 | - (vidya/after! 120 (fn [] (reset! s/jump-to nil))) | |
| 244 | - ;; The highlight stays long enough to be read, | |
| 245 | - ;; and only clears itself: a later jump elsewhere | |
| 246 | - ;; owns the highlight from then on. | |
| 247 | - (vidya/after! 2000 | |
| 248 | - (fn [] | |
| 249 | - (when (= id @s/highlight) | |
| 250 | - (reset! s/highlight nil)))))}] | |
| 238 | + ;; A link, not a button: the chip is a pointer back to a line, not an | |
| 239 | + ;; action, and a filled pill above every answer was the loudest thing in | |
| 240 | + ;; the column. | |
| 241 | + [:link {:label (str "↩ " (:from target) ": " (summarise target 48)) | |
| 242 | + :on-click #(do (reset! s/jump-to id) | |
| 243 | + (reset! s/highlight id) | |
| 244 | + ;; Off again once the frame that scrolled has | |
| 245 | + ;; been painted, so the reader keeps the view. | |
| 246 | + (vidya/after! 120 (fn [] (reset! s/jump-to nil))) | |
| 247 | + ;; The highlight stays long enough to be read, | |
| 248 | + ;; and only clears itself: a later jump elsewhere | |
| 249 | + ;; owns the highlight from then on. | |
| 250 | + (vidya/after! 2000 | |
| 251 | + (fn [] | |
| 252 | + (when (= id @s/highlight) | |
| 253 | + (reset! s/highlight nil)))))}] | |
| 251 | 254 | ;; The message it answers is older than this buffer goes. |
| 252 | 255 | [:dim-label {:label "↩ replying to an earlier message"}])]) |
| 253 | 256 | |
| @@ -268,6 +271,10 @@ | ||
| 268 | 271 | ;; than in the reader's count of rows. |
| 269 | 272 | highlit? (boolean (and (:id m) (= (:id m) @s/highlight)))] |
| 270 | 273 | [:vbox {:key i :spacing 2 :margin 0 |
| 274 | + ;; Space above a new speaker, none inside their run: the gap is | |
| 275 | + ;; what separates one person's block from the next, and evenly | |
| 276 | + ;; spaced lines are what made the column read as one dense list. | |
| 277 | + :margin-top (if same-sender? 0 10) | |
| 271 | 278 | ;; The jump target is what a "go to message" click scrolls to. |
| 272 | 279 | :scroll-here (boolean (and (:id m) (= (:id m) @s/jump-to)))} |
| 273 | 280 | [(if highlit? :card :vbox) {:key :body :spacing 2 :margin 0} |
| @@ -290,8 +297,11 @@ | ||
| 290 | 297 | (let [_ @s/media-tick] |
| 291 | 298 | [:avatar {:label (:from m) |
| 292 | 299 | :src (or (avatars/path-when-ready (:actor m)) "") |
| 293 | - :size 22}]) | |
| 294 | - [:dim-label {:label (:from m)}] | |
| 300 | + :size 32}]) | |
| 301 | + ;; The name carries the row, so it is set at body size in the plain | |
| 302 | + ;; text colour: dimmed caption made the one thing you scan a column | |
| 303 | + ;; for the faintest thing on it. | |
| 304 | + [:label {:label (:from m)}] | |
| 295 | 305 | (when-let [at (:at m)] |
| 296 | 306 | [:dim-label {:label (clock/clock-time at)}]) |
| 297 | 307 | ;; Answering is offered where the sender is named — a run of lines |
| @@ -235,19 +235,22 @@ | |||
| 235 | [channel id] | 235 | [channel id] |
| 236 | [:vbox {:key :reply} | 236 | [:vbox {:key :reply} |
| 237 | (if-let [target (s/message-by-id channel id)] | 237 | (if-let [target (s/message-by-id channel id)] |
| 238 | - [:button {:label (str "↩ " (:from target) ": " (summarise target 48)) | 238 | + ;; A link, not a button: the chip is a pointer back to a line, not an |
| 239 | - :on-click #(do (reset! s/jump-to id) | 239 | + ;; action, and a filled pill above every answer was the loudest thing in |
| 240 | - (reset! s/highlight id) | 240 | + ;; the column. |
| 241 | - ;; Off again once the frame that scrolled has | 241 | + [:link {:label (str "↩ " (:from target) ": " (summarise target 48)) |
| 242 | - ;; been painted, so the reader keeps the view. | 242 | + :on-click #(do (reset! s/jump-to id) |
| 243 | - (vidya/after! 120 (fn [] (reset! s/jump-to nil))) | 243 | + (reset! s/highlight id) |
| 244 | - ;; The highlight stays long enough to be read, | 244 | + ;; Off again once the frame that scrolled has |
| 245 | - ;; and only clears itself: a later jump elsewhere | 245 | + ;; been painted, so the reader keeps the view. |
| 246 | - ;; owns the highlight from then on. | 246 | + (vidya/after! 120 (fn [] (reset! s/jump-to nil))) |
| 247 | - (vidya/after! 2000 | 247 | + ;; The highlight stays long enough to be read, |
| 248 | - (fn [] | 248 | + ;; and only clears itself: a later jump elsewhere |
| 249 | - (when (= id @s/highlight) | 249 | + ;; owns the highlight from then on. |
| 250 | - (reset! s/highlight nil)))))}] | 250 | + (vidya/after! 2000 |
| 251 | + (fn [] | ||
| 252 | + (when (= id @s/highlight) | ||
| 253 | + (reset! s/highlight nil)))))}] | ||
| 251 | ;; The message it answers is older than this buffer goes. | 254 | ;; The message it answers is older than this buffer goes. |
| 252 | [:dim-label {:label "↩ replying to an earlier message"}])]) | 255 | [:dim-label {:label "↩ replying to an earlier message"}])]) |
| 253 | 256 | ||
| @@ -268,6 +271,10 @@ | |||
| 268 | ;; than in the reader's count of rows. | 271 | ;; than in the reader's count of rows. |
| 269 | highlit? (boolean (and (:id m) (= (:id m) @s/highlight)))] | 272 | highlit? (boolean (and (:id m) (= (:id m) @s/highlight)))] |
| 270 | [:vbox {:key i :spacing 2 :margin 0 | 273 | [:vbox {:key i :spacing 2 :margin 0 |
| 274 | + ;; Space above a new speaker, none inside their run: the gap is | ||
| 275 | + ;; what separates one person's block from the next, and evenly | ||
| 276 | + ;; spaced lines are what made the column read as one dense list. | ||
| 277 | + :margin-top (if same-sender? 0 10) | ||
| 271 | ;; The jump target is what a "go to message" click scrolls to. | 278 | ;; The jump target is what a "go to message" click scrolls to. |
| 272 | :scroll-here (boolean (and (:id m) (= (:id m) @s/jump-to)))} | 279 | :scroll-here (boolean (and (:id m) (= (:id m) @s/jump-to)))} |
| 273 | [(if highlit? :card :vbox) {:key :body :spacing 2 :margin 0} | 280 | [(if highlit? :card :vbox) {:key :body :spacing 2 :margin 0} |
| @@ -290,8 +297,11 @@ | |||
| 290 | (let [_ @s/media-tick] | 297 | (let [_ @s/media-tick] |
| 291 | [:avatar {:label (:from m) | 298 | [:avatar {:label (:from m) |
| 292 | :src (or (avatars/path-when-ready (:actor m)) "") | 299 | :src (or (avatars/path-when-ready (:actor m)) "") |
| 293 | - :size 22}]) | 300 | + :size 32}]) |
| 294 | - [:dim-label {:label (:from m)}] | 301 | + ;; The name carries the row, so it is set at body size in the plain |
| 302 | + ;; text colour: dimmed caption made the one thing you scan a column | ||
| 303 | + ;; for the faintest thing on it. | ||
| 304 | + [:label {:label (:from m)}] | ||
| 295 | (when-let [at (:at m)] | 305 | (when-let [at (:at m)] |
| 296 | [:dim-label {:label (clock/clock-time at)}]) | 306 | [:dim-label {:label (clock/clock-time at)}]) |
| 297 | ;; Answering is offered where the sender is named — a run of lines | 307 | ;; Answering is offered where the sender is named — a run of lines |