Redirects PageUp and PageDown keys to scroll the conversation window while typing in the input field.
// ==UserScript==
// @name Fix PageUp and PageDown scrolling for ChatGPT
// @description Redirects PageUp and PageDown keys to scroll the conversation window while typing in the input field.
// @author NWP/DEVULSKY
// @namespace https://greasyfork.org/en/scripts/519486/
// @version 0.3
// @license MIT
// @match https://chat.openai.com/*
// @match https://chatgpt.com/*
// @grant none
//
// ==/UserScript==
(function () {
'use strict';
/**
* Finds the scrollable container of the conversation window.
* Falls back to a dynamic class-based selector if a specific container ID is not found.
* @returns {HTMLElement|null} The scrollable container element, or null if not found.
*/
const getScrollableContainer = () =>
document.querySelector('#conversation-inner-div') ||
Array.from(document.querySelectorAll('div')).find(div => /^react-scroll-to-bottom--css-\S+$/.test(div.className));
/**
* Checks if an element has `overflow: hidden`.
* @param {HTMLElement} element - The element to check.
* @returns {boolean} True if `overflow` is `hidden`, false otherwise.
*/
const hasOverflowHidden = (element) => {
if(!element) return false;
const style = getComputedStyle(element);
return style && style.overflow === 'hidden';
};
/**
* Smoothly scrolls the container over a period of time.
* @param {HTMLElement} container - The scrollable container.
* @param {number} targetPosition - The target scroll position.
* @param {number} duration - The duration of the scroll animation.
*/
const smoothScroll = (container, targetPosition, duration) => {
const startPosition = container.scrollTop;
const distance = targetPosition - startPosition;
let startTime = null;
// Animation function that runs on each frame
const animateScroll = (currentTime) => {
if (!startTime) startTime = currentTime;
const timeElapsed = currentTime - startTime;
const progress = Math.min(timeElapsed / duration, 1); // Ensure progress is capped at 1
// Apply the easing function
container.scrollTop = startPosition + distance * progress;
if (timeElapsed < duration) {
requestAnimationFrame(animateScroll); // Continue the animation until duration is reached
}
};
// Start the smooth scrolling animation
requestAnimationFrame(animateScroll);
};
/**
* Handles the PageUp and PageDown key events.
* Prevents the default browser behavior and scrolls the conversation window instead.
* @param {KeyboardEvent} event - The keydown event triggered by the user.
*/
const handlePageKeys = (event) => {
if (!['PageUp', 'PageDown'].includes(event.key)) return;
const scrollableContainer = getScrollableContainer();
if (!scrollableContainer) return;
// Add this part
if (hasOverflowHidden(scrollableContainer)) {
scrollableContainer.style.overflow = 'visible';
}
event.preventDefault();
const scrollFactor = event.key === 'PageUp' ? -0.75 : 0.75;
const targetScrollPosition = scrollableContainer.scrollTop + (scrollableContainer.clientHeight * scrollFactor);
smoothScroll(scrollableContainer, targetScrollPosition, 400);
};
document.addEventListener('keydown', handlePageKeys);
})();