Size a preview against the window it is shown in
A picture in the conversation was capped at 260 points however big the window was. That is a stamp on a laptop and most of the screen on a phone, and the pictures that actually arrive here are phone screenshots — 1080x2400, whose height is what binds, so a low cap draws them as a strip too narrow to read a word of. The height comes from the window now, polled off the same tick that already follows its width: `screen-size` reports both, and only what a component derefs re-renders, so a drag of the window's edge resizes the pictures and touches nothing else. Four fifths of the height, clamped, with the old 260 standing in until the first poll lands. The width is bounded the same way, which is what keeps a wide, short picture from taking the column. Neither is a stretch: `:image` fits to whichever bound it meets first, and never past the picture's own pixels. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9a5c2a8 parent: 03d5a6b modified
src/frq/app.jolt +41 -3 | @@ -496,6 +496,36 @@ | ||
| 496 | 496 | (= :failed (:status pr)) [:dim-label {:label "No Bluesky profile found"}])] |
| 497 | 497 | [:dim-label {:label "Click for the full profile"}]])) |
| 498 | 498 | |
| 499 | +(defn- preview-height | |
| 500 | + "How tall a picture in the conversation may be. | |
| 501 | + | |
| 502 | + Against the window rather than a fixed 260: on a phone-sized window a | |
| 503 | + preview that tall is the whole screen, and on a laptop one it is a stamp of | |
| 504 | + something worth looking at. | |
| 505 | + | |
| 506 | + Four fifths of the window, because the pictures that arrive here are mostly | |
| 507 | + phone screenshots — 1080x2400, whose height is what binds, and any smaller | |
| 508 | + share draws them as a strip too narrow to read a word of. A picture is what | |
| 509 | + the message is; the line above it is enough context to place it, and the | |
| 510 | + scroll is how the rest is reached. | |
| 511 | + | |
| 512 | + Clamped at both ends, and falling back to the old fixed height until the | |
| 513 | + first poll has landed: a preview is a preview either way, and the lightbox | |
| 514 | + is what full size is for." | |
| 515 | + [] | |
| 516 | + (let [h @s/window-height] | |
| 517 | + (if (pos? h) (min 900 (max 240 (long (* 0.8 h)))) 260))) | |
| 518 | + | |
| 519 | +(defn- preview-width | |
| 520 | + "How wide a picture in the conversation may be. | |
| 521 | + | |
| 522 | + The height alone leaves a wide, short picture to take the column and push | |
| 523 | + the words around it; this holds it inside the conversation the same way. | |
| 524 | + Fitted, never stretched — `:image` scales to whichever bound it meets first." | |
| 525 | + [] | |
| 526 | + (let [w @s/window-width] | |
| 527 | + (if (pos? w) (min 900 (max 240 (long (* 0.95 w)))) 320))) | |
| 528 | + | |
| 499 | 529 | (defn message-row |
| 500 | 530 | "One message: who said it, when, what you can do to it, and the words. |
| 501 | 531 | |
| @@ -618,7 +648,8 @@ | ||
| 618 | 648 | (when-let [path (media/path-when-ready url)] |
| 619 | 649 | [:image {:key url |
| 620 | 650 | :src path |
| 621 | - :max-height 260 | |
| 651 | + :max-height (preview-height) | |
| 652 | + :max-width (preview-width) | |
| 622 | 653 | :on-click #(reset! s/lightbox {:path path :url url})}]))))] |
| 623 | 654 | ;; Reactions go last, under whatever the message turned out to be: a |
| 624 | 655 | ;; line with a picture on it is the picture, and pills between the words |
| @@ -1256,9 +1287,16 @@ | ||
| 1256 | 1287 | ;; delivered: the backend reports a size by writing it onto the window node, |
| 1257 | 1288 | ;; and only what a component derefs re-renders — so the layout follows a drag |
| 1258 | 1289 | ;; of the window's edge without every frame touching the tree. |
| 1259 | - (vidya/every! 200 #(let [w (vidya/window-width)] | |
| 1290 | + ;; | |
| 1291 | + ;; The height comes off the same tick, from `screen-size` rather than a | |
| 1292 | + ;; second call: it is the window's content size, and the pictures in the | |
| 1293 | + ;; conversation are sized against it. | |
| 1294 | + (vidya/every! 200 #(let [w (vidya/window-width) | |
| 1295 | + h (long (second (vidya/screen-size)))] | |
| 1260 | 1296 | (when (not= w @s/window-width) |
| 1261 | - (reset! s/window-width w)))) | |
| 1297 | + (reset! s/window-width w)) | |
| 1298 | + (when (not= h @s/window-height) | |
| 1299 | + (reset! s/window-height h)))) | |
| 1262 | 1300 | ;; The nick in the window title, so a second window of this client is told |
| 1263 | 1301 | ;; apart from the first by the one thing that differs — and so the answer to |
| 1264 | 1302 | ;; "who am I here?" is on screen without opening Settings. |
| @@ -496,6 +496,36 @@ | |||
| 496 | (= :failed (:status pr)) [:dim-label {:label "No Bluesky profile found"}])] | 496 | (= :failed (:status pr)) [:dim-label {:label "No Bluesky profile found"}])] |
| 497 | [:dim-label {:label "Click for the full profile"}]])) | 497 | [:dim-label {:label "Click for the full profile"}]])) |
| 498 | 498 | ||
| 499 | +(defn- preview-height | ||
| 500 | + "How tall a picture in the conversation may be. | ||
| 501 | + | ||
| 502 | + Against the window rather than a fixed 260: on a phone-sized window a | ||
| 503 | + preview that tall is the whole screen, and on a laptop one it is a stamp of | ||
| 504 | + something worth looking at. | ||
| 505 | + | ||
| 506 | + Four fifths of the window, because the pictures that arrive here are mostly | ||
| 507 | + phone screenshots — 1080x2400, whose height is what binds, and any smaller | ||
| 508 | + share draws them as a strip too narrow to read a word of. A picture is what | ||
| 509 | + the message is; the line above it is enough context to place it, and the | ||
| 510 | + scroll is how the rest is reached. | ||
| 511 | + | ||
| 512 | + Clamped at both ends, and falling back to the old fixed height until the | ||
| 513 | + first poll has landed: a preview is a preview either way, and the lightbox | ||
| 514 | + is what full size is for." | ||
| 515 | + [] | ||
| 516 | + (let [h @s/window-height] | ||
| 517 | + (if (pos? h) (min 900 (max 240 (long (* 0.8 h)))) 260))) | ||
| 518 | + | ||
| 519 | +(defn- preview-width | ||
| 520 | + "How wide a picture in the conversation may be. | ||
| 521 | + | ||
| 522 | + The height alone leaves a wide, short picture to take the column and push | ||
| 523 | + the words around it; this holds it inside the conversation the same way. | ||
| 524 | + Fitted, never stretched — `:image` scales to whichever bound it meets first." | ||
| 525 | + [] | ||
| 526 | + (let [w @s/window-width] | ||
| 527 | + (if (pos? w) (min 900 (max 240 (long (* 0.95 w)))) 320))) | ||
| 528 | + | ||
| 499 | (defn message-row | 529 | (defn message-row |
| 500 | "One message: who said it, when, what you can do to it, and the words. | 530 | "One message: who said it, when, what you can do to it, and the words. |
| 501 | 531 | ||
| @@ -618,7 +648,8 @@ | |||
| 618 | (when-let [path (media/path-when-ready url)] | 648 | (when-let [path (media/path-when-ready url)] |
| 619 | [:image {:key url | 649 | [:image {:key url |
| 620 | :src path | 650 | :src path |
| 621 | - :max-height 260 | 651 | + :max-height (preview-height) |
| 652 | + :max-width (preview-width) | ||
| 622 | :on-click #(reset! s/lightbox {:path path :url url})}]))))] | 653 | :on-click #(reset! s/lightbox {:path path :url url})}]))))] |
| 623 | ;; Reactions go last, under whatever the message turned out to be: a | 654 | ;; Reactions go last, under whatever the message turned out to be: a |
| 624 | ;; line with a picture on it is the picture, and pills between the words | 655 | ;; line with a picture on it is the picture, and pills between the words |
| @@ -1256,9 +1287,16 @@ | |||
| 1256 | ;; delivered: the backend reports a size by writing it onto the window node, | 1287 | ;; delivered: the backend reports a size by writing it onto the window node, |
| 1257 | ;; and only what a component derefs re-renders — so the layout follows a drag | 1288 | ;; and only what a component derefs re-renders — so the layout follows a drag |
| 1258 | ;; of the window's edge without every frame touching the tree. | 1289 | ;; of the window's edge without every frame touching the tree. |
| 1259 | - (vidya/every! 200 #(let [w (vidya/window-width)] | 1290 | + ;; |
| 1291 | + ;; The height comes off the same tick, from `screen-size` rather than a | ||
| 1292 | + ;; second call: it is the window's content size, and the pictures in the | ||
| 1293 | + ;; conversation are sized against it. | ||
| 1294 | + (vidya/every! 200 #(let [w (vidya/window-width) | ||
| 1295 | + h (long (second (vidya/screen-size)))] | ||
| 1260 | (when (not= w @s/window-width) | 1296 | (when (not= w @s/window-width) |
| 1261 | - (reset! s/window-width w)))) | 1297 | + (reset! s/window-width w)) |
| 1298 | + (when (not= h @s/window-height) | ||
| 1299 | + (reset! s/window-height h)))) | ||
| 1262 | ;; The nick in the window title, so a second window of this client is told | 1300 | ;; The nick in the window title, so a second window of this client is told |
| 1263 | ;; apart from the first by the one thing that differs — and so the answer to | 1301 | ;; apart from the first by the one thing that differs — and so the answer to |
| 1264 | ;; "who am I here?" is on screen without opening Settings. | 1302 | ;; "who am I here?" is on screen without opening Settings. |
modified
src/frq/state.jolt +6 -0 | @@ -108,6 +108,12 @@ | ||
| 108 | 108 | ;; list and the conversation are both on screen instead of taking turns. |
| 109 | 109 | (defonce window-width (atom 0)) |
| 110 | 110 | |
| 111 | +;; The window's content height, polled beside the width and for the same | |
| 112 | +;; reason. What it is for is the pictures in the conversation: a preview sized | |
| 113 | +;; against the window is a picture on a laptop and a thumbnail on a phone, | |
| 114 | +;; where one fixed height is only ever right on one of them. | |
| 115 | +(defonce window-height (atom 0)) | |
| 116 | + | |
| 111 | 117 | ;; Where the second pane starts paying for itself. Below this a 300pt list |
| 112 | 118 | ;; beside a conversation leaves the messages narrower than the phone layout |
| 113 | 119 | ;; they were written for. |
| @@ -108,6 +108,12 @@ | |||
| 108 | ;; list and the conversation are both on screen instead of taking turns. | 108 | ;; list and the conversation are both on screen instead of taking turns. |
| 109 | (defonce window-width (atom 0)) | 109 | (defonce window-width (atom 0)) |
| 110 | 110 | ||
| 111 | +;; The window's content height, polled beside the width and for the same | ||
| 112 | +;; reason. What it is for is the pictures in the conversation: a preview sized | ||
| 113 | +;; against the window is a picture on a laptop and a thumbnail on a phone, | ||
| 114 | +;; where one fixed height is only ever right on one of them. | ||
| 115 | +(defonce window-height (atom 0)) | ||
| 116 | + | ||
| 111 | ;; Where the second pane starts paying for itself. Below this a 300pt list | 117 | ;; Where the second pane starts paying for itself. Below this a 300pt list |
| 112 | ;; beside a conversation leaves the messages narrower than the phone layout | 118 | ;; beside a conversation leaves the messages narrower than the phone layout |
| 113 | ;; they were written for. | 119 | ;; they were written for. |