Skip to content

Preserve scroll position when toggling markdown Rendered/Raw views - #13967

Open
slash9494 wants to merge 4 commits into
warpdotdev:masterfrom
slash9494:preserve-markdown-toggle-scroll
Open

Preserve scroll position when toggling markdown Rendered/Raw views#13967
slash9494 wants to merge 4 commits into
warpdotdev:masterfrom
slash9494:preserve-markdown-toggle-scroll

Conversation

@slash9494

@slash9494 slash9494 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Description

Toggling a markdown file between Rendered and Raw view modes always reset the view to the top of the document, because the toggle replaces the entire pane (FileNotebookViewCodeView) and the new pane starts with a fresh scroll state.

This PR preserves the reading position across the toggle:

  • The outgoing pane's vertical scroll is captured as a fraction of its scrollable range (scroll_top / max_scroll, clamped 0..=1).
  • The fraction rides on PaneEvent::ReplaceWithCodePane / ReplaceWithFilePane (as Option<OrderedFloat<f32>> so PaneEvent keeps Eq).
  • The incoming pane re-applies it after its content is laid out:
    • Raw side: a new ScrollPosition::Fraction variant on the existing pending-scroll (ScrollTrigger) path.
    • Rendered side: a new LayoutAction::ScrollToFraction, stored as a pending fraction in RenderState and applied in apply_element_update, gated on the viewport having been laid out (viewport.height() > 0) and the reset content's buffer version. Applying immediately would read a stale (~0) content height on a freshly created pane and clamp to the top.

A fraction is used instead of a char-offset snapshot (ScrollPositionSnapshot) because the two modes render different documents, so a char offset cannot map between them. The landing position is therefore proportional rather than line-exact; documents whose blocks resize late (e.g. large tables) may land slightly off, which seems like an acceptable trade-off for the common case.

All pre-existing callers of the pane constructors pass None and keep the current scroll-to-top behavior.

Linked Issue

Closes #14062

#14062 is the focused, single-scope issue for scroll preservation (following the repo's Markdown viewer: … issue convention). It was split out of #7897, which bundles two separate asks — scroll preservation (this PR) and find/replace in the Markdown viewer (still tracked on #7897).

Testing

  • Added a unit test for the fraction ⇄ scroll-top math (crates/editor/src/render/model/viewport_tests.rs::test_scroll_fraction): midpoint mapping, out-of-range clamping at both ends, and content-fits-in-viewport (no scrollable range → 0).

  • cargo nextest-compatible: full warp_editor suite passes locally; cargo check -p warp clean; ./script/format --check clean.

  • No UI integration test: the integration framework cannot simulate clicking the Rendered/Raw toggle (noted in crates/integration/src/test/notebooks.rs).

  • I have manually tested my changes locally with ./script/run

Manually verified end-to-end in a local build, both directions:

  • Rendered → Raw: scrolled to ~40% in Rendered, toggled, Raw opens at the equivalent position.
  • Raw → Rendered: scrolled to ~55% in Raw, toggled, Rendered opens at the equivalent position.
  • Verified via debug logging that the applied scroll_top matches fraction × (content_height − viewport_height) exactly in both directions, and that panes opened without a toggle (file tree, session restore) still start at the top.

Screenshots / Videos

Before (master build): Raw scrolled to the bottom of the document (§37–40 + table) → toggling to Rendered resets to the top:

Raw, scrolled to bottom → Rendered: reset to top (bug)
Raw scrolled to bottom Rendered reset to top

After (this branch): the same action lands at the equivalent position (§40 + table still in view):

Raw, scrolled to bottom → Rendered: position preserved
Raw scrolled to bottom Rendered position preserved

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

CHANGELOG-IMPROVEMENT: The markdown viewer now preserves your scroll position when switching between Rendered and Raw view modes.

@cla-bot cla-bot Bot added the cla-signed label Jul 19, 2026
@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label Jul 19, 2026
@slash9494
slash9494 force-pushed the preserve-markdown-toggle-scroll branch 2 times, most recently from 14d87e2 to 2ac5964 Compare July 20, 2026 02:34
Toggling a markdown file between Rendered and Raw replaces the entire
pane (FileNotebookView <-> CodeView), so the new pane always started at
the top of the document. Capture the outgoing pane's vertical scroll as
a fraction of its scrollable range, carry it on the pane-replacement
events, and re-apply it after the new pane's content is laid out.

A fraction is used instead of a char-offset snapshot because the two
modes render different documents. Application is deferred through the
render model's element-update cycle (version- and viewport-gated),
since a freshly created pane has no laid-out content yet and applying
immediately would clamp the scroll to the top.

Fixes the scroll-preservation half of warpdotdev#7897; search/replace is out of
scope.
@slash9494
slash9494 force-pushed the preserve-markdown-toggle-scroll branch from 2ac5964 to 2db7358 Compare July 20, 2026 04:19
@slash9494
slash9494 marked this pull request as ready for review July 20, 2026 04:35
@oz-for-oss

oz-for-oss Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

@slash9494

Every PR must be linked to a same-repo issue before Oz can review it.

This PR is linked to #14062, but no linked issue is marked ready-to-implement yet. Only repository maintainers apply that label, so please wait for a maintainer to mark the issue. Once it is marked, push a new commit or comment /oz-review to re-trigger review.

See the contribution guidelines for the full readiness model.

Powered by Oz

oz-for-oss[bot]
oz-for-oss Bot previously requested changes Jul 20, 2026

@oz-for-oss oz-for-oss Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@slash9494

Every PR must be linked to a same-repo issue before Oz can review it.

Next step: open or find a same-repo issue describing this change, then link it to this PR by adding Closes #123 to the PR description (or using the "Development" sidebar on GitHub). A maintainer will mark the issue ready-to-implement when it is ready. Once it is marked, comment /oz-review to re-trigger review.

See the contribution guidelines for the full readiness model.

Powered by Oz

@slash9494

Copy link
Copy Markdown
Contributor Author

/oz-review

oz-for-oss[bot]
oz-for-oss Bot previously requested changes Jul 21, 2026

@oz-for-oss oz-for-oss Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@slash9494

Every PR must be linked to a same-repo issue before Oz can review it.

This PR is linked to #14062, but no linked issue is marked ready-to-implement yet. Only repository maintainers apply that label, so please wait for a maintainer to mark the issue. Once it is marked, push a new commit or comment /oz-review to re-trigger review.

See the contribution guidelines for the full readiness model.

Powered by Oz

@slash9494

Copy link
Copy Markdown
Contributor Author

/oz-review

@oz-for-oss

oz-for-oss Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

@slash9494

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I reviewed this pull request and requested human review from: @bnavetta.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss
oz-for-oss Bot dismissed stale reviews from themself July 22, 2026 01:50

Oz no longer requests changes for this pull request after the latest automated review.

@oz-for-oss oz-for-oss Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overview

This PR preserves markdown viewer scroll position when switching between Rendered and Raw modes by carrying a scroll fraction through pane replacement and applying it after the incoming editor has laid out. The implementation updates both code-editor and rendered-notebook paths, includes focused viewport math coverage, and the PR description includes before/after screenshots for the user-facing behavior.

Concerns

No blocking correctness, security, or spec-alignment concerns found. No approved or repository spec context was available for this PR.

Verdict

Found: 0 critical, 0 important, 0 suggestions

Approve

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss
oz-for-oss Bot requested a review from bnavetta July 22, 2026 01:50
@slash9494

Copy link
Copy Markdown
Contributor Author

Hi @bnavetta — gentle nudge on this one. Oz approved it (0 concerns) and requested your review on 07-22, and the linked issue #14062 is marked ready-to-implement, so I believe it's ready whenever you have a moment. Happy to rebase or make any changes if something's needed — just let me know. Thanks!

@slash9494

Copy link
Copy Markdown
Contributor Author

@oss-maintainers — flagging this one per the CONTRIBUTING escalation note.

Oz approved this PR on 07-22 with 0 concerns and requested human review from
@bnavetta, and the linked issue #14062 is marked ready-to-implement. It's been
about two weeks without a human review, so I wanted to raise it here rather than
keep pinging.

I've just merged the latest master in — the branch is up to date with no
conflicts, cargo check passes for both warp_editor and warp, and the
viewport tests pass.

Could someone on the team take a look, or route it to another reviewer if
@bnavetta is stretched thin right now? Happy to rebase or make any changes
needed. Thanks!

@bnavetta bnavetta left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry for the delay! One small question, but the code otherwise looks good

Comment thread app/src/code/view.rs Outdated
}

/// The current vertical scroll fraction of the active tab's editor, in `0..=1`.
#[cfg(feature = "local_fs")]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does this need to be gated on local_fs? The functionality itself isn’t FS-specific; if this is dead code without a filesystem then we can add a conditional expect(dead_code) attribute

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — you're right that it isn't FS-specific.

Digging in, code/view.rs turned out to be a special case: code/mod.rs path-swaps it to wasm.rs for wasm, so local_fs is always enabled wherever this file compiles and the gate never actually applied. I dropped it rather than swapping the attribute.

notebooks/file/mod.rs does compile for wasm, so its two helpers use the conditional expect(dead_code) you suggested, matching terminal/recorder.rs.

While there I also narrowed set_pending_scroll_fraction to pub(crate) since pane_group is the only caller — that turned out to surface a real dead_code error on wasm that the pub had been masking, now covered by the same attribute.

Verified with cargo clippy -p warp -- -D warnings on both the native target and wasm32-unknown-unknown, plus the viewport tests. Latest master is merged in as well.

…-toggle-scroll

# Conflicts:
#	app/src/notebooks/file/mod.rs
…hem out

These helpers only read editor scroll state, so `local_fs` was standing in for
"this build has no caller" rather than for an actual filesystem dependency.

`code/view.rs` is path-swapped to `wasm.rs` for wasm, so `local_fs` is always
enabled wherever it compiles and the gate never applied — drop it. The notebook
view does compile for wasm, so mark its helpers with a conditional
`expect(dead_code)`, matching `terminal/recorder.rs`.

Also narrow `set_pending_scroll_fraction` to `pub(crate)`; it is only called
from `pane_group`.

Verified with `cargo clippy -p warp -- -D warnings` on both the native target
and `wasm32-unknown-unknown`, plus the viewport tests.
@slash9494
slash9494 requested a review from bnavetta August 19, 2026 08:49
@slash9494
slash9494 force-pushed the preserve-markdown-toggle-scroll branch from 22d5b72 to 318299f Compare August 19, 2026 11:17
@bnavetta
bnavetta enabled auto-merge (squash) August 19, 2026 11:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Markdown viewer: preserve scroll position when toggling Rendered/Raw views

2 participants