GPT Message Top Scroll

The “↑” button next to Copy/Edit scrolls only large messages (>=700 characters) to their top, aligned per message; supports both user and assistant.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         GPT Message Top Scroll
// @namespace    https://greasyfork.org/users/your-id-or-homepage
// @version      1.3
// @description  The “↑” button next to Copy/Edit scrolls only large messages (>=700 characters) to their top, aligned per message; supports both user and assistant.
// @author       Madhuvrata
// @match        https://chatgpt.com/*
// @grant        none
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(() => {
  'use strict';

  // CSS
  const style = document.createElement('style');
  style.textContent = `
    .tm-scroll-top-btn {
      width: 32px!important;
      height: 32px!important;
      margin: 0 4px!important;
      background: rgba(0,0,0,0.05)!important;
      border: 1px solid rgba(0,0,0,0.1)!important;
      border-radius: 6px!important;
      display: inline-flex!important;
      align-items: center!important;
      justify-content: center!important;
      cursor: pointer!important;
      opacity: .7!important;
      transition: opacity .2s, background .2s;
      pointer-events: auto!important;
    }
    .tm-scroll-top-btn:hover { opacity: 1!important; background: rgba(0,0,0,0.1)!important; }
    .tm-force-panel {
      opacity: 1!important;
      pointer-events: auto!important;
      mask-image: none!important;
      -webkit-mask-image: none!important;
    }
  `;
  document.head.appendChild(style);

  const svgArrow = `
    <svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
      <path d="M10 4l-6 6h4v6h4v-6h4l-6-6z"/>
    </svg>`;

  // панель кнопок для user и assistant
  const PANEL_SELECTOR = `
    article[data-testid^="conversation-turn-"] .flex.justify-end,
    article[data-testid^="conversation-turn-"] .flex.justify-start
  `.trim().replace(/\s+/g,' ');

  function injectButtons() {
    document.querySelectorAll(PANEL_SELECTOR).forEach(panel => {
      panel.classList.add('tm-force-panel');
      const inner = panel.querySelector(':scope > div');
      if (!inner || inner.querySelector('.tm-scroll-top-btn')) return;
      inner.classList.add('tm-force-panel');

      // найдём связанный article и контейнер markdown
      const article = panel.closest('article[data-testid^="conversation-turn-"]');
      if (!article) return;
      const md = article.querySelector('.markdown') || article;

      // длина текста
      const txtLen = (md.innerText || '').length;

      // создаём кнопку
      const btn = document.createElement('button');
      btn.className = 'tm-scroll-top-btn';
      btn.innerHTML = svgArrow;
      btn.title = 'Scroll message to top';
      btn.addEventListener('click', e => {
        e.stopPropagation();
        // если короткое сообщение — игнорируем
        if (txtLen < 700) return;
        article.scrollIntoView({ behavior: 'smooth', block: 'start' });
      });

      inner.appendChild(btn);
    });
  }

  injectButtons();
  new MutationObserver(injectButtons)
    .observe(document.body, { childList: true, subtree: true });
})();