Make the suggestion rail's overhang reachable, and keep the text in view
The margin rail hangs off an absolutely-positioned column, so its cards add no layout height. On her live document that meant four 173px cards anchored inside 126px of text: a 714px stack over a page whose scrollHeight equalled its clientHeight. The lower cards weren't far from their sentence, they were off-screen with nothing to scroll. The rail now reports how far its resolved stack reaches and the wrapper takes that as a minimum height, so the space those cards occupy is real, scrollable page. minHeight never shrinks the column, so a rail that fits beside its text is unaffected. Scrolling into that space would have carried every sentence off the top, so the prose is pinned while the stack overhangs it. The offset is min(0, port - content): prose shorter than the viewport pins at the top, taller prose pins by its bottom edge, keeping the last lines visible — those are the ones the overhanging cards flag. The prose box has to stay at its natural height. Keeping the old h-full made it measure the wrapper this change had just grown, reporting the cards' height as the text's own, so the pin could never trip. Verified in a browser at the review's 1517x810, driven offline by the rule pack: 8 cards over 95px of prose gained 675px of scroll where there was none, the last card lands fully in view with the text still on screen, tall prose pins bottom-anchored without disturbing ordinary scrolling, and hover-linking still glows the right span. Claude-Session: https://claude.ai/code/session_016y6gyuHkQXPiEuW8RGQyua
This commit is contained in:
@@ -302,6 +302,74 @@ takes over when the stack exceeds the viewport.
|
||||
**Acceptance:** with 10+ suggestions, the flagged text stays visible while
|
||||
scrolling the card list; hover-linking still highlights the right span.
|
||||
|
||||
### 4 — DONE (fifth session). The cards weren't distant; they were unreachable.
|
||||
|
||||
Measured on the live build before touching anything, and the item understates
|
||||
its own bug. Her open document: **four cards, 173 px each, all anchored inside
|
||||
126 px of text** — the stack resolved to tops 4 / 189 / 374 / 559, so ~714 px
|
||||
of cards beside four lines of prose. And because `.petal-rail` is
|
||||
`position: absolute`, none of that counts as layout height: the page reported
|
||||
`scrollHeight === clientHeight`, **no scroll container at all**. On the review's
|
||||
810 px viewport the lower cards weren't merely severed from their sentence,
|
||||
they were off-screen with no way to scroll to them. That is the real defect,
|
||||
and it is why the item read as a scrolling problem.
|
||||
|
||||
**Implemented:**
|
||||
|
||||
- `SuggestionRail.tsx` — the stack reports how far it reaches (`onExtent`),
|
||||
computed in the same pass that resolves the collision-avoided tops.
|
||||
- `EditorCore.tsx` — the wrapper takes `minHeight: railExtent + 24`, so the
|
||||
space the cards occupy becomes real, scrollable page. `minHeight` never
|
||||
shrinks the column, so a rail that fits beside its text changes nothing.
|
||||
- The prose moved into its own box, pinned with `position: sticky` while the
|
||||
stack overhangs it, so scrolling down to reach the lower cards no longer
|
||||
carries every sentence off the top. The offset is `min(0, port − content)`:
|
||||
prose shorter than the viewport pins at the top; **taller prose pins by its
|
||||
bottom edge**, so the last lines — the ones the overhanging cards flag —
|
||||
stay visible rather than the first.
|
||||
- The extent is cleared when the last card goes, or the window narrows past
|
||||
the rail's threshold; otherwise the column keeps the height of a stack that
|
||||
no longer exists.
|
||||
|
||||
**A trap worth recording.** That prose box must be left at its natural height.
|
||||
The first version kept the existing `h-full`, so it measured the wrapper — which
|
||||
this change had just grown to the stack's height — and reported the cards'
|
||||
height back as the text's own. `railExtent > contentH` was then never true and
|
||||
the pin could never trip. It typechecked, looked right, and did nothing; only
|
||||
measuring the running page caught it (`proseHeight: 1424` for a two-line
|
||||
document).
|
||||
|
||||
**Verified in a real browser at the review's own 1517×810**, driving the local
|
||||
build with the rule pack from item 3b — which needs no model, so eight cards
|
||||
appear offline in one paragraph. All three branches exercised:
|
||||
|
||||
- *Overhang, short prose* — 8 cards, stack 1400 px, prose 95 px. Page gained
|
||||
675 px of scroll where it previously had none; scrolled to the end, the last
|
||||
card sits fully in view (770–926) **and the prose is still on screen** (80–175).
|
||||
- *Overhang, tall prose* — port 225 px, prose 347 px → `top: −146px`. Ordinary
|
||||
scrolling is untouched (at `scrollTop` 200 the text moves normally with the
|
||||
page); only at the overhang does it pin, bottom-anchored, last lines visible.
|
||||
- *No overhang* — the port stays unscrollable and nothing moves.
|
||||
|
||||
Hover-linking re-checked on the last card, the one this fix made reachable at
|
||||
all: it glows the right span ("It make"), the span is on screen, the card lifts.
|
||||
|
||||
**Known limit, not fixed.** The overlays anchored in wrapper coordinates (gloss
|
||||
tip, selection bubble, word/misspell cards, confetti) rely on the invariant
|
||||
noted at `recomputeRail` — "stable under scroll since text and wrapper scroll
|
||||
together" — which the pin breaks. They are still placed correctly when opened,
|
||||
because their coordinates come from live rects; they drift only if she scrolls
|
||||
*while one is open* *and* the column is pinned, i.e. inside the overhang. Left
|
||||
alone rather than papered over; if it ever bites, the fix is to close or
|
||||
re-anchor them on scroll.
|
||||
|
||||
**Deliberately not done:** no compaction of the cards. Making crowded cards
|
||||
drop to a one-line form is the obvious way to shorten the stack, and it is
|
||||
wrong here — the explanation *is* the teaching, and hiding it from an ESL
|
||||
writer to save vertical space trades the product's purpose for tidiness. Ten
|
||||
cards cannot sit beside four lines of text; the answer is to make the overhang
|
||||
navigable, not to shrink what each card says.
|
||||
|
||||
## 5. Mixed-language spans: offer translation, don't ignore
|
||||
|
||||
**Status (follow-up session): premise partly wrong — re-scope before
|
||||
@@ -421,6 +489,12 @@ incremental-surfacing half is now cheap — the chunking it was waiting on
|
||||
exists — but it needs streaming, which the current `/check` shape doesn't
|
||||
do.)*
|
||||
|
||||
*(Fifth session: item 4 done — see the subsection under it. Still nothing
|
||||
deployed: `main` remains at `ba06d90`, and 3b → 2 → 4 are now three stacked
|
||||
topic branches. **Merging and deploying that stack is the obvious next move**
|
||||
— three sessions of work she hasn't seen. Untouched: 6, 7, 8, item 3's
|
||||
incremental half, and item 5's re-scoped Translate card type.)*
|
||||
|
||||
**Suggested next:** item 3b, the instant local rules layer — but it is
|
||||
**largely already built, in `main`**, and the item as written doesn't know
|
||||
that. Before writing any rules engine, read:
|
||||
|
||||
Reference in New Issue
Block a user