LogVisualizer Visual Unit Filter

Auto-detect units, toggle-select, color buttons visually

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         LogVisualizer Visual Unit Filter
// @namespace    logvisualizer
// @version      6.0
// @description  Auto-detect units, toggle-select, color buttons visually
// @match        https://www.logvisualizer.app/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    let panel;
    let knownUnits = new Set();
    let activeUnits = new Set(); // visual state

    function createPanel() {
        panel = document.createElement("div");
        panel.style.cssText = `
            position: fixed;
            top: 80px;
            right: 20px;
            z-index: 999999;
            background: #0b0b0b;
            border: 1px solid #333;
            border-radius: 10px;
            padding: 10px;
            font-family: monospace;
            font-size: 12px;
            color: #fff;
            box-shadow: 0 0 15px rgba(0,0,0,.7);
            min-width: 160px;
            max-height: 75vh;
            overflow-y: auto;
        `;

        panel.innerHTML = `
            <div style="text-align:center;font-weight:bold;margin-bottom:8px;">
                Unit Filter
            </div>
            <div id="unit-buttons"></div>
        `;

        document.body.appendChild(panel);
    }

    function addUnitButton(unit) {
        const container = document.getElementById("unit-buttons");
        if ([...container.querySelectorAll("button")].some(b => b.dataset.unit === unit)) return;

        const btn = document.createElement("button");
        btn.textContent = unit;
        btn.dataset.unit = unit;

        btn.style.cssText = `
            display:block;
            width:100%;
            margin:5px 0;
            padding:6px;
            background:#151515;
            color:#ddd;
            border:1px solid #333;
            border-radius:6px;
            cursor:pointer;
            transition: all 0.15s ease;
        `;

        btn.onclick = () => toggleUnit(unit, btn);

        container.appendChild(btn);
    }

    function toggleUnit(unit, btn) {
        const labels = document.querySelectorAll("li.group label");

        // toggle visual state
        if (activeUnits.has(unit)) {
            activeUnits.delete(unit);
            setButtonInactive(btn);
        } else {
            activeUnits.add(unit);
            setButtonActive(btn);
        }

        // toggle actual checkboxes
        labels.forEach(label => {
            const unitSpan = label.querySelector("span.text-neutral-400, span.dark\\:text-neutral-600");
            if (!unitSpan) return;

            if (unitSpan.textContent.trim() === unit) {
                const checkbox = label.querySelector('button[role="checkbox"]');
                if (checkbox) checkbox.click();
            }
        });
    }

    function setButtonActive(btn) {
        btn.style.background = "#1f2937";   // dark blue
        btn.style.border = "1px solid #3b82f6";
        btn.style.color = "#93c5fd";
        btn.style.boxShadow = "0 0 8px rgba(59,130,246,0.6)";
    }

    function setButtonInactive(btn) {
        btn.style.background = "#151515";
        btn.style.border = "1px solid #333";
        btn.style.color = "#ddd";
        btn.style.boxShadow = "none";
    }

    function scanUnits() {
        const spans = document.querySelectorAll(
            "li.group span.text-neutral-400, li.group span.dark\\:text-neutral-600"
        );

        spans.forEach(span => {
            const unit = span.textContent.trim();
            if (!unit.startsWith("[") || !unit.endsWith("]")) return;

            if (!knownUnits.has(unit)) {
                knownUnits.add(unit);
                addUnitButton(unit);
            }
        });
    }

    function init() {
        createPanel();
        scanUnits();

        const observer = new MutationObserver(() => scanUnits());
        observer.observe(document.body, { childList: true, subtree: true });
    }

    const wait = setInterval(() => {
        if (document.querySelector("li.group")) {
            clearInterval(wait);
            init();
        }
    }, 300);

})();