diff --git a/Sources/SaplingEditor/HybridMarkdownEditor.swift b/Sources/SaplingEditor/HybridMarkdownEditor.swift index c5db35a..b18f9d8 100644 --- a/Sources/SaplingEditor/HybridMarkdownEditor.swift +++ b/Sources/SaplingEditor/HybridMarkdownEditor.swift @@ -689,6 +689,13 @@ private struct NativeMarkdownTextView: NSViewRepresentable { return false } + if anchor.visibleOrigin.y <= 0 { + return scrollVisibleOrigin( + NSPoint(x: anchor.visibleOrigin.x, y: 0), + in: textView + ) + } + return scrollVisibleOrigin( NSPoint( x: anchor.visibleOrigin.x, diff --git a/Tests/SaplingEditorTests/HybridMarkdownLiveEditorHarnessTests.swift b/Tests/SaplingEditorTests/HybridMarkdownLiveEditorHarnessTests.swift index 685e433..282f9a1 100644 --- a/Tests/SaplingEditorTests/HybridMarkdownLiveEditorHarnessTests.swift +++ b/Tests/SaplingEditorTests/HybridMarkdownLiveEditorHarnessTests.swift @@ -14,6 +14,19 @@ final class HybridMarkdownLiveEditorHarnessTests: XCTestCase { XCTAssertTrue(harness.headingMarkerIsHidden()) } + func testClickAtTopOfDocumentDoesNotScrollViewportDown() { + let source = (["# Heading", "Opening paragraph"] + (1...80).map { "Line \($0)" }) + .joined(separator: "\n") + let harness = HybridMarkdownLiveEditorHarness(source: source) + harness.simulateLaunchFirstResponder() + harness.scrollViewport(toY: 0) + + let paragraphLocation = (source as NSString).range(of: "Opening").location + harness.setSelectionByMouse(NSRange(location: paragraphLocation, length: 0)) + + XCTAssertEqual(harness.viewportOrigin().y, 0, accuracy: 0.001) + } + func testLiveParagraphGeometryReturnsAfterClickAndFocusAway() throws { let source = """ # Heading