Skip to content

0.4.0: collapse across a wrapping container, in all seven implementations - #84

Merged
ralyodio merged 4 commits into
mainfrom
collapse-lockstep-and-0.4.0
Sep 9, 2026
Merged

0.4.0: collapse across a wrapping container, in all seven implementations#84
ralyodio merged 4 commits into
mainfrom
collapse-lockstep-and-0.4.0

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #83, which landed the first half. This is the rest.

What #83 left unfinished

#83 closed the gaps so every seam could touch. But the panels then touched while still drawing two borders, because a seam merges only where two bordered siblings meet — and the only way to put a stack of panels beside one tall panel is to wrap the stack in a column, which is not itself bordered. So the seam down the middle of every screen was the one seam that could never merge, and a grid never merged at all. The dashboard looked right only because its panels happen to be direct children of the row.

before                                after
╭─ Services ─╮╭─ Kernel ─────╮        ╭─ Services ─┬─ Kernel ─────╮
│ ...        ││ ...          │        │ ...        │ ...          │
╰────────────╯╰──────────────╯        ╰────────────┴──────────────╯

Containers now take a bordered flag saying their own edges are panel borders, and a grid of panels merges its tracks. It is declared rather than inferred: the children are built only once the layout has been solved, and the seam has to be known before that, so there is nothing to inspect at the moment the question is asked.

And panels with a background

box filled its whole rect before drawing its border, which erased the neighbour's border out of the shared column — so the merge found a blank cell and overwrote it, leaving a corner where a junction belonged. That is why the themes grid showed where a was due. Filling under collapsing now stops at the border ring, which the border then paints in the same background.

All seven implementations

TypeScript, Rust, Go, Python, Zig and C/C++, plus the Ruby/PHP/Perl bindings that ride on the C++ demo.

  • Rust and Zig widened their grid gap arithmetic to signed, since a merged track seam is minus one.
  • In C++ the flag has to be copied into the panel body: those lambdas are stored and run by flush() after the enclosing function returns, so a captured-by-reference gap was a dangling local — which is exactly how the dashboard's sub-panels kept their gap while everything around them merged.

Uncollapsed rendering is untouched

The regenerated demo-parity.json proves it: of 240 cases, all 120 open frames are byte-identical, and 90 of the 120 collapsed ones changed.

0.4.0

The demo has to move with the library rather than after it: it calls worldMap, which does not exist in the published 0.3.0, so a demo published against ^0.3.0 would resolve the old library and crash on the world screen.

Checks

  • TypeScript: 356 tests, under both bun test and node --test
  • Go: all suites; Python: 36 tests; Rust: 17 demo-parity; Zig: 17; C++: 9/9 including its own 240-case parity
  • Ruby and Perl bindings: each reports 240 exact reference frames in both border modes (PHP's extension needs phpize, built in CI)
  • Rendering all eleven screens both ways and counting seams that stayed open gives zero on every one
  • bun install --frozen-lockfile clean; node apps/demo/bin/hqtui-demo.mjs --version prints hqtui-demo 0.4.0

🤖 Generated with Claude Code

https://claude.ai/code/session_01VhrgsHr1C5BYxXDpN1mahf

ralyodio and others added 4 commits September 9, 2026 16:58
Closing the gaps was necessary and not sufficient. Panels then touched but
still drew two borders, because a seam merges only where two *bordered
siblings* meet -- and the only way to put a stack of panels beside one tall
panel is to wrap the stack in a column, which is not itself bordered. So the
seam down the middle of every screen was the one seam that could never merge,
and a grid never merged at all. The dashboard looked right only because its
panels happen to be direct children of the row.

Containers now take a `bordered` flag that says their own edges are panel
borders, and a grid of panels merges its tracks. It is declared rather than
inferred: the children are built only once the layout has been solved, and the
seam has to be known before that, so there is nothing to inspect at the moment
the question is asked.

The other half was a panel with a background. `box` filled the whole rect
before drawing its border, which erased the neighbour's border out of the
shared column -- so the merge found a blank cell and overwrote it, leaving a
corner where a junction belonged. Filling under collapsing now stops at the
border ring, which the border then paints in the same background.

Uncollapsed rendering is untouched, and the regenerated fixture proves it:
of 240 cases, all 120 open frames are byte-identical and 90 of the 120
collapsed ones changed.

Rust and Zig widened their grid gap arithmetic to signed, since a merged track
seam is minus one. In C++ the flag has to be copied into the panel body: those
lambdas are stored and run by flush() after the enclosing function returns, so
a captured-by-reference `gap` was a dangling local, which is exactly how the
dashboard's sub-panels kept their gap while everything around them merged.

Verified: TypeScript 356 tests; Go, Python 36, Rust 17 demo-parity, Zig 17,
C++ 9/9 including its own 240-case parity; Ruby and Perl bindings each report
240 exact reference frames in both border modes. Rendering all eleven screens
both ways and counting seams that stayed open gives zero on every one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VhrgsHr1C5BYxXDpN1mahf
Bumps the library, the demo, the COBOL adapter and every workspace range that
points at them, plus the two hand-written strings no build step touches — the
CLI's VERSION and the demo's --version — and the site's badge.

The demo has to move with the library rather than after it: it now calls
`worldMap`, which does not exist in the published 0.3.0, so a demo published
against `^0.3.0` would resolve the old library and crash on the world screen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VhrgsHr1C5BYxXDpN1mahf
The test ran green under `bun test`, which does not typecheck, and failed
`bun run typecheck` on all three platforms. `Color` is a `number` in hqtui, and
a "#202020" string only looked right.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VhrgsHr1C5BYxXDpN1mahf
`col` gained a `bordered` argument and two callers were missed: the read and
write graphs inside the dashboard's I/O Summary, which are graphs rather than
panels and so pass false.

They compile only under the `test-demo` target. `zig build test` alone leaves
the demo out, which is why this reached CI: the command to run is
`zig build test test-demo`, the one mise's `test:zig` task uses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VhrgsHr1C5BYxXDpN1mahf
@ralyodio
ralyodio merged commit 4155d20 into main Sep 9, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant