Skip to content

fix(desktop/transcript): quiescence-based reveal + retired hydration pin, reserved image box (#331)#350

Merged
physercoe merged 1 commit into
physercoe:mainfrom
agentfleets:desktop/331-settle-reveal
Jul 20, 2026
Merged

fix(desktop/transcript): quiescence-based reveal + retired hydration pin, reserved image box (#331)#350
physercoe merged 1 commit into
physercoe:mainfrom
agentfleets:desktop/331-settle-reveal

Conversation

@agentfleets

Copy link
Copy Markdown
Collaborator

Closes #331

What

Replaces the time-capped hide-until-settled reveal (6b461bb) with a quiescence-based reveal plus a retired hydration pin, and reserves the attachment-image box so blob swaps stop growing rows mid-hydration.

  • desktop/src/surfaces/AgentTranscript.tsx
    • Reveal is now driven by quiescence: every totalListHeightChanged re-arms a 150ms quiet timer, so the feed stays visibility:hidden for the whole hydration storm on heavy transcripts instead of popping in mid-storm.
    • The flat 500ms hard cap becomes an 1800ms backstop — it only bounds the hidden window for pathological cases; the quiet timer is the normal reveal path.
    • A settledRef retires the re-pin loop at reveal: post-reveal, pinBottom neither scrolls nor re-arms, so late height changes (font swap, async image pop) no longer cause visible jumps. Bottom-following after reveal remains covered by the existing followOutput + atBottomStateChange contract, unchanged.
  • desktop/src/styles/partials/05-transcript-boards.css
    • .md-img-loading now reserves ≈ the final image box (width:100%; aspect-ratio:16/10; max-height:360px) and .md img gains the matching max-height:360px, so the placeholder → blob swap is a geometric near-no-op.

Why

Per the diagnosis posted on the issue (comment thread), the shipped fix fails on heavy transcripts for three reasons: (1) the 500ms cap reveals unconditionally mid-hydration-storm while the 80ms debounce can only reveal early, never delay; (2) the re-pin loop never retires, so every late post-reveal height change is a visible jump; (3) AttachmentImage swaps a 120×80 placeholder for a full-width height:auto image with no reserved dimensions, growing rows by hundreds of px up to seconds later. This PR implements items A+B+C from that diagnosis.

Item D (document.fonts.ready gating) is deliberately skipped: a font swap changes rendered-row heights, which already fires totalListHeightChanged and pushes the quiet timer out — the quiescence window covers it implicitly, so a separate fonts gate would be redundant machinery.

How verified

  • npm ci && npm run build (tsc --noEmit + vite build) — clean.
  • scripts/lint-desktop-tokens.sh — clean (no new hex / primitive-var / phantom tokens).
  • git diff origin/main --stat — only the two intended files; no version bumps or manifest changes.
  • CI on this PR is the only device-level verification available in this environment; the WebKit reveal behavior itself wants a manual once-over on a heavy transcript (the scenario in the issue report).

@physercoe physercoe left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — approve. Focused on what CI can't catch (timer/reveal correctness, CSS geometry).

  • Diff matches the description; no hidden changes.
  • Timers: reveal() is idempotent (sets settledRef synchronously, then state, then clears both timers); effect cleanup clears both settleTimer and backstopTimer on unmount/re-arm; post-reveal the settledRef guard stops any further re-pin. No leak, no double-reveal.
  • No stuck-hidden: the 1800ms backstop is armed unconditionally and calls reveal() regardless of height-change churn, so reveal is guaranteed even if totalListHeightChanged fires forever.
  • Bottom-following post-reveal: retiring the re-pin loop relies on followOutput + atBottomStateChange, which covers appended items; an existing bottom row growing after reveal is exactly what the reserved-image-box CSS mitigates — the two halves are complementary by design.
  • CSS: .md img { max-width:100%; max-height:360px; height:auto } scales tall images down preserving aspect ratio (no clip/squash, so object-fit correctly unneeded); .md-img-loading is an empty placeholder so its aspect-ratio:16/10 never distorts real content.

Non-blocking: guaranteed-reveal latency for the no-height-change path grew 500ms→1800ms (in practice the 150ms quiet path reveals fast); wide-short real images can still be shorter than the 16:10 placeholder (acknowledged as approximate). Fine.

@physercoe
physercoe merged commit 302a211 into physercoe:main Jul 20, 2026
6 checks passed
physercoe pushed a commit that referenced this pull request Jul 20, 2026
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.

feat(desktop): agent transcript should open at the last page with zero visible scrolling (ChatGPT/Claude-style open)

2 participants