Canonicalize document-side Buttons and Page map - #233
Conversation
Render compact clusters and the complete Page map from canonical Button records, preserve control identity across live updates, and keep active targets expanded. Turn the gallery into a sequence of explanations beside their live controls and cover keyboard, overflow, projection, and performance regressions.
leaf-agent
left a comment
There was a problem hiding this comment.
Read the diff and ran the four touched nightly files here. Two findings, both inline.
The load-bearing one is that uv run pytest tests never ran the new margin tests: tests/conftest.py deselects nightly-marked items unless --run-nightly or an explicit selection is given, and tests/test_render_margin.py carries pytestmark = pytest.mark.nightly. test_unchanged_margin_refresh_cost_is_bounded_by_refresh_count fails on this head, so it lands red on the nightly suite once main moves.
Separately, raiseMarginSurface now sets marginComment.hidden = true and marginSuggest.hidden = true unconditionally and nothing ever un-hides them, so the two Buttons buildReactBar builds and appends to marginSurface are inert — as is setReact's reactSurface === marginSurface && !marginSuggest.hidden ? marginSuggest : … branch. Per the Stage section's "delete and regenerate stale state", dropping them from the margin surface reads better than hiding them on every raise.
Other failures I saw, not attributed to this PR
test_holding_a_key_repeats_only_where_the_press_is_a_walk, test_an_external_link_says_and_opens_where_it_goes, and test_an_addressed_link_leaves_the_reader_at_its_destination fail here on a missing …/.fixture-versions/v1.html, and test_a_widgets_label_takes_a_comment_inside_the_control_it_labels fails with "a drag across the tab's name selected nothing". All four reproduce standalone with -n0, none touch code this PR changes. I haven't confirmed whether they're pre-existing — the recent ci runs on main are all cancelled by concurrency, so there's no green baseline to compare against.
leaf-agent
left a comment
There was a problem hiding this comment.
Both prior findings are closed on this head: test_unchanged_margin_refresh_cost_is_bounded_by_refresh_count passes now, and tests/test_render_margin.py + tests/test_render_reactions.py (108) and tests/test_render_semantic_selection.py (24) are green here. The capacity gate matches what paintMarker actually hides — subtracting a fitting for primary || markerFace(entry).kinds.length is true exactly when a primary or a visible marker stands outside the group — and the marginComment/marginSuggest removal leaves no live reference in the runtime, the theme, or the corpus.
One thing, inline: the comment justifying the coalescing has the listener order backwards. The suppression itself is right, just for a different reason.
This makes the document-side Button a canonical semantic record instead of a collection of visually similar controls. The compact rail, expanded cluster, overflow route, and complete Page map now project the same key, face, label, behavior, tone, role, and lifecycle state, while the original contributor remains the only activation owner.
The interaction model follows from that shared record: one peer appears directly instead of hiding behind an ellipsis; focused or engaged targets keep all available peers exposed; larger clusters show as many circles as fit and route the remainder into the searchable Page map.
g mnow opens that complete map rather than a nine-item address prefix. Reconciliation retains live Button and map-row identity so heartbeats cannot cancel a pointer press, and margin geometry is read in batches instead of repeatedly forcing layout.The developer feature gallery now explains each Button promise beside the live control that demonstrates it. Its browser coverage exercises actions, disclosures, threads, reactions, dense overflow, keyboard movement, narrow layouts, state transitions, tooltip collision, and Page-map search.
Tests:
uv run pytest tests(811 passed);uv run pre-commit run --all-files.