Auto-detect units, toggle-select, color buttons visually
// ==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);
})();