Collapse the borders the [c] key promised, in every port - #72
Merged
Conversation
The `c` key toggles collapse_borders in every port, and moved nothing in any of them -- the same defect fixed for TypeScript in #57. The library merges a seam only where two bordered siblings already touch, and every screen laid its panels out a column apart. No fixture noticed because all 120 demo-parity cases rendered with collapsing off. They now render both ways, 240 cases, which is what turns this from a thing you have to look for into a failing test. Rust first. Every screen threads State::panel_gap(), and the wide dashboard puts panels straight into their rows: a column is not a bordered child, so a row of them has no seam to merge however the gap is set. Rows that genuinely hold columns keep their gap of 1, exactly as TypeScript does -- closing those would butt two borders together and read as a bug rather than a collapse. The fixture walked the port screen by screen: dashboard, then graphics, then traffic, then components, each failing with the merged glyphs on one side and a gap on the other until it was threaded. Go, Python, Zig and C++ still fail these cases and are next. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
Both ports thread panel_gap through their screens and put the wide dashboard's panels straight into their rows, as Rust and TypeScript do. Getting there turned up something the fixtures had never been able to see. The Go components screen differed from TypeScript by a single braille dot, and a sweep of the gauge across 341 sizes -- with no collapsing involved at all -- found 14 more, every one at height 6. The cause is not in the demo. At sample 28 of an 84-step arc the angle is exactly 120 degrees, where cosine is exactly -0.5: V8 cos = -0.50000000000000022204 -> x = 16.499999999999993 -> 16 Go cos = -0.50000000000000011102 -> x = 16.5 exactly -> 17 The two libm implementations differ by one ULP and the result lands on a rounding tie. Neither is wrong. Rounding the raw coordinate lets that ULP decide a pixel, so the same widget at the same size renders differently in each language, and no amount of matching the code fixes it. BrailleCanvas now rounds with a tolerance far wider than an ULP and far narrower than anything geometric, in TypeScript and in all three ports so far. Only demo-parity.json moved: widgets.json, screen.json and the rest regenerate byte for byte, so the blast radius is one cell in one screen that was already inconsistent between languages. Zig and C++ still fail the collapsed cases and are next. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
The `c` key set a flag the layouts never read: collapsing merges a seam only where two bordered siblings already touch, so every panel row kept its gap of 1 and nothing moved. Thread the gap through the containers whose children are panels, and give the reference binary a --collapsed flag so the parity harness can cover both modes -- 240 frames now. Read the gap where the seam is declared rather than into a local. Panel bodies are closures the UI runs at flush, after the screen function has returned, so a `[&]` capture of a local int dangles: the nested columns in traffic, sessions, network, services and components read a dead stack slot and laid out with whatever it held. Also snap the braille pixel rounding, matching the other ports. Plot geometry runs through cos and sin, and libm implementations differ by an ULP at the exact angles where a coordinate lands on .5. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
Same two blockers as the other ports. The screens wrapped each panel in a column, so a row's children were containers rather than bordered siblings and there was no seam to merge; and every panel row hard-coded a gap of 1, which collapsing leaves alone. Rows now size their panels directly, through a size carried on the screen context, and read the gap from the model. The parity test replays the collapsed half of the fixtures too, so the inert `c` key could not have gone unnoticed a second time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
The three bindings replay the same demo fixtures through the C++ bridge, so the collapsed half of the corpus arrived with nowhere to say which border mode it wanted. Collapsing is a property of a scene rather than of one call, like its theme, so the ABI gains a setter -- additively, and it applies to custom trees as well as demo frames. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
The published catalog is generated, and the ±1e-9 snap moves one pixel in the gauge's dial at this size -- the same divergence the ports were disagreeing over. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ccollapsed nothing outside TypeScript, and it barely collapsed there. Twothings had to be true for a seam to close, and only one of them was:
so a panel wrapped in a column was invisible to it; and
So the flag flipped, the app-level setting changed, and the screen looked
identical. Fixed in all six demos — TypeScript, Rust, Go, Python, C++, Zig —
plus the Ruby/PHP/Perl bindings, which replay the same frames through the C++
bridge.
The fixtures now carry both modes
demo-parity.jsonwent from 120 cases to 240: ten screens × three themes ×four sizes × collapsed/open. Every port replays all of them cell for cell, so
an inert
ckey cannot go unnoticed again. That is really the point of thisbranch — the bug survived because nothing tested the other border mode.
Two bugs found on the way
A libm tie decides a pixel. At gauge sample 28/84 the angle is exactly 120°
and
cosis exactly −0.5. V8 returns a hair under it (-0.50000000000000022),Go a hair over (
-0.50000000000000011); one rounds to x=16, the other to 17. Asweep of 341 gauge sizes found 14 such divergences, all at height 6, none of
them involving collapsing at all.
BrailleCanvas.pixelnow snaps by ±1e-9 —far wider than an ULP, far narrower than anything geometric — in every
language. Only
demo-parity.jsonmoved;widgets.jsonandscreen.jsonregenerate byte for byte.
A dangling capture in the C++ demo. Panel bodies are closures the UI runs at
flush(), after the screen function has returned, so[&]capturing a localconst int gapread a dead stack slot. It surfaced as a one-row layout shift inthe uncollapsed traffic screen. The gap is now read where the seam is
declared.
Bindings ABI
Additive:
hqb_collapse(scene, enabled). Collapsing is a property of a scenelike its theme, not of one call, and it applies to custom trees too — so no
existing signature changed.
Verified
go test ./...greenunittest discovergreen🤖 Generated with Claude Code
https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy