Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion plugins/leaf/skills/leaf/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,8 @@ message is scrolled by the panel's own list and by nothing else. The open
comment panel and tray panel each occupy their own strip when the viewport can
hold it and cover the page under their respective media query otherwise.
`stateStrip` and `stateRoom` are the geometry readings, and both count every
strip the chrome holds; CSS owns the body's corresponding layout.
strip the chrome holds and the gutter the scroller's own bar takes — a window is
the page's box on neither count; CSS owns the body's corresponding layout.

Both regions fixed to a side of the window are drawn by the reader. `drawnEdge`
is the one implementation: each caller supplies the side its region is held to, a
Expand Down
17 changes: 13 additions & 4 deletions plugins/leaf/skills/leaf/assets/leaf.js
Original file line number Diff line number Diff line change
Expand Up @@ -1731,11 +1731,20 @@ const trayStrip = () =>
// owes. The width is published rather than spent here for the reason the floor is read
// blind — the runtime says how wide the page's box is and never learns which idiom hangs
// something in the margin, so an idiom's own rule does its own arithmetic against it, the
// way the wide rules already spend --lf-room. A query cannot see the panel and this can,
// which is the whole of what the runtime adds; a page with no runtime behind it falls back
// to the viewport in each rule that reads it.
// way the wide rules already spend --lf-room. A query cannot see the panel or the
// scroller's own bar and this can, which is the whole of what the runtime adds; a page with
// no runtime behind it falls back to the viewport in each rule that reads it.
function stateStrip() {
const avail = document.documentElement.clientWidth - panelStrip() - trayStrip();
// The same gutter stateRoom takes off, and taken the same way, for the reason it gives
// there: body is the document's scroller, so a classic bar comes out of the room this
// page has while the window says nothing about it. The coarse answer owes it as much as
// the fine one. Without it the floor was met by a window with a bar's width less page
// behind it, and the strip came out of the column the floor exists to keep it out of —
// a sidenote page at exactly 1152px read at a 705px measure, and a sidebar and a note at
// 1416px did the same. Overlay scrollbars are 0 here, which is why a Mac never saw it.
const gutter = pageScroller.offsetWidth - pageScroller.clientWidth;
const avail =
document.documentElement.clientWidth - gutter - panelStrip() - trayStrip();
document.body.toggleAttribute("data-lf-cramped", avail < stripMin());
document.documentElement.style.setProperty("--lf-avail", avail + "px");
}
Expand Down
17 changes: 17 additions & 0 deletions tests/test_render_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -1990,7 +1990,24 @@ def test_opposite_margin_residents_wait_for_the_room_they_need(
assert tight["column"]["width"] == 720
assert tight["sideways"] == 0

# The floor is a fact about the page's box, and the window is not that box wherever
# the platform draws a classic scrollbar: body is the document's scroller, so its bar
# comes out of the room the strips and the column divide between them. So the page is
# asked for the difference rather than a number being written that is right on one
# platform. Either way the column keeps its measure at the floor itself, which is what
# the strip is floored to protect: given the room, both strips stand outside a full
# column; short of it by the width of a bar, the veto hands them back.
resized(page, 1416, 800)
bar = page.evaluate(
"() => document.documentElement.clientWidth - document.body.clientWidth"
)
at_floor = page.evaluate(reading)
assert at_floor["column"]["width"] == 720, (

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The runtime reads the gutter off the scroller's own two boxes; this reads it off the window and body's padding box. Those are the same number only while body carries no margin — which holds here, the panel not being clicked until the block below — but that is a fact about where this block sits in the test rather than about the gutter, and the panel's strip is a body margin. A later case reordered above the .lf-comments click would drive resized a panel's width off with no assertion naming the reason. Matching stateStrip's own reading makes it margin-independent and puts the test on the formula it is holding:

Suggested change
assert at_floor["column"]["width"] == 720, (
"() => document.body.offsetWidth - document.body.clientWidth"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taken, and one step further in #65: the read is gone rather than respelled. main had meanwhile moved the reading probe's own gutter key onto exactly this expression (a196d93), so the roomy width is driven from at_floor["gutter"] and the separate evaluate is dropped — one bar, read once, in the same terms the veto does its arithmetic in. Your margin-independence point is what makes that the right key to reach for: the window spelling agrees with the other two only while body carries no margin, and the panel's strip below is a body margin.

"the strip came out of the column at the combined floor, which is the one width "
f"the floor exists to keep it out of: {at_floor}"
)

resized(page, 1416 + bar, 800)
roomy = page.evaluate(reading)
assert [(s["float"], s["position"]) for s in roomy["sidebars"]] == [
("left", "sticky"),
Expand Down
Loading