Making `.sources` a scroll container turned its `padding-left` from spacing
into a correctness constraint. A scroll container clips inline-start overflow
with no way to scroll it back, and `::marker` is right-aligned to the content
edge, so past nine sources the markers rendered as `0.` and `1.` where `10.`
and `11.` belonged.
`searchMaxResults` is an unbounded positive integer, so size the padding in
`em` against the list's own font to hold a three-digit marker. The browser e2e
measures a `999. ` marker in that inherited font and requires the computed
padding to be at least that wide, pinning the room against the widest marker
rather than one fixture's source count.
PR #1003 re-recorded this golden from a base that predated PR #1150, which
disables the branch control on any message that is not a completed turn's
tail. Both landed on master, whose CI skips the snapshot lane, so the
conflict only surfaces on PRs. The first message's control is now disabled
with its explanatory text.
jsdom resolves no CSS Modules layout and reports scrollHeight === clientHeight
for every element, so the unit test cannot witness the scroll the .sources
max-height produces. Drive it in the real browser instead.
The web-search-round search double now returns 12 provider results with a
title, snippet, and date each. That pins the seam's cap end to end for the
first time in a real composition — searchMaxResults keeps 8, the model-visible
text carries the kept titles and none of the dropped URLs, meta.truncated is
true — and 8 such rows overflow the 320px container. A case after the aria
golden expands the row and asserts 8 <li>, no button, the truncation
indicator, and computed max-height/overflow-y with scrollHeight > clientHeight.
The recorded stream and the aria golden are unchanged: replay is a positional
cursor over the fixture's chunks and the search double is a separate local
endpoint, while the card is collapsed at capture time.
Skipping the draft-following scan whenever ANY pane happened to list the
directory was the cheaper rule and the wrong one: erasing a segment left the
level being typed on the LEFT, with its own child pane still standing to its
right, so the two panes stopped reading as "where I am, and where I came
from".
The pane arity is now the invariant the editor maintains — the last pane lists
the level the path names, its parent sits beside it, and only a display root
lists alone. Only that last pane's own tail costs no scan; every other
directory part re-lands.
The draft-following scan replaced the panes with one wide level, so typing a
path collapsed the dialog's Miller view — the thing the dialog is. It now
lands through the same selection-anchored landing every navigation uses:
target and parent legs as one frame, the target re-selected in its parent
level, its children on the right. Typing a path moves the Miller view exactly
as a crumb jump does.
One landing shape, two callers: `land(path, {closeEditor, announce})` is what
`navigate` and the draft-following scan share. A submitted path closes the
editor and announces failures; the speculative scan keeps both to itself and
re-parks the focus its swap dropped.
A level a pane already lists still needs no scan at all — the filter alone
answers the draft — so erasing back into the parent's own path keeps both
panes and only moves the filter.