AnimeStars Card ID Overlay

Show data-id on cards (updates live) and color by highest-lowest

// ==UserScript==
// @name         AnimeStars Card ID Overlay
// @namespace    Violentmonkey
// @version      1.2
// @description  Show data-id on cards (updates live) and color by highest-lowest
// @match        https://astars.club/*
// @match        https://asstars.club/*
// @match        https://asstars1.astars.club/*
// @match        https://animestars.org/*
// @match        https://as1.astars.club/*
// @match        https://asstars.tv/*
// @match        https://ass.astars.club/*
// @match        https://as2.asstars.tv/*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    // Tunables
    const MAX_ID = 60000; // treat this as "very new"
    const MIN_ID = 1; // oldest baseline
    const SCAN_INTERVAL_MS = 1000; // safety scan

    const BADGE_CLASS = 'lb-id-badge';

    // Map id → color (0..120 hue = red→green)
    function idToColor(id) {
        const ratio = Math.min(1, Math.max(0, (id - MIN_ID) / (MAX_ID - MIN_ID)));
        const hue = ratio * 120; // 0 red → 120 green
        return `hsl(${hue}, 100%, 45%)`;
    }

    // Create/update a badge in the top-right of a .lootbox__card
    function setBadge(cardEl, idNum) {
        let b = cardEl.querySelector(`.${BADGE_CLASS}`);
        const color = idToColor(idNum);

        if (!b) {
            b = document.createElement('div');
            b.className = BADGE_CLASS;
            Object.assign(b.style, {
                position: 'absolute',
                top: '6px',
                right: '6px',
                padding: '2px 6px',
                borderRadius: '10px',
                background: 'rgba(0,0,0,0.55)',
                color,
                font: 'bold 12px/1.2 -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Arial, sans-serif',
                textShadow: '0 1px 1px rgba(0,0,0,.35)',
                border: `1px solid ${color}`,
                boxShadow: '0 1px 3px rgba(0,0,0,.25)',
                zIndex: 10,
                pointerEvents: 'none'
            });
            // ensure the card is positioned so the absolute badge anchors correctly
            const cs = getComputedStyle(cardEl);
            if (cs.position === 'static') cardEl.style.position = 'relative';
            cardEl.appendChild(b);
        }

        b.textContent = String(idNum);
        b.style.color = color;
        b.style.borderColor = color;
        b.title = `ID: ${idNum} • freshness ${(Math.min(1, Math.max(0, (idNum - MIN_ID) / (MAX_ID - MIN_ID))) * 100).toFixed(1)}%`;
    }

    function updateAll() {
        const cards = document.querySelectorAll('.lootbox__card, .anime-cards__item');
        cards.forEach(el => {
            const id = Number(el.getAttribute('data-id')) || 0;
            if (!id) return;
            setBadge(el, id);
        });
    }

    // Debounced observer to react to dynamic swaps
    let rafPending = false;
    function scheduleUpdate() {
        if (rafPending) return;
        rafPending = true;
        requestAnimationFrame(() => {
            rafPending = false;
            updateAll();
        });
    }

    new MutationObserver(scheduleUpdate).observe(document.body, { childList: true, subtree: true });
    setInterval(updateAll, SCAN_INTERVAL_MS); // safety net
    updateAll();
})();