nandi/frqpublic Fork 0
795fda4
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.

Give the Flutter backlog the gaps it was already asking for

Every message row writes `:margin-top 10` for the gap above it and
`:margin-right 10` for clearance from the scrollbar, and the hiccup backend
read `:margin` and nothing else — so on Flutter the whole conversation was
drawn edge to edge, one message touching the next, while the same tree under
libcosmic breathed.

`insets-of` reads all three spellings in one place: `:margin` for four sides,
a per-side key for one, and the per-side key winning where a box gives both —
which is how `message-row` writes `:margin 0` with a top and a right over it.
Nil when nothing is asked for, so a box with no margin still gets no Padding.

Rows get it too, for the jump button's `:margin-bottom` under the backlog.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-09-12T07:11:46-07:00 Browse files
795fda4 parent: 3f26695
modified flutter/src/frq/hiccup.cljd +44 -22
@@ -46,6 +46,24 @@
4646 (cond (number? x) (double x)
4747 :else default))
4848
49+(defn- insets-of
50+ "The margin a container asks for, or nil when it asks for none.
51+
52+ Three spellings, and a box may mix them: `:margin` is all four sides, the
53+ per-side keys are each one side, and a per-side key wins over `:margin`
54+ where both are given — that is how `message-row` writes `:margin 0` with a
55+ `:margin-top` and a `:margin-right` over it.
56+
57+ The per-side keys are not decoration: the backlog's gap between messages and
58+ its clearance for the scrollbar are both written that way, so a backend that
59+ reads `:margin` alone draws the conversation edge to edge."
60+ [p]
61+ (let [side (fn [k] (dbl (get p k) (dbl (:margin p) 0.0)))
62+ l (side :margin-left) r (side :margin-right)
63+ tp (side :margin-top) b (side :margin-bottom)]
64+ (when (some pos? [l r tp b])
65+ (m/EdgeInsets.only .left l .right r .top tp .bottom b))))
66+
4967 ;; ------------------------------------------------------------- text entry
5068
5169 (defonce ^:private controllers
@@ -359,8 +377,8 @@
359377 m/MainAxisSize.min)
360378 .spacing (dbl (:spacing p) 0.0)
361379 .children (flexed fills-column? (body node)))
362- c (if-let [mg (:margin p)]
363- (m/Padding .padding (m/EdgeInsets.all (dbl mg 0.0)) .child c)
380+ c (if-let [mg (insets-of p)]
381+ (m/Padding .padding mg .child c)
364382 c)]
365383 (if-let [w (width-of p)]
366384 (m/SizedBox .width w .child c)
@@ -391,26 +409,30 @@
391409 ;; Wrap. Without it the longest message decided the width of the
392410 ;; conversation and the rest overflowed off the right.
393411 :hbox
394- (if (true? (:wrap p))
395- (m/Wrap
396- .spacing (dbl (:spacing p) 0.0)
397- .runSpacing (dbl (:spacing p) 0.0)
398- .crossAxisAlignment m/WrapCrossAlignment.center
399- .children kids)
400- (m/Row
401- ;; Stretch when a child fills vertically: a Row hands its children a
402- ;; loose height by default, and a column that wants what is left of it
403- ;; needs a tight one.
404- .crossAxisAlignment (if (some #(fills-column? (expand %)) (body node))
405- m/CrossAxisAlignment.stretch
406- m/CrossAxisAlignment.center)
407- ;; A row holding something that fills has to be given the width to
408- ;; divide, so it is max rather than min whenever a child asks.
409- .mainAxisSize (if (some #(fills-row? (expand %)) (body node))
410- m/MainAxisSize.max
411- m/MainAxisSize.min)
412- .spacing (dbl (:spacing p) 0.0)
413- .children (flexed fills-row? (body node))))
412+ (let [row (if (true? (:wrap p))
413+ (m/Wrap
414+ .spacing (dbl (:spacing p) 0.0)
415+ .runSpacing (dbl (:spacing p) 0.0)
416+ .crossAxisAlignment m/WrapCrossAlignment.center
417+ .children kids)
418+ (m/Row
419+ ;; Stretch when a child fills vertically: a Row hands its
420+ ;; children a loose height by default, and a column that
421+ ;; wants what is left of it needs a tight one.
422+ .crossAxisAlignment (if (some #(fills-column? (expand %)) (body node))
423+ m/CrossAxisAlignment.stretch
424+ m/CrossAxisAlignment.center)
425+ ;; A row holding something that fills has to be given the
426+ ;; width to divide, so it is max rather than min whenever a
427+ ;; child asks.
428+ .mainAxisSize (if (some #(fills-row? (expand %)) (body node))
429+ m/MainAxisSize.max
430+ m/MainAxisSize.min)
431+ .spacing (dbl (:spacing p) 0.0)
432+ .children (flexed fills-row? (body node))))]
433+ (if-let [mg (insets-of p)]
434+ (m/Padding .padding mg .child row)
435+ row))
414436
415437 :label (txt ctx (:label p "") t/text-body t/on-bg)
416438 :dim-label (txt ctx (:label p "") t/text-caption t/dim)
@@ -46,6 +46,24 @@
46 (cond (number? x) (double x)46 (cond (number? x) (double x)
47 :else default))47 :else default))
48 48
49+(defn- insets-of
50+ "The margin a container asks for, or nil when it asks for none.
51+
52+ Three spellings, and a box may mix them: `:margin` is all four sides, the
53+ per-side keys are each one side, and a per-side key wins over `:margin`
54+ where both are given — that is how `message-row` writes `:margin 0` with a
55+ `:margin-top` and a `:margin-right` over it.
56+
57+ The per-side keys are not decoration: the backlog's gap between messages and
58+ its clearance for the scrollbar are both written that way, so a backend that
59+ reads `:margin` alone draws the conversation edge to edge."
60+ [p]
61+ (let [side (fn [k] (dbl (get p k) (dbl (:margin p) 0.0)))
62+ l (side :margin-left) r (side :margin-right)
63+ tp (side :margin-top) b (side :margin-bottom)]
64+ (when (some pos? [l r tp b])
65+ (m/EdgeInsets.only .left l .right r .top tp .bottom b))))
66+
49 ;; ------------------------------------------------------------- text entry67 ;; ------------------------------------------------------------- text entry
50 68
51 (defonce ^:private controllers69 (defonce ^:private controllers
@@ -359,8 +377,8 @@
359 m/MainAxisSize.min)377 m/MainAxisSize.min)
360 .spacing (dbl (:spacing p) 0.0)378 .spacing (dbl (:spacing p) 0.0)
361 .children (flexed fills-column? (body node)))379 .children (flexed fills-column? (body node)))
362- c (if-let [mg (:margin p)]380+ c (if-let [mg (insets-of p)]
363- (m/Padding .padding (m/EdgeInsets.all (dbl mg 0.0)) .child c)381+ (m/Padding .padding mg .child c)
364 c)]382 c)]
365 (if-let [w (width-of p)]383 (if-let [w (width-of p)]
366 (m/SizedBox .width w .child c)384 (m/SizedBox .width w .child c)
@@ -391,26 +409,30 @@
391 ;; Wrap. Without it the longest message decided the width of the409 ;; Wrap. Without it the longest message decided the width of the
392 ;; conversation and the rest overflowed off the right.410 ;; conversation and the rest overflowed off the right.
393 :hbox411 :hbox
394- (if (true? (:wrap p))412+ (let [row (if (true? (:wrap p))
395- (m/Wrap413+ (m/Wrap
396- .spacing (dbl (:spacing p) 0.0)414+ .spacing (dbl (:spacing p) 0.0)
397- .runSpacing (dbl (:spacing p) 0.0)415+ .runSpacing (dbl (:spacing p) 0.0)
398- .crossAxisAlignment m/WrapCrossAlignment.center416+ .crossAxisAlignment m/WrapCrossAlignment.center
399- .children kids)417+ .children kids)
400- (m/Row418+ (m/Row
401- ;; Stretch when a child fills vertically: a Row hands its children a419+ ;; Stretch when a child fills vertically: a Row hands its
402- ;; loose height by default, and a column that wants what is left of it420+ ;; children a loose height by default, and a column that
403- ;; needs a tight one.421+ ;; wants what is left of it needs a tight one.
404- .crossAxisAlignment (if (some #(fills-column? (expand %)) (body node))422+ .crossAxisAlignment (if (some #(fills-column? (expand %)) (body node))
405- m/CrossAxisAlignment.stretch423+ m/CrossAxisAlignment.stretch
406- m/CrossAxisAlignment.center)424+ m/CrossAxisAlignment.center)
407- ;; A row holding something that fills has to be given the width to425+ ;; A row holding something that fills has to be given the
408- ;; divide, so it is max rather than min whenever a child asks.426+ ;; width to divide, so it is max rather than min whenever a
409- .mainAxisSize (if (some #(fills-row? (expand %)) (body node))427+ ;; child asks.
410- m/MainAxisSize.max428+ .mainAxisSize (if (some #(fills-row? (expand %)) (body node))
411- m/MainAxisSize.min)429+ m/MainAxisSize.max
412- .spacing (dbl (:spacing p) 0.0)430+ m/MainAxisSize.min)
413- .children (flexed fills-row? (body node))))431+ .spacing (dbl (:spacing p) 0.0)
432+ .children (flexed fills-row? (body node))))]
433+ (if-let [mg (insets-of p)]
434+ (m/Padding .padding mg .child row)
435+ row))
414 436
415 :label (txt ctx (:label p "") t/text-body t/on-bg)437 :label (txt ctx (:label p "") t/text-body t/on-bg)
416 :dim-label (txt ctx (:label p "") t/text-caption t/dim)438 :dim-label (txt ctx (:label p "") t/text-caption t/dim)