Skip to main content

max / makeover-layout

Arrangement gains Single, for a screen that is one region Both existing members divide a width between two regions, so a screen that is one region had to borrow one and then undo it: MNW's embeds named list_detail(title, false) and the host spent a display:block cancelling the grid that produced. share() answers Option<Share> now: one region divides nothing, and an infallible full-width answer would let a host quietly draw a one-region screen as a grid with an empty second column. with_share leaves Single alone. The bar for a fourth member stays where 1786cb94 put it: a counted consumer, not a shape somebody can imagine.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01DwpiantpUgohzML4xr6KeQ
Author: Max Johnson <me@maxj.phd> · 2026-08-30 17:47 UTC
Signed with PGP, not checked
Commit: 571614da10f4867f8c9a267c318b8838f38b4ca1
Parent: 4b24345
2 files changed, +41 insertions, -11 deletions
M Cargo.toml +1 -1
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makeover-layout"
3 - version = "0.40.0"
3 + version = "0.41.0"
4 4 edition = "2024"
5 5 # One copy of this vocabulary per dependency graph, enforced by cargo rather
6 6 # than by remembering. Two versions of a description layer in one build means
M src/lib.rs +40 -10
@@ -3225,10 +3225,11 @@
3225 3225
3226 3226 /// How a screen is laid out.
3227 3227 ///
3228 - /// Two, and the second is not a variant of the first. goingson is list-detail,
3229 - /// Balanced Breakfast is sidebar plus content, and neither app has a third.
3230 - /// The tab group is a modifier rather than a member, because goingson uses it
3231 - /// *inside* the same content region rather than instead of one.
3228 + /// Three, and no one of them is a variant of another. goingson is list-detail,
3229 + /// Balanced Breakfast is sidebar plus content, and MNW's embeds are one region
3230 + /// filling the document. The tab group is a modifier rather than a member,
3231 + /// because goingson uses it *inside* the same content region rather than
3232 + /// instead of one.
3232 3233 ///
3233 3234 /// This exists at all because the router has to be able to express a screen
3234 3235 /// rather than only a control. Discovering the arrangement layer missing after
@@ -3258,6 +3259,18 @@
3258 3259 /// How much of the width the sidebar takes.
3259 3260 share: Share,
3260 3261 },
3262 + /// One region, filling the document.
3263 + ///
3264 + /// `52699de6`. The other two are both about dividing a width between two
3265 + /// regions, so a screen that is one region had to borrow one of them and
3266 + /// then undo it: MNW's five embeds said `list_detail(title, false)` and the
3267 + /// host spent a `display: block` cancelling the grid that produced. A host
3268 + /// writing CSS to contradict the description rather than to add to it is
3269 + /// the thing this member ends.
3270 + ///
3271 + /// Carries no [`Share`], because there is no division to describe. That is
3272 + /// why [`share`](Self::share) answers `None` here.
3273 + Single,
3261 3274 }
3262 3275
3263 3276 impl Arrangement {
@@ -3278,20 +3291,31 @@
3278 3291 }
3279 3292 }
3280 3293
3281 - /// How much of the width the first region takes.
3294 + /// How much of the width the first region takes, when two regions divide it.
3295 + ///
3296 + /// `None` for [`Single`](Self::Single): one region takes the width, and a
3297 + /// renderer that asked how to divide it was asking the wrong question. It
3298 + /// answers `Option` rather than a full-width `Share` so that a host cannot
3299 + /// quietly draw a one-region screen as a grid with an empty second column.
3282 3300 #[must_use]
3283 - pub const fn share(self) -> Share {
3301 + pub const fn share(self) -> Option<Share> {
3284 3302 match self {
3285 - Self::ListDetail { share, .. } | Self::SidebarContent { share } => share,
3303 + Self::ListDetail { share, .. } | Self::SidebarContent { share } => Some(share),
3304 + Self::Single => None,
3286 3305 }
3287 3306 }
3288 3307
3289 3308 /// The same arrangement, at this share.
3309 + ///
3310 + /// [`Single`](Self::Single) is returned unchanged: it has no division to
3311 + /// set, so a share named for it is a statement about nothing rather than an
3312 + /// error worth refusing a screen over.
3290 3313 #[must_use]
3291 3314 pub const fn with_share(self, share: Share) -> Self {
3292 3315 match self {
3293 3316 Self::ListDetail { tabbed, .. } => Self::ListDetail { tabbed, share },
3294 3317 Self::SidebarContent { .. } => Self::SidebarContent { share },
3318 + Self::Single => Self::Single,
3295 3319 }
3296 3320 }
3297 3321 }
@@ -7290,11 +7314,17 @@
7290 7314 // How much a sidebar takes and how much a list side takes are different
7291 7315 // questions, and this enum is the only thing that knows which is being
7292 7316 // asked.
7293 - assert_eq!(Arrangement::sidebar_content().share(), Share::SIDEBAR);
7294 - assert_eq!(Arrangement::list_detail(false).share(), Share::LIST);
7317 + assert_eq!(Arrangement::sidebar_content().share(), Some(Share::SIDEBAR));
7318 + assert_eq!(Arrangement::list_detail(false).share(), Some(Share::LIST));
7319 + // One region divides nothing, so there is no share to answer with.
7320 + assert_eq!(Arrangement::Single.share(), None);
7321 + assert_eq!(
7322 + Arrangement::Single.with_share(Share::percent(20)),
7323 + Arrangement::Single
7324 + );
7295 7325
7296 7326 let narrow = Arrangement::sidebar_content().with_share(Share::percent(20));
7297 - assert_eq!(narrow.share(), Share::percent(20));
7327 + assert_eq!(narrow.share(), Some(Share::percent(20)));
7298 7328 assert!(matches!(narrow, Arrangement::SidebarContent { .. }));
7299 7329 }
7300 7330