-
Notifications
You must be signed in to change notification settings - Fork 310
fix(core): keep keyboard selection anchor stable across scroll #542
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
kommander
merged 10 commits into
anomalyco:main
from
simonklee:fix/textarea-scrollable-select-behaviour
Jan 18, 2026
Merged
fix(core): keep keyboard selection anchor stable across scroll #542
kommander
merged 10 commits into
anomalyco:main
from
simonklee:fix/textarea-scrollable-select-behaviour
Jan 18, 2026
+239
−9
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- remove FFI plumbing - track logical anchor and viewport-delta selection - keep backward selection inclusive and clear keyboard selection state on reset
When using Shift+Home/End to select text after scrolling, the viewport wasn't following the cursor because native code skips ensureCursorVisible when a selection is active. Added ensureCursorVisibleForSelection() to handle viewport scrolling for keyboard-driven selection.
@opentui/core
@opentui/react
@opentui/solid
@opentui/core-darwin-arm64
@opentui/core-darwin-x64
@opentui/core-linux-arm64
@opentui/core-linux-x64
@opentui/core-win32-arm64
@opentui/core-win32-x64
commit: |
Contributor
Author
|
Didn't commit, but used this example to test. import { CliRenderer, createCliRenderer, TextareaRenderable, BoxRenderable, TextRenderable } from "../index"
import { setupCommonDemoKeys } from "./lib/standalone-keys"
// Content long enough to require scrolling in a small viewport
const initialContent = `Line 1: First line of content
Line 2: Second line
Line 3: Third line
Line 4: Fourth line
Line 5: Fifth line with some longer text to test horizontal behavior
Line 6: Sixth line
Line 7: Seventh line
Line 8: Eighth line
Line 9: Ninth line
Line 10: Tenth line
Line 11: Eleventh line
Line 12: Twelfth line
Line 13: Thirteenth line
Line 14: Fourteenth line
Line 15: Fifteenth line with extra content for testing
Line 16: Sixteenth line
Line 17: Seventeenth line
Line 18: Eighteenth line
Line 19: Nineteenth line
Line 20: Twentieth line - end of content`
let renderer: CliRenderer | null = null
let parentContainer: BoxRenderable | null = null
let editor: TextareaRenderable | null = null
let statusText: TextRenderable | null = null
let instructionsText: TextRenderable | null = null
export async function run(rendererInstance: CliRenderer): Promise<void> {
renderer = rendererInstance
renderer.setBackgroundColor("#1a1a2e")
parentContainer = new BoxRenderable(renderer, {
id: "parent-container",
zIndex: 10,
padding: 1,
flexDirection: "column",
})
renderer.root.add(parentContainer)
// Instructions at the top
instructionsText = new TextRenderable(renderer, {
id: "instructions",
content: `Issue #521: Scrollable Textarea Selection Bug
Test: Hold Shift + use arrow keys or Cmd+Arrow to select past visible content
Expected: Selection should work correctly even when scrolling
The textarea below has a fixed height of 8 lines (content has 20 lines)`,
fg: "#f8f8f2",
height: 4,
flexShrink: 0,
})
parentContainer.add(instructionsText)
// Fixed-height editor box to force scrolling
const editorBox = new BoxRenderable(renderer, {
id: "editor-box",
borderStyle: "single",
borderColor: "#6272a4",
backgroundColor: "#282a36",
title: "Scrollable Textarea (8 lines visible)",
titleAlignment: "left",
border: true,
height: 10, // 8 lines + 2 for border
flexShrink: 0,
})
parentContainer.add(editorBox)
// Create scrollable textarea
editor = new TextareaRenderable(renderer, {
id: "editor",
initialValue: initialContent,
textColor: "#f8f8f2",
selectionBg: "#44475a",
selectionFg: "#f8f8f2",
wrapMode: "none",
showCursor: true,
cursorColor: "#50fa7b",
})
editorBox.add(editor)
// Status bar at the bottom
statusText = new TextRenderable(renderer, {
id: "status",
content: "",
fg: "#8be9fd",
height: 1,
flexShrink: 0,
})
parentContainer.add(statusText)
editor.focus()
rendererInstance.setFrameCallback(async () => {
if (statusText && editor && !editor.isDestroyed) {
try {
const cursor = editor.logicalCursor
const selection = editor.getSelection()
const selectionInfo = selection
? `Selection: ${selection.start} to ${selection.end} | "${editor.getSelectedText().substring(0, 30)}..."`
: "No selection"
statusText.content = `Cursor: Line ${cursor.row + 1}, Col ${cursor.col + 1} | ${selectionInfo}`
} catch (error) {
// Ignore errors during shutdown
}
}
})
}
export function destroy(rendererInstance: CliRenderer): void {
rendererInstance.clearFrameCallbacks()
parentContainer?.destroy()
parentContainer = null
editor = null
statusText = null
instructionsText = null
renderer = null
}
if (import.meta.main) {
const renderer = await createCliRenderer({
exitOnCtrlC: true,
targetFps: 60,
})
run(renderer)
setupCommonDemoKeys(renderer)
} |
Collaborator
|
Ah sorry for splurting into your PR, should have run the tests fully local first. Will fix. |
Collaborator
|
I fixed it. I think the keyboard vs. mouse selection distinction could be solved as well, but this seems to satisfy the added expectations for now. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Refactor EditBufferRenderable keyboard shift-selection to track a logical anchor and viewport delta, ensuring the cursor stays visible while selection updates correctly when the viewport scrolls (e.g. Shift+Home/End).
This is a continuation of #522 without FFI plumbing for fixing #521