[PR #753] feat: add allowUserScroll option to disable user-initiated scrolling #1539

Open
opened 2026-03-14 09:42:13 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/anomalyco/opentui/pull/753
Author: @kassieclaire
Created: 2/28/2026
Status: 🔄 Open

Base: mainHead: feature/allow-user-scroll


📝 Commits (10+)

  • ff1eb98 add allowUserScroll option to disable user-initiated scrolling on ScrollBox
  • 344f992 fix: guard slider's mouse handlers when user-initiated scrolling is disabled
  • 81e077d feat(quick-001): add allowUserInput property to Slider
  • f8bd371 feat(quick-001): propagate allowUserInput from ScrollBar to Slider
  • d64bed3 fix(quick-001): fix Box focusable option to use setter
  • bf81188 test(quick-001): add unit tests for allowUserScroll blocking all input methods
  • 270fafe feat(quick-002): add slider thumb drag demonstration to demo
  • 12a4f7c feat(quick-002): update tape file timing and regenerate GIF
  • 1b06516 fix(003): isDragging always resets on mouseup regardless of allowUserInput
  • 8f1357f test(003): add test for isDragging reset when allowUserInput disabled

📊 Changes

10 files changed (+747 additions, -5 deletions)

View changed files

demos/allow-user-scroll-demo.gif (+0 -0)
packages/core/src/examples/allow-user-scroll-demo.ts (+458 -0)
📝 packages/core/src/examples/index.ts (+7 -0)
📝 packages/core/src/renderables/Box.ts (+2 -2)
📝 packages/core/src/renderables/ScrollBar.ts (+26 -0)
📝 packages/core/src/renderables/ScrollBox.ts (+27 -1)
📝 packages/core/src/renderables/Slider.test.ts (+29 -0)
📝 packages/core/src/renderables/Slider.ts (+15 -2)
📝 packages/core/src/tests/scrollbox.test.ts (+141 -0)
scripts/demo-allow-user-scroll.tape (+42 -0)

📄 Description

feat: add allowUserScroll option to disable user-initiated scrolling

Summary

Adds a new allowUserScroll boolean option to ScrollBoxRenderable that optionally disables user-initiated scrolling (mouse wheel, keyboard, scrollbar arrows) while still allowing programmatic/auto-scrolling. This is useful for log viewers, chat interfaces, or any scrollable view that should remain sticky to the bottom while receiving new content.

Changes

Core Implementation

packages/core/src/renderables/ScrollBox.ts

  • Added allowUserScroll?: boolean option to ScrollBoxOptions (defaults to true)
  • Added private _allowUserScroll field with getter/setter
  • Modified onMouseEvent() to skip scroll events when allowUserScroll is false
  • Modified handleKeyPress() to return false immediately when allowUserScroll is false
  • Added updateScrollBarUserScroll() method to propagate setting to scrollbars

packages/core/src/renderables/ScrollBar.ts

  • Added allowUserInput?: boolean property (defaults to true)
  • Modified handleKeyPress() to respect allowUserInput flag
  • Modified arrow button onMouseDown handlers to check allowUserInput before scrolling
  • Fixed: isDragging now always resets on mouseup regardless of allowUserInput state

packages/core/src/renderables/Slider.ts

  • Added allowUserInput?: boolean property (defaults to true)
  • Modified mouse handlers to guard against user input when disabled
  • Mouse drag tracking is properly reset even when allowUserInput is disabled mid-drag

packages/core/src/renderables/Box.ts

  • Fixed: focusable option now properly uses setter to ensure focus manager registration

Demo & Documentation

packages/core/src/examples/allow-user-scroll-demo.ts (new)

  • Side-by-side comparison demo with two scrollboxes
  • Left: allowUserScroll = true (user scrolling enabled)
  • Right: allowUserScroll = false (user scrolling disabled, auto-scroll only)
  • Automatic demo sequence showing the difference with notification bar
  • Uses mock mouse events to simulate real user scroll input
  • Includes slider thumb drag demonstration

packages/core/src/examples/index.ts

  • Added new example to the examples menu

scripts/demo-allow-user-scroll.tape (new)

  • VHS recording script for automated demo capture

Demo

allowUserScroll Demo

Demo: Side-by-side comparison showing left panel (allowUserScroll = true) responding to scroll input while right panel (allowUserScroll = false) remains fixed at the bottom, only auto-scrolling when new content is added.

The demo shows:

  1. Both panels auto-scroll as new content is added (sticky to bottom)
  2. Left panel responds to simulated mouse wheel scroll (moves up)
  3. Right panel ignores all scroll attempts (stays at bottom)
  4. Auto-scroll continues working on both panels throughout
  5. Slider thumb drag interaction

Usage

import { ScrollBoxRenderable } from "@opentui/core"

// Disable user scrolling, only allow programmatic scrolling
const scrollBox = new ScrollBoxRenderable(renderer, {
  allowUserScroll: false,
  stickyScroll: true,
  stickyStart: "bottom",
  // ... other options
})

// Programmatic scrolling still works
scrollBox.scrollTop = 100  // ✓ Works
scrollBox.scrollBy(10)     // ✓ Works

// User input is ignored
// - Mouse wheel: ignored
// - Keyboard (j/k, arrows, page up/down): ignored  
// - Scrollbar arrows: ignored

// Re-enable user scrolling later
scrollBox.allowUserScroll = true

Testing

  • Manually tested with the demo example: bun run src/examples/allow-user-scroll-demo.ts
  • Verified mouse wheel, keyboard, and scrollbar arrow inputs are blocked
  • Verified programmatic scrolling (scrollTop, scrollBy) still works
  • Verified auto-scroll (sticky scroll) continues working when allowUserScroll = false
  • Unit tests for allowUserScroll blocking all input methods
  • Unit tests for isDragging reset behavior when allowUserInput is disabled mid-drag

Resolves: N/A (new feature)


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/anomalyco/opentui/pull/753 **Author:** [@kassieclaire](https://github.com/kassieclaire) **Created:** 2/28/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feature/allow-user-scroll` --- ### 📝 Commits (10+) - [`ff1eb98`](https://github.com/anomalyco/opentui/commit/ff1eb9825499459bc829d93636b492e32c6fd1cd) add allowUserScroll option to disable user-initiated scrolling on ScrollBox - [`344f992`](https://github.com/anomalyco/opentui/commit/344f9924f12731f6a222c86b7e26a5825e89deb6) fix: guard slider's mouse handlers when user-initiated scrolling is disabled - [`81e077d`](https://github.com/anomalyco/opentui/commit/81e077dd59c696d8ff9bd9b466cfc8a2b15bd5cf) feat(quick-001): add allowUserInput property to Slider - [`f8bd371`](https://github.com/anomalyco/opentui/commit/f8bd371268d62ee6dd7cf91b71aed31044f8a0f1) feat(quick-001): propagate allowUserInput from ScrollBar to Slider - [`d64bed3`](https://github.com/anomalyco/opentui/commit/d64bed3eaebd033d28cac78d70086d9131d93576) fix(quick-001): fix Box focusable option to use setter - [`bf81188`](https://github.com/anomalyco/opentui/commit/bf811880432f46899063a7f227f89268589fd859) test(quick-001): add unit tests for allowUserScroll blocking all input methods - [`270fafe`](https://github.com/anomalyco/opentui/commit/270fafe67129a72a43a260965fd02213d73f72c6) feat(quick-002): add slider thumb drag demonstration to demo - [`12a4f7c`](https://github.com/anomalyco/opentui/commit/12a4f7ce91fb5c0128c163850c6a29062ae7d966) feat(quick-002): update tape file timing and regenerate GIF - [`1b06516`](https://github.com/anomalyco/opentui/commit/1b06516a694fc992680c61a84d6536281ffd66f5) fix(003): isDragging always resets on mouseup regardless of allowUserInput - [`8f1357f`](https://github.com/anomalyco/opentui/commit/8f1357f29b2f63abc0b338ac80e91ee1754aac78) test(003): add test for isDragging reset when allowUserInput disabled ### 📊 Changes **10 files changed** (+747 additions, -5 deletions) <details> <summary>View changed files</summary> ➕ `demos/allow-user-scroll-demo.gif` (+0 -0) ➕ `packages/core/src/examples/allow-user-scroll-demo.ts` (+458 -0) 📝 `packages/core/src/examples/index.ts` (+7 -0) 📝 `packages/core/src/renderables/Box.ts` (+2 -2) 📝 `packages/core/src/renderables/ScrollBar.ts` (+26 -0) 📝 `packages/core/src/renderables/ScrollBox.ts` (+27 -1) 📝 `packages/core/src/renderables/Slider.test.ts` (+29 -0) 📝 `packages/core/src/renderables/Slider.ts` (+15 -2) 📝 `packages/core/src/tests/scrollbox.test.ts` (+141 -0) ➕ `scripts/demo-allow-user-scroll.tape` (+42 -0) </details> ### 📄 Description # feat: add allowUserScroll option to disable user-initiated scrolling ## Summary Adds a new `allowUserScroll` boolean option to `ScrollBoxRenderable` that optionally disables user-initiated scrolling (mouse wheel, keyboard, scrollbar arrows) while still allowing programmatic/auto-scrolling. This is useful for log viewers, chat interfaces, or any scrollable view that should remain sticky to the bottom while receiving new content. ## Changes ### Core Implementation **packages/core/src/renderables/ScrollBox.ts** - Added `allowUserScroll?: boolean` option to `ScrollBoxOptions` (defaults to `true`) - Added private `_allowUserScroll` field with getter/setter - Modified `onMouseEvent()` to skip scroll events when `allowUserScroll` is `false` - Modified `handleKeyPress()` to return `false` immediately when `allowUserScroll` is `false` - Added `updateScrollBarUserScroll()` method to propagate setting to scrollbars **packages/core/src/renderables/ScrollBar.ts** - Added `allowUserInput?: boolean` property (defaults to `true`) - Modified `handleKeyPress()` to respect `allowUserInput` flag - Modified arrow button `onMouseDown` handlers to check `allowUserInput` before scrolling - Fixed: `isDragging` now always resets on mouseup regardless of `allowUserInput` state **packages/core/src/renderables/Slider.ts** - Added `allowUserInput?: boolean` property (defaults to `true`) - Modified mouse handlers to guard against user input when disabled - Mouse drag tracking is properly reset even when `allowUserInput` is disabled mid-drag **packages/core/src/renderables/Box.ts** - Fixed: `focusable` option now properly uses setter to ensure focus manager registration ### Demo & Documentation **packages/core/src/examples/allow-user-scroll-demo.ts** (new) - Side-by-side comparison demo with two scrollboxes - Left: `allowUserScroll = true` (user scrolling enabled) - Right: `allowUserScroll = false` (user scrolling disabled, auto-scroll only) - Automatic demo sequence showing the difference with notification bar - Uses mock mouse events to simulate real user scroll input - Includes slider thumb drag demonstration **packages/core/src/examples/index.ts** - Added new example to the examples menu **scripts/demo-allow-user-scroll.tape** (new) - VHS recording script for automated demo capture ## Demo ![allowUserScroll Demo](https://vhs.charm.sh/vhs-3UkpeNdqdy4qVAzvuFtKP4.gif) *Demo: Side-by-side comparison showing left panel (`allowUserScroll = true`) responding to scroll input while right panel (`allowUserScroll = false`) remains fixed at the bottom, only auto-scrolling when new content is added.* The demo shows: 1. Both panels auto-scroll as new content is added (sticky to bottom) 2. Left panel responds to simulated mouse wheel scroll (moves up) 3. Right panel ignores all scroll attempts (stays at bottom) 4. Auto-scroll continues working on both panels throughout 5. Slider thumb drag interaction ## Usage ```typescript import { ScrollBoxRenderable } from "@opentui/core" // Disable user scrolling, only allow programmatic scrolling const scrollBox = new ScrollBoxRenderable(renderer, { allowUserScroll: false, stickyScroll: true, stickyStart: "bottom", // ... other options }) // Programmatic scrolling still works scrollBox.scrollTop = 100 // ✓ Works scrollBox.scrollBy(10) // ✓ Works // User input is ignored // - Mouse wheel: ignored // - Keyboard (j/k, arrows, page up/down): ignored // - Scrollbar arrows: ignored // Re-enable user scrolling later scrollBox.allowUserScroll = true ``` ## Testing - Manually tested with the demo example: `bun run src/examples/allow-user-scroll-demo.ts` - Verified mouse wheel, keyboard, and scrollbar arrow inputs are blocked - Verified programmatic scrolling (`scrollTop`, `scrollBy`) still works - Verified auto-scroll (sticky scroll) continues working when `allowUserScroll = false` - Unit tests for `allowUserScroll` blocking all input methods - Unit tests for `isDragging` reset behavior when `allowUserInput` is disabled mid-drag ## Related Resolves: N/A (new feature) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/opentui#1539
No description provided.