Skip to content

Commit df5102c

Browse files
JohnMcLearclaude
andcommitted
fix: use outerDoc instead of outerWin.document for viewport height in PageDown/Up
outerWin is an HTMLIFrameElement (returned by getElementsByName), not a Window object, so it has no .document property. The existing getInnerHeight() helper already uses outerDoc.documentElement.clientHeight correctly; align the PageDown/PageUp handler with that pattern. Adds a Playwright regression test that verifies PageDown scrolls the viewport when the pad contains long wrapping lines. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e42f3d1 commit df5102c

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

src/static/js/ace2_inner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2888,7 +2888,7 @@ function Ace2Inner(editorInfo, cssManagers) {
28882888
// Scroll by actual viewport height in pixels, not by line count.
28892889
// This fixes the case where very long wrapped lines consume the
28902890
// entire viewport, making line-count-based scrolling useless.
2891-
const viewportHeight = outerWin.document.documentElement.clientHeight;
2891+
const viewportHeight = outerDoc.documentElement.clientHeight;
28922892
// Keep a small overlap so the user doesn't lose context
28932893
const scrollAmount = viewportHeight - 40;
28942894
const currentScrollY = scroll.getScrollY();

src/tests/frontend-new/specs/page_up_down.spec.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,39 @@ test.describe('Page Up / Page Down', function () {
8484
expect(selection).toBeLessThan(50);
8585
});
8686

87+
// Regression test: long wrapping lines should still allow PageDown to scroll
88+
// the viewport. Before the fix, outerWin.document was accessed on an iframe
89+
// element (which has no .document property), causing the handler to break.
90+
test('PageDown scrolls viewport when pad has long wrapping lines', async function ({page}) {
91+
const padBody = await getPadBody(page);
92+
await clearPadContent(page);
93+
94+
// Create 3 very long lines that will wrap many times in the viewport
95+
const longText = 'This is a very long line that should wrap multiple times in the editor viewport to ensure that page down scrolling works correctly even when lines are longer than the visible area. '.repeat(20);
96+
for (let i = 0; i < 3; i++) {
97+
await writeToPad(page, longText);
98+
if (i < 2) await page.keyboard.press('Enter');
99+
}
100+
101+
// Move caret to the very top
102+
await page.keyboard.down('Control');
103+
await page.keyboard.press('Home');
104+
await page.keyboard.up('Control');
105+
await page.waitForTimeout(200);
106+
107+
// Record the scroll position before PageDown
108+
const outerFrame = page.frame('ace_outer')!;
109+
const scrollBefore = await outerFrame.evaluate(() => document.documentElement.scrollTop);
110+
111+
// Press PageDown
112+
await page.keyboard.press('PageDown');
113+
await page.waitForTimeout(1000);
114+
115+
// The viewport should have scrolled
116+
const scrollAfter = await outerFrame.evaluate(() => document.documentElement.scrollTop);
117+
expect(scrollAfter).toBeGreaterThan(scrollBefore);
118+
});
119+
87120
test('PageDown then PageUp returns to approximately same position', async function ({page}) {
88121
const padBody = await getPadBody(page);
89122
await clearPadContent(page);

0 commit comments

Comments
 (0)