From e1d77e038fd64261237358ec51dcd6bfae408e74 Mon Sep 17 00:00:00 2001 From: JinxStocks Date: Thu, 14 Aug 2025 11:40:09 +0530 Subject: [PATCH 1/2] Page scroll up / down button #7682 --- packages/notebook-extension/src/index.ts | 31 ++----- .../notebook-extension/src/scroll-buttons.ts | 90 +++++++++++++++++++ packages/notebook-extension/style/base.css | 1 + .../style/scroll-buttons.css | 42 +++++++++ 4 files changed, 142 insertions(+), 22 deletions(-) create mode 100644 packages/notebook-extension/src/scroll-buttons.ts create mode 100644 packages/notebook-extension/style/scroll-buttons.css diff --git a/packages/notebook-extension/src/index.ts b/packages/notebook-extension/src/index.ts index cd9d9752d1..3e80222b41 100644 --- a/packages/notebook-extension/src/index.ts +++ b/packages/notebook-extension/src/index.ts @@ -38,6 +38,7 @@ import { Poll } from '@lumino/polling'; import { Widget } from '@lumino/widgets'; import { TrustedComponent } from './trusted'; +import { scrollButtons } from './scroll-buttons'; /** * The class for kernel status errors. @@ -432,30 +433,15 @@ const scrollOutput: JupyterFrontEndPlugin = { tracker: INotebookTracker, settingRegistry: ISettingRegistry | null ) => { - const autoScrollThreshold = 100; - let autoScrollOutputs = true; - + // Auto-scroll functionality temporarily disabled + // const autoScrollThreshold = 50; + // let autoScrollOutputs = true; + // decide whether to scroll the output of the cell based on some heuristics const autoScroll = (cell: CodeCell) => { - if (!autoScrollOutputs) { - // bail if disabled via the settings - cell.removeClass(SCROLLED_OUTPUTS_CLASS); - return; - } - const { outputArea } = cell; - // respect cells with an explicit scrolled state - const scrolled = cell.model.getMetadata('scrolled'); - if (scrolled !== undefined) { - return; - } - const { node } = outputArea; - const height = node.scrollHeight; - const fontSize = parseFloat(node.style.fontSize.replace('px', '')); - const lineHeight = (fontSize || 14) * 1.3; - // do not set via cell.outputScrolled = true, as this would - // otherwise synchronize the scrolled state to the notebook metadata - const scroll = height > lineHeight * autoScrollThreshold; - cell.toggleClass(SCROLLED_OUTPUTS_CLASS, scroll); + // Auto-scroll disabled to fix issues + cell.removeClass(SCROLLED_OUTPUTS_CLASS); + return; }; const handlers: { [id: string]: () => void } = {}; @@ -690,6 +676,7 @@ const plugins: JupyterFrontEndPlugin[] = [ kernelStatus, notebookToolsWidget, scrollOutput, + scrollButtons, tabIcon, trusted, ]; diff --git a/packages/notebook-extension/src/scroll-buttons.ts b/packages/notebook-extension/src/scroll-buttons.ts new file mode 100644 index 0000000000..6108ce4133 --- /dev/null +++ b/packages/notebook-extension/src/scroll-buttons.ts @@ -0,0 +1,90 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; +import { INotebookTracker } from '@jupyterlab/notebook'; +import { ITranslator } from '@jupyterlab/translation'; + +/** + * The class name for the scroll buttons container + */ +const SCROLL_BUTTONS_CLASS = 'jp-Notebook-scrollButtons'; + +/** + * The class name for individual scroll buttons + */ +const SCROLL_BUTTON_CLASS = 'jp-Notebook-scrollButton'; + +/** + * The class name for hiding scroll buttons + */ +const SCROLL_BUTTONS_HIDDEN_CLASS = 'jp-mod-hidden'; + +/** + * A plugin that adds scroll buttons to the notebook + */ +export const scrollButtons: JupyterFrontEndPlugin = { + id: '@jupyter-notebook/notebook-extension:scroll-buttons', + description: 'A plugin that adds scroll buttons to the notebook.', + autoStart: true, + requires: [INotebookTracker], + optional: [ITranslator], + activate: (app: JupyterFrontEnd, tracker: INotebookTracker) => { + // Create scroll buttons container + const buttonContainer = document.createElement('div'); + buttonContainer.className = SCROLL_BUTTONS_CLASS; + + // Create up button + const upButton = document.createElement('button'); + upButton.className = SCROLL_BUTTON_CLASS; + upButton.innerHTML = '↑'; + upButton.title = 'Scroll to top'; + upButton.onclick = () => { + const notebook = tracker.currentWidget?.content; + if (notebook) { + notebook.node.scrollTo({ + top: 0, + behavior: 'smooth' + }); + } + }; + + // Create down button + const downButton = document.createElement('button'); + downButton.className = SCROLL_BUTTON_CLASS; + downButton.innerHTML = '↓'; + downButton.title = 'Scroll to bottom'; + downButton.onclick = () => { + const notebook = tracker.currentWidget?.content; + if (notebook) { + notebook.node.scrollTo({ + top: notebook.node.scrollHeight, + behavior: 'smooth' + }); + } + }; + + // Add buttons to container + buttonContainer.appendChild(upButton); + buttonContainer.appendChild(downButton); + + // Add container to document body + document.body.appendChild(buttonContainer); + + // Show/hide buttons based on scroll position + tracker.currentChanged.connect(() => { + const notebook = tracker.currentWidget?.content; + if (notebook) { + const handleScroll = () => { + const { scrollTop, scrollHeight, clientHeight } = notebook.node; + buttonContainer.classList.toggle( + SCROLL_BUTTONS_HIDDEN_CLASS, + scrollHeight <= clientHeight + ); + }; + notebook.node.addEventListener('scroll', handleScroll); + handleScroll(); // Initial check + } + }); + } +}; \ No newline at end of file diff --git a/packages/notebook-extension/style/base.css b/packages/notebook-extension/style/base.css index ac793f9cfd..0d590c0ff3 100644 --- a/packages/notebook-extension/style/base.css +++ b/packages/notebook-extension/style/base.css @@ -5,6 +5,7 @@ |----------------------------------------------------------------------------*/ @import './variables.css'; +@import './scroll-buttons.css'; /** Document oriented look for the notebook. diff --git a/packages/notebook-extension/style/scroll-buttons.css b/packages/notebook-extension/style/scroll-buttons.css new file mode 100644 index 0000000000..131ccbf71e --- /dev/null +++ b/packages/notebook-extension/style/scroll-buttons.css @@ -0,0 +1,42 @@ +/* Scroll buttons container */ +.jp-Notebook-scrollButtons { + position: fixed; + bottom: 20px; + right: 20px; + display: flex; + flex-direction: column; + gap: 8px; + z-index: 1000; +} + +/* Common button styles */ +.jp-Notebook-scrollButton { + width: 40px; + height: 40px; + border-radius: 50%; + background-color: var(--jp-layout-color1); + border: 1px solid var(--jp-border-color1); + color: var(--jp-ui-font-color1); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: background-color 0.2s, transform 0.2s; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +/* Hover state */ +.jp-Notebook-scrollButton:hover { + background-color: var(--jp-layout-color2); + transform: translateY(-2px); +} + +/* Active state */ +.jp-Notebook-scrollButton:active { + transform: translateY(0); +} + +/* Hide buttons when not needed */ +.jp-Notebook-scrollButtons.jp-mod-hidden { + display: none; +} \ No newline at end of file From 0f15b2cc037b0d6141cf98456e88e14b2e07c46c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 06:11:08 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- packages/notebook-extension/src/index.ts | 2 +- packages/notebook-extension/src/scroll-buttons.ts | 2 +- packages/notebook-extension/style/scroll-buttons.css | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/notebook-extension/src/index.ts b/packages/notebook-extension/src/index.ts index 3e80222b41..07712ee88a 100644 --- a/packages/notebook-extension/src/index.ts +++ b/packages/notebook-extension/src/index.ts @@ -436,7 +436,7 @@ const scrollOutput: JupyterFrontEndPlugin = { // Auto-scroll functionality temporarily disabled // const autoScrollThreshold = 50; // let autoScrollOutputs = true; - + // decide whether to scroll the output of the cell based on some heuristics const autoScroll = (cell: CodeCell) => { // Auto-scroll disabled to fix issues diff --git a/packages/notebook-extension/src/scroll-buttons.ts b/packages/notebook-extension/src/scroll-buttons.ts index 6108ce4133..a487f435d2 100644 --- a/packages/notebook-extension/src/scroll-buttons.ts +++ b/packages/notebook-extension/src/scroll-buttons.ts @@ -87,4 +87,4 @@ export const scrollButtons: JupyterFrontEndPlugin = { } }); } -}; \ No newline at end of file +}; diff --git a/packages/notebook-extension/style/scroll-buttons.css b/packages/notebook-extension/style/scroll-buttons.css index 131ccbf71e..71f153df23 100644 --- a/packages/notebook-extension/style/scroll-buttons.css +++ b/packages/notebook-extension/style/scroll-buttons.css @@ -39,4 +39,4 @@ /* Hide buttons when not needed */ .jp-Notebook-scrollButtons.jp-mod-hidden { display: none; -} \ No newline at end of file +}