coder / coder/ghostty-web

getViewport() returns corrupted data when viewport spans multiple pages

Open
#139 2 comments 0 reactions 1 assignee Claimed by @ThomasK33 View on GitHub
accepted bug triage:done
Dominant language
TypeScript
Stars
2.8k
Forks
172
PR merge metrics
No merged PRs in 30d

Description

### Description

`renderStateGetViewport` in the WASM build resolves the viewport top-left position independently for each row using `pages.pin(.active)`. When the viewport spans multiple internal pages, this per-row resolution can produce inconsistent results, leading to corrupted viewport data.

Additionally, `scrollbackLimit` is passed as a line count but `Terminal.init()` interprets `max_scrollback` as bytes, causing the scrollback buffer to be much smaller than intended and making the page-spanning condition occur more frequently.

### Symptoms

- `getScrollbackLength()` drops unexpectedly (e.g., 498 → 269) after repeated writes
- `getViewport()` returns rows with content from different terminal lines merged together
- Both `getViewport()` and `getLine()` return the same wrong data
- Corruption frequency depends on column width:
- cols=80: OK
- cols=120: CORRUPT
- cols=130: CORRUPT
- cols=140: OK
- cols=160: scrollback drops but viewport may appear OK (merge lands on empty rows)

### Root cause

The native Ghostty renderer reads cell data from cached row pins in `RenderState.row_data` (built during `update()`), which guarantees consistent page resolution across all viewport rows. The WASM `renderStateGetViewport` instead calls `pages.pin(.active)` per-row, which can resolve to different pages inconsistently when the viewport straddles a page boundary.

### Reproduction

1. Create a terminal at cols=130, rows=39, scrollback=10000
2. Repeatedly write ~68 lines of escape-heavy output (SGR 256-color, truecolor, attributes)
3. After ~8 repetitions, call `getScrollbackLength()` and `getViewport()`
4. Scrollback length will drop and/or viewport rows will contain merged content

### Expected behavior

`getViewport()` should return consistent, correct row data regardless of internal page layout. `scrollbackLimit` should be interpreted consistently between the JS config and WASM init.

### Fix

Draft PR with fix and regression tests: #133

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.