diff --git a/e2e/pageScroll.spec.ts b/e2e/pageScroll.spec.ts new file mode 100644 index 000000000..ab2a00220 --- /dev/null +++ b/e2e/pageScroll.spec.ts @@ -0,0 +1,34 @@ +import { expect, openFromTree, test } from "./fixtures"; + +test("an open editor leaves nothing for the page itself to scroll", async ({ workbench: page }) => { + await openFromTree(page, "README.md"); + const page_ = await page.evaluate(() => { + window.scrollBy(0, 100); + const root = document.documentElement; + return { scrollY: window.scrollY, scrollHeight: root.scrollHeight, clientHeight: root.clientHeight }; + }); + expect(page_.scrollY).toBe(0); + expect(page_.scrollHeight).toBe(page_.clientHeight); +}); + +test("scrolling to where the editor already is keeps the page in place", async ({ workbench: page }) => { + const editor = await openFromTree(page, "README.md"); + await expect(editor.locator(".cm-content")).toBeVisible(); + const scrollY = await page.evaluate(async () => { + const content = document.querySelector(".cm-content") as HTMLElement & { + cmTile?: { view: never }; + cmView?: { view: never }; + }; + const view = (content.cmTile ?? content.cmView)!.view as { + constructor: { scrollIntoView(pos: number, options: { y: string }): unknown }; + dispatch(spec: { effects: unknown }): void; + requestMeasure(): void; + }; + for (let i = 0; i < 2; i++) { + view.dispatch({ effects: view.constructor.scrollIntoView(0, { y: "start" }) }); + await new Promise((resolve) => requestAnimationFrame(() => setTimeout(resolve))); + } + return window.scrollY; + }); + expect(scrollY).toBe(0); +}); diff --git a/src/editor/core/cm/theme.ts b/src/editor/core/cm/theme.ts index 553e57924..ac1a8da87 100644 --- a/src/editor/core/cm/theme.ts +++ b/src/editor/core/cm/theme.ts @@ -121,8 +121,9 @@ export const overviewStyles = { }; export const openideaTheme = EditorView.theme({ + // Not on "&": the tooltip container in `body` carries the theme classes too. + "&.cm-editor": { height: "100%" }, "&": { - height: "100%", backgroundColor: "var(--bg-editor, var(--bg-island, var(--bg-app)))", color: "var(--fg-editor, var(--fg))", "--editor-line-number": "var(--fg-line-number, var(--fg-gutter))", diff --git a/src/features/notebook/CellEditor.tsx b/src/features/notebook/CellEditor.tsx index bcf558b82..42a97fbd2 100644 --- a/src/features/notebook/CellEditor.tsx +++ b/src/features/notebook/CellEditor.tsx @@ -80,7 +80,7 @@ export function CellEditor({ language.of([]), readonly.current.of(EditorState.readOnly.of(session.getState().frozen)), EditorView.lineWrapping, - EditorView.theme({ "&": { height: "auto" }, ".cm-scroller": { maxHeight: "640px", overflow: "auto" } }), + EditorView.theme({ "&.cm-editor": { height: "auto" }, ".cm-scroller": { maxHeight: "640px", overflow: "auto" } }), EditorView.contentAttributes.of({ "aria-label": tx(type === "code" ? "Python cell source" : "Markdown cell source"), }), diff --git a/src/features/vcs/cm.ts b/src/features/vcs/cm.ts index 67f06a428..1c63aa9fc 100644 --- a/src/features/vcs/cm.ts +++ b/src/features/vcs/cm.ts @@ -313,8 +313,8 @@ export const viewerHighlight = HighlightStyle.define([ export const viewerTheme = EditorView.theme( { ...overviewStyles, + "&.cm-editor": { height: "100%" }, "&": { - height: "100%", backgroundColor: "var(--bg-app)", color: "var(--fg)", fontSize: "var(--openidea-editor-font-size, var(--fs-code))", diff --git a/src/styles/global.css b/src/styles/global.css index 66fef15e6..a3617e3cd 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -20,6 +20,11 @@ body, margin: 0; } +/* The page never scrolls: `clip` on the body leaves the viewport nothing to scroll into view. */ +html { + overflow: hidden; +} + body { background: var(--bg-frame); color: var(--fg); @@ -27,7 +32,7 @@ body { font-size: var(--fs-ui); line-height: 1.4; -webkit-font-smoothing: antialiased; - overflow: hidden; + overflow: clip; -webkit-user-select: none; user-select: none; cursor: default;