Skip to main content

max / makenotwork

Declare the carousel glue, taking a parked shape off the list `widgets/carousel.rs:region` was refused on the hard limit: a closure holding a `let mut` and an `if let`. The refusal was about the frame conversion inside the closure, not about the description. With the conversion written as two matches beside the declaration and `quasi_basics::Gallery` doing the ordering, the shape is one `include`. `frame` and `gallery` stay plain functions on purpose. Turning a template's frame into the widget's is a conversion, and a conversion is what a supplier is for. mnw goes `declared 154, remaining 9` to `declared 155, remaining 8`.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01P8ostB2UmZJGj5WjSHRSot
Author: Max Johnson <me@maxj.phd> · 2026-09-05 13:47 UTC
Signed with PGP, not checked
Commit: 945e99e3c71d7a65add10686afc0844291b2e415
Parent: 168e611
1 file changed, +39 insertions, -22 deletions
@@ -11,34 +11,51 @@
11 11 //! because it has no idea this host prefixes its tags with `mnw-`. The template
12 12 //! puts one around the other and both halves stay honest.
13 13
14 - use quasi_basics::Frame;
15 - use quasi_router::Node;
14 + use quasi_basics::{Frame, Gallery};
15 + use quasi_declare::declare;
16 16
17 - /// The widget, for a screen that owns its whole document.
17 + use crate::templates::CarouselFrame;
18 +
19 + /// One template frame as the widget's own.
18 20 ///
19 - /// [`html`] is the Askama half and this is the described half. Both come out of
20 - /// `quasi_basics::carousel`, so the project page's gallery and the three
21 - /// templates that still call the macro cannot be two different carousels.
22 - #[must_use]
23 - pub fn region(id: &str, items: &[crate::templates::CarouselFrame]) -> Node {
24 - Node::Region(quasi_basics::carousel(
25 - id,
26 - items.iter().map(|frame| {
27 - let mut built = Frame::new(&frame.image, &frame.alt);
28 - if let Some((w, h)) = frame.intrinsic {
29 - built = built.intrinsic(w, h);
30 - }
31 - match &frame.caption {
32 - Some(caption) => built.caption(caption),
33 - None => built,
34 - }
35 - }),
36 - ))
21 + /// The conversion, and the whole of what stayed in MNW. Written as two matches
22 + /// rather than a `let mut` and an `if let` because a description reads a value
23 + /// and a builder that reassigns itself is not one, which is the same reason
24 + /// [`gallery`] is a supplier and not part of the declaration below.
25 + fn frame(source: &CarouselFrame) -> Frame {
26 + let sized = match source.intrinsic {
27 + Some((width, height)) => Frame::new(&source.image, &source.alt).intrinsic(width, height),
28 + None => Frame::new(&source.image, &source.alt),
29 + };
30 + match &source.caption {
31 + Some(caption) => sized.caption(caption),
32 + None => sized,
33 + }
34 + }
35 +
36 + /// The template's frames as the widget's read.
37 + ///
38 + /// [`Gallery::new`] is where the first frame becomes the one on screen, so this
39 + /// is the one place the order is looked at. Everything after it reads a fact.
40 + fn gallery(items: &[CarouselFrame]) -> Gallery {
41 + items.iter().map(frame).collect()
42 + }
43 +
44 + declare! {
45 + /// The widget, for a screen that owns its whole document.
46 + ///
47 + /// [`html`] is the Askama half and this is the described half. Both come out
48 + /// of `quasi_basics::carousel`, so the project page's gallery and the three
49 + /// templates that still call the macro cannot be two different carousels.
50 + #[must_use]
51 + pub shape region(id: &str, items: &[CarouselFrame]) -> Node;
52 +
53 + include quasi_basics::carousel(id, &gallery(items));
37 54 }
38 55
39 56 /// The markup, for an Askama template to drop in.
40 57 #[must_use]
41 - pub fn html(id: &str, items: &[crate::templates::CarouselFrame]) -> String {
58 + pub fn html(id: &str, items: &[CarouselFrame]) -> String {
42 59 use quasi_axum::Serves as _;
43 60
44 61 let node = region(id, items);