// ==UserScript==
// @name         Chat Generation Progress Bar
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Show progress bar reflecting streamed token activity in chat
// @match        *://yuntian-deng-chatgpt*.hf.space/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // === SETTINGS ===
    const DEBOUNCE_MS = 700; // wait this long after last token to consider generation finished
    const PROGRESS_STEP = 5; // how much % to increase per token arrival
    const MAX_PROGRESS = 95; // max % before finalizing

    // === create progress bar elements ===
    const barContainer = document.createElement('div');
    barContainer.style.position = 'fixed';
    barContainer.style.top = '0';
    barContainer.style.left = '0';
    barContainer.style.width = '100%';
    barContainer.style.height = '4px';
    barContainer.style.backgroundColor = 'rgba(0,0,0,0.1)';
    barContainer.style.zIndex = '9999';
    barContainer.style.transition = 'opacity 0.3s ease';
    barContainer.style.opacity = '0'; // initially hidden

    const bar = document.createElement('div');
    bar.style.height = '100%';
    bar.style.width = '0%';
    bar.style.backgroundColor = '#39ff14'; // bright green
    bar.style.transition = 'width 0.3s ease';

    barContainer.appendChild(bar);
    document.body.appendChild(barContainer);

    // === state ===
    let progress = 0;
    let timeoutId = null;
    let active = false;

    // === functions ===
    function startProgress() {
        if (!active) {
            active = true;
            bar.style.width = '0%';
            barContainer.style.opacity = '1';
            progress = 0;
        }
        incrementProgress();
        debounceFinish();
    }

    function incrementProgress() {
        if (progress < MAX_PROGRESS) {
            progress = Math.min(progress + PROGRESS_STEP, MAX_PROGRESS);
            bar.style.width = progress + '%';
        }
    }

    function finishProgress() {
        progress = 100;
        bar.style.width = '100%';
        setTimeout(() => {
            barContainer.style.opacity = '0';
            bar.style.width = '0%';
            active = false;
        }, 500);
    }

    function debounceFinish() {
        if (timeoutId) clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
            finishProgress();
            timeoutId = null;
        }, DEBOUNCE_MS);
    }

    // === observe chat container for streamed tokens ===
    const chatContainer = document.querySelector('.bubble-wrap.svelte-gjtrl6');
    if (!chatContainer) {
        console.warn('Chat container not found for progress bar');
        return;
    }

    const observer = new MutationObserver(mutations => {
        let tokensAdded = false;
        for (const mutation of mutations) {
            for (const node of mutation.addedNodes) {
                if (!(node instanceof HTMLElement)) continue;
                const text = node.textContent.trim();
                if (text.length > 0) {
                    tokensAdded = true;
                    break;
                }
            }
            if (tokensAdded) break;
        }
        if (tokensAdded) {
            startProgress();
        }
    });

    observer.observe(chatContainer, { childList: true, subtree: true });

})();