NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name BroMerc Companion
// @namespace tampermonkey
// @version 1.1
// @description BroMerc Companion: Your Ultimate tool for Pre-War hits or monitoring multiple targets at once!
// @author Brother [2590792]
// @match https://www.torn.com/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_xmlhttpRequest
// @connect api.torn.com
// @license AGPL-3.0-only
// @updateURL https://openuserjs.org/meta/brother-torn/BroMerc_Companion.meta.js
// ==/UserScript==
(function () {
"use strict";
function saveRaw(key, value) {
try { GM_setValue(key, JSON.stringify(value)); } catch (e) { console.error("saveRaw", e); }
}
function loadRaw(key, def = null) {
try {
const v = GM_getValue(key);
return v ? JSON.parse(v) : def;
} catch (e) { return def; }
}
function delRaw(key) {
try { GM_deleteValue(key); } catch (e) { console.error("delRaw", e); }
}
const KEY_API = "mc_apiKey_v1";
const KEY_CACHE = "mc_cache_v1";
const KEY_FILTER = "mc_filter_v1";
const KEY_UI = "mc_ui_v1";
const KEY_SHOW_ATTACKABLE = "mc_show_attackable_v1";
const KEY_LASTFILTER = "mc_lastfilter_v1";
const KEY_LASTMINUTES = "mc_lastminutes_v1";
const CACHE_TTL = 5 * 60 * 1000;
let apiKey = loadRaw(KEY_API, "") || "";
let cache = loadRaw(KEY_CACHE, { timestamp: 0, input: "", data: [] });
let filterMode = loadRaw(KEY_FILTER, "all");
let uiState = loadRaw(KEY_UI, { minimized: false, x: null, y: null, iconX: null, iconY: null });
let showOnlyAttackable = loadRaw(KEY_SHOW_ATTACKABLE, false);
let lastFilterMode = loadRaw(KEY_LASTFILTER, "all");
let lastMinutesThreshold = loadRaw(KEY_LASTMINUTES, 0);
function escapeHtml(s) {
return String(s).replace(/[&<>"'`=\/]/g, ch => ({
'&':'&','<':'<','>':'>','"':'"',"'":''','/':'/','`':'`','=':'='
}[ch]));
}
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
function gmFetchJson(url) {
return new Promise(resolve => {
try {
GM_xmlhttpRequest({
method: "GET",
url,
responseType: "text",
onload(res) {
try {
const json = JSON.parse(res.responseText);
resolve({ ok: true, json });
} catch (err) {
resolve({ ok: false, error: "parse_error" });
}
},
onerror() { resolve({ ok: false, error: "network" }); },
ontimeout() { resolve({ ok: false, error: "timeout" }); }
});
} catch (err) {
resolve({ ok: false, error: "gm_error" });
}
});
}
function buildApiUrl(id) {
return `https://api.torn.com/user/${id}?selections=profile&key=${encodeURIComponent(apiKey)}&comment=TornStakeout×tamp=${Date.now()}`;
}
if (!document.getElementById("mc_panel_full_v1")) {
createUI();
} else {
applyUiStateIfPresent();
}
function createUI() {
const panel = document.createElement("div");
panel.id = "mc_panel_full_v1";
panel.style.cssText = `
position:fixed; top:80px; right:18px; width:560px; z-index:999999;
background:#0f0f10; color:#e7e7e7; border:1px solid #222; border-radius:10px;
padding:12px; font-family:Arial, sans-serif; font-size:13px;
max-height:86vh; overflow:auto; box-shadow:0 8px 30px rgba(0,0,0,0.6);
`;
panel.innerHTML = `
<div id="mc_header_v1" style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;cursor:move;">
<div>
<div style="font-weight:700;color:#7adfff">⚔️ Merc Companion</div>
<div style="font-size:12px;color:#9aa2a6">Paste IDs, links (XID=...), or lists.</div>
</div>
<div style="display:flex;gap:8px;align-items:center">
<label style="color:#9aa2a6;font-size:12px;display:flex;align-items:center">
<input id="mc_show_attackable_v1" type="checkbox" style="margin-right:6px" />
Only Attackable
</label>
<button id="mc_minbtn_v1" title="Minimize" style="background:#121212;border:1px solid #273238;color:#7adfff;padding:6px;border-radius:6px;cursor:pointer">_</button>
<button id="mc_setKey_v1" title="Set API Key" style="background:#121212;border:1px solid #273238;color:#7adfff;padding:6px;border-radius:6px;cursor:pointer">Set API Key</button>
</div>
</div>
<div id="mc_body_v1">
<textarea id="mc_input_v1" placeholder="Paste names + IDs or Profile URLs here" style="width:100%;height:140px;background:#0b0b0b;color:#e7e7e7;border:1px solid #222;padding:8px;border-radius:6px;resize:vertical"></textarea>
<div style="display:flex;gap:10px;margin-top:8px;align-items:center;flex-wrap:wrap;">
<label style="color:#9aa2a6">Patch size
<input id="mc_patch_v1" type="number" value="10" min="1" style="width:72px;margin-left:6px;background:#111;color:#e7e7e7;border:1px solid #222;padding:6px;border-radius:6px" />
</label>
<label style="color:#9aa2a6">Delay (s)
<input id="mc_delay_v1" type="number" value="5" min="0" style="width:72px;margin-left:6px;background:#111;color:#e7e7e7;border:1px solid #222;padding:6px;border-radius:6px" />
</label>
<label style="color:#9aa2a6">Auto recheck (s)
<input id="mc_auto_v1" type="number" value="0" min="0" style="width:90px;margin-left:6px;background:#111;color:#e7e7e7;border:1px solid #222;padding:6px;border-radius:6px" />
</label>
<label style="color:#9aa2a6;display:flex;align-items:center">Show which members?
<select id="mc_lastfilter_v1" style="margin-left:6px;background:#111;color:#e7e7e7;border:1px solid #222;padding:6px;border-radius:6px">
<option value="all">All</option>
<option value="offline_idle">Offline + Idle</option>
<option value="online">Online</option>
</select>
</label>
<label style="color:#9aa2a6;display:flex;align-items:center">Hide if last action <
<input id="mc_lastminutes_v1" type="number" min="0" value="0" style="width:70px;margin-left:6px;background:#111;color:#e7e7e7;border:1px solid #222;padding:6px;border-radius:6px" />
minutes
</label>
</div>
<div style="display:flex;gap:8px;margin-top:10px">
<button id="mc_run_v1" style="flex:1;background:#06232b;color:#cfffff;border:1px solid #154a56;padding:8px;border-radius:6px;cursor:pointer">▶ Run Check</button>
<button id="mc_clear_v1" style="background:#2b1414;color:#ffb3b3;border:1px solid #4b2b2b;padding:8px;border-radius:6px;cursor:pointer">🗑 Clear Cache</button>
<button id="mc_toggle_v1" style="background:#121212;color:#7adfff;border:1px solid #273238;padding:8px;border-radius:6px;cursor:pointer">Filter: ${filterMode === "all" ? "All" : "Non-Rev Only"}</button>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px">
<div id="mc_info_v1" style="font-size:12px;color:#9aa2a6">Cached: ${cache.timestamp ? (new Date(cache.timestamp)).toLocaleTimeString() : "none"}</div>
<div id="mc_status_v1" style="font-size:13px;color:lime"></div>
</div>
<div id="mc_results_v1" style="margin-top:8px;background:#060607;border:1px solid #121213;border-radius:6px;padding:10px;max-height:360px;overflow:auto;font-family:monospace;font-size:13px;color:#e7e7e7">
<div style="color:#9aa2a6">No results yet.</div>
</div>
<textarea id="mc_readyBox_v1" readonly style="width:100%;height:36px;margin-top:8px;background:#0b0b0b;color:#00ff7f;border:1px solid #222;padding:6px;border-radius:6px;resize:none"></textarea>
</div>
`;
document.body.appendChild(panel);
const iconContainer = document.createElement("div");
iconContainer.id = "mc_icon_wrap_v1";
iconContainer.style.cssText = "position:fixed; top:100px; right:20px; z-index:999999; display:none;";
iconContainer.innerHTML = `<div id="mc_icon_v1" style="width:30px;height:30px;display:flex;align-items:center;justify-content:center;background:#1a1a1a;color:#7adfff;border:1px solid #333;border-radius:6px;font-size:16px;cursor:pointer">⚔️</div>`;
document.body.appendChild(iconContainer);
const inputEl = document.getElementById("mc_input_v1");
const runBtn = document.getElementById("mc_run_v1");
const clearBtn = document.getElementById("mc_clear_v1");
const toggleBtn = document.getElementById("mc_toggle_v1");
const setKeyBtn = document.getElementById("mc_setKey_v1");
const minBtn = document.getElementById("mc_minbtn_v1");
const resultsEl = document.getElementById("mc_results_v1");
const statusEl = document.getElementById("mc_status_v1");
const infoEl = document.getElementById("mc_info_v1");
const readyBox = document.getElementById("mc_readyBox_v1");
const header = document.getElementById("mc_header_v1");
const panelEl = document.getElementById("mc_panel_full_v1");
const iconEl = document.getElementById("mc_icon_v1");
const showAttackableEl = document.getElementById("mc_show_attackable_v1");
const lastFilterEl = document.getElementById("mc_lastfilter_v1");
const lastMinutesEl = document.getElementById("mc_lastminutes_v1");
showAttackableEl.checked = !!showOnlyAttackable;
lastFilterEl.value = lastFilterMode || "all";
lastMinutesEl.value = lastMinutesThreshold || 0;
function applyUiState() {
if (uiState.minimized) {
panelEl.style.display = "none";
iconContainer.style.display = "block";
if (typeof uiState.iconX === "number" && typeof uiState.iconY === "number") {
iconContainer.style.left = uiState.iconX + "px";
iconContainer.style.top = uiState.iconY + "px";
iconContainer.style.right = "auto";
}
} else {
panelEl.style.display = "block";
iconContainer.style.display = "none";
if (typeof uiState.x === "number" && typeof uiState.y === "number") {
panelEl.style.left = uiState.x + "px";
panelEl.style.top = uiState.y + "px";
panelEl.style.right = "auto";
}
}
}
applyUiState();
function makeDraggableWithClickDetection(handleEl, moveEl, onClickIfNotDragged) {
let isDown = false;
let startX = 0, startY = 0;
let moved = false;
function down(e) {
if (e.target.tagName === "BUTTON" || e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA" || e.target.closest("a")) return;
isDown = true; moved = false;
startX = e.clientX; startY = e.clientY;
const rect = moveEl.getBoundingClientRect();
moveEl.dataset._drag_offsetX = e.clientX - rect.left;
moveEl.dataset._drag_offsetY = e.clientY - rect.top;
document.addEventListener("mousemove", move);
document.addEventListener("mouseup", up);
document.body.style.userSelect = "none";
}
function move(e) {
if (!isDown) return;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
if (Math.hypot(dx, dy) > 6) moved = true;
const offX = parseFloat(moveEl.dataset._drag_offsetX || 0);
const offY = parseFloat(moveEl.dataset._drag_offsetY || 0);
moveEl.style.left = (e.clientX - offX) + "px";
moveEl.style.top = (e.clientY - offY) + "px";
moveEl.style.right = "auto";
}
function up(e) {
if (!isDown) return;
isDown = false;
document.removeEventListener("mousemove", move);
document.removeEventListener("mouseup", up);
document.body.style.userSelect = "";
const rect = moveEl.getBoundingClientRect();
if (moveEl === panelEl) {
uiState.x = rect.left; uiState.y = rect.top;
} else if (moveEl === iconContainer) {
uiState.iconX = rect.left; uiState.iconY = rect.top;
}
saveRaw(KEY_UI, uiState);
if (!moved && typeof onClickIfNotDragged === "function") {
onClickIfNotDragged();
}
}
handleEl.addEventListener("mousedown", down);
}
makeDraggableWithClickDetection(header, panelEl, null);
makeDraggableWithClickDetection(iconContainer, iconContainer, () => {
uiState.minimized = false;
if (typeof uiState.iconX === "number" && typeof uiState.iconY === "number") {
panelEl.style.left = uiState.iconX + "px";
panelEl.style.top = uiState.iconY + "px";
panelEl.style.right = "auto";
uiState.x = uiState.iconX; uiState.y = uiState.iconY;
}
saveRaw(KEY_UI, uiState);
applyUiState();
});
minBtn.addEventListener("click", () => {
const r = panelEl.getBoundingClientRect();
uiState.iconX = r.left; uiState.iconY = r.top;
uiState.minimized = true;
saveRaw(KEY_UI, uiState);
applyUiState();
});
function extractIdsFromText(text) {
const ids = [];
if (!text) return ids;
let m;
// 1. URL Parameter (XID=)
const urlRe = /XID=(\d+)/gi;
while ((m = urlRe.exec(text)) !== null) ids.push(m[1]);
// 2. Brackets
const bracketRe = /[\[\(\{<]\s*(\d{3,})\s*[\]\)\}>]/g;
while ((m = bracketRe.exec(text)) !== null) ids.push(m[1]);
// 3. Fallback: Bare numbers (if none found yet or to catch strays)
// If we found some via URL/brackets, we usually don't want every loose number,
// but to be safe and "find ID straight ahead", we only check bare if ids are empty
// OR we can just add them. Let's check bare only if list is empty to avoid "10" from settings.
if (ids.length === 0) {
const bareRe = /\b(\d{5,})\b/g;
while ((m = bareRe.exec(text)) !== null) ids.push(m[1]);
}
return [...new Set(ids)];
}
function isAttackableStrict(d) {
if (!d || !d.raw) return false;
const state = (d.status || "").toLowerCase().trim();
if (state !== "okay") return false;
const statusObj = d.raw.status || {};
const details = (statusObj.details || statusObj.description || "").toLowerCase();
if (!details) return true;
if (details.includes("hospital") || details.includes("hospitalized")) return false;
return true;
}
function lastActionMatchesFilter(raw, filter) {
if (!raw || !raw.last_action || !raw.last_action.status) return filter === "all";
const st = String(raw.last_action.status).toLowerCase();
if (filter === "all") return true;
if (filter === "online") return st.includes("online");
if (filter === "offline_idle") return st.includes("offline") || st.includes("idle") || st.includes("away") || st.includes("afk");
return true;
}
function renderResults(objects) {
let filtered = objects;
if (showOnlyAttackable) filtered = filtered.filter(d => isAttackableStrict(d));
filtered = filtered.filter(d => lastActionMatchesFilter(d.raw, lastFilterMode));
const threshold = Math.max(0, Number(lastMinutesThreshold) || 0);
if (threshold > 0) {
const nowSec = Date.now() / 1000;
filtered = filtered.filter(d => {
const la = d.raw && d.raw.last_action;
if (!la || !la.timestamp) return true;
const mins = (nowSec - Number(la.timestamp)) / 60;
return mins >= threshold;
});
}
if (filterMode === "nonrevivable") filtered = filtered.filter(d => Number(d.revivable) === 0);
const total = objects.length; const shown = filtered.length;
const counts = { ok:0, hospital:0, travel:0, other:0, error:0 };
const lines = filtered.map(d => {
if (d.isError) { counts.error++; return `<div style="color:#ff8b8b">${escapeHtml(d.id)}: Error fetching</div>`; }
const s = (d.status || "").toLowerCase();
const attackable = isAttackableStrict(d);
if (attackable) {
counts.ok++;
let lastActionText = "";
if (d.raw && d.raw.last_action) {
const la = d.raw.last_action;
const laStatus = (la.status || "").toLowerCase();
const rel = la.relative || "";
if (laStatus.includes("online")) lastActionText = `Online`;
else if (laStatus.includes("idle") || laStatus.includes("away") || laStatus.includes("afk")) lastActionText = rel ? `Idle (${escapeHtml(rel)})` : `Idle`;
else if (laStatus.includes("offline")) lastActionText = rel ? `Offline (${escapeHtml(rel)})` : `Offline`;
else lastActionText = rel ? `${escapeHtml(la.status)} (${escapeHtml(rel)})` : escapeHtml(la.status || "");
}
const attack = `<a href="https://www.torn.com/loader.php?sid=attack&user2ID=${d.player_id}" target="_blank" style="color:#7adfff">Attack</a>`;
const laHtml = lastActionText ? ` <span style="color:#bfc9cc;margin-left:8px;font-size:12px">— ${escapeHtml(lastActionText)}</span>` : "";
return `<div style="color:#b8f5b8">✅ ${escapeHtml(d.name)} [${d.player_id}] → ${attack}${laHtml}</div>`;
}
if (s.includes("hospital") || s.includes("hospitalized")) { counts.hospital++; return `<div style="color:orange;text-decoration:line-through">Hospital${Number(d.revivable) === 1 ? " – Revivable" : ""} — ${escapeHtml(d.name)} [${d.player_id}]</div>`; }
if (s.includes("travel") || s.includes("travelling") || s.includes("traveling")) { counts.travel++; return `<div style="color:gray;text-decoration:line-through">Traveling — ${escapeHtml(d.name)} [${d.player_id}]</div>`; }
if (s.includes("jail") || s.includes("jailed") || s.includes("prison")) { counts.other++; return `<div style="color:#ff9f9f;text-decoration:line-through">Jailed — ${escapeHtml(d.name)} [${d.player_id}]</div>`; }
counts.other++; return `<div style="color:#ff9f9f">${escapeHtml(d.status || "Unknown")} — ${escapeHtml(d.name)} [${d.player_id}]</div>`;
});
const summary = `<div style="color:#9aa2a6;margin-bottom:6px">Showing ${shown} of ${total} — OK:${counts.ok} Hospital:${counts.hospital} Travel:${counts.travel} Other:${counts.other} Errors:${counts.error}</div>`;
resultsEl.innerHTML = summary + lines.join("");
return counts;
}
function saveCache(obj) {
saveRaw(KEY_CACHE, obj);
cache = obj;
infoEl.textContent = `Cached: ${new Date(obj.timestamp).toLocaleTimeString()}`;
}
async function fetchProfile(id) {
const url = buildApiUrl(id);
const res = await gmFetchJson(url);
if (!res.ok) return { id, player_id: id, name: `#${id}`, isError: true, raw: null };
const data = res.json;
if (data && !data.error) {
return {
id,
player_id: data.player_id || id,
name: data.name || `#${id}`,
status: (data.status && data.status.state) ? data.status.state : (data.status ? data.status : "Unknown"),
revivable: typeof data.revivable !== "undefined" ? Number(data.revivable) : 0,
isError: false,
raw: data
};
} else {
return { id, player_id: id, name: `#${id}`, isError: true, raw: data || null };
}
}
let currentObjects = [];
async function runCheck(force = false) {
if (!apiKey) {
const k = prompt("Enter your Torn API key (saved locally):");
if (!k) return;
apiKey = k.trim();
saveRaw(KEY_API, apiKey);
}
lastFilterMode = lastFilterEl.value || "all";
lastMinutesThreshold = Number(lastMinutesEl.value || 0);
saveRaw(KEY_LASTFILTER, lastFilterMode);
saveRaw(KEY_LASTMINUTES, lastMinutesThreshold);
const rawText = (inputEl.value && inputEl.value.trim()) ? inputEl.value.trim() : (cache.input || "");
if (!rawText) { alert("Paste a list of targets with IDs first."); return; }
const ids = extractIdsFromText(rawText);
if (!ids || ids.length === 0) { alert("No valid IDs found in input."); return; }
const patchSize = Math.max(1, parseInt(document.getElementById("mc_patch_v1").value || 10, 10));
const delaySec = Math.max(0, parseInt(document.getElementById("mc_delay_v1").value || 5, 10));
const autoSec = Math.max(0, parseInt(document.getElementById("mc_auto_v1").value || 0, 10));
runBtn.disabled = true; clearBtn.disabled = true; toggleBtn.disabled = true; setKeyBtn.disabled = true;
statusEl.style.color = "#7adfff"; statusEl.textContent = `Checking ${ids.length} targets...`;
readyBox.value = "";
currentObjects = [];
for (let i = 0; i < ids.length; i += patchSize) {
const chunk = ids.slice(i, i + patchSize);
const promises = chunk.map(id => fetchProfile(id));
const chunkResults = await Promise.all(promises);
currentObjects.push(...chunkResults);
renderResults(currentObjects);
if (i + patchSize < ids.length && delaySec > 0) {
statusEl.style.color = "#ffb86b";
statusEl.textContent = `Waiting ${delaySec}s before next patch...`;
await sleep(delaySec * 1000);
statusEl.style.color = "#7adfff";
statusEl.textContent = `Checking ${ids.length} targets...`;
}
}
const counts = renderResults(currentObjects);
const nowTs = Date.now();
saveCache({ timestamp: nowTs, input: rawText, data: currentObjects });
readyBox.value = `✅ List ready — OK:${counts.ok} Hospital:${counts.hospital} Travel:${counts.travel} Errors:${counts.error}`;
statusEl.style.color = "lime";
statusEl.textContent = "Done";
runBtn.disabled = false; clearBtn.disabled = false; toggleBtn.disabled = false; setKeyBtn.disabled = false;
if (autoSec > 0) setTimeout(() => runCheck(true), autoSec * 1000);
}
runBtn.addEventListener("click", () => runCheck(false));
clearBtn.addEventListener("click", () => {
delRaw(KEY_CACHE);
cache = { timestamp: 0, input: "", data: [] };
resultsEl.innerHTML = `<div style="color:#9aa2a6">Cache cleared.</div>`;
readyBox.value = "";
infoEl.textContent = `Cached: none`;
statusEl.textContent = "";
});
toggleBtn.addEventListener("click", () => {
filterMode = (filterMode === "all") ? "nonrevivable" : "all";
saveRaw(KEY_FILTER, filterMode);
toggleBtn.textContent = `Filter: ${filterMode === "all" ? "All" : "Non-Rev Only"}`;
const src = (currentObjects && currentObjects.length) ? currentObjects : (cache && cache.data && cache.data.length ? cache.data : []);
if (src && src.length) renderResults(src);
});
showAttackableEl.addEventListener("change", () => {
showOnlyAttackable = !!showAttackableEl.checked;
saveRaw(KEY_SHOW_ATTACKABLE, showOnlyAttackable);
const src = (currentObjects && currentObjects.length) ? currentObjects : (cache && cache.data && cache.data.length ? cache.data : []);
if (src && src.length) renderResults(src);
});
lastFilterEl.addEventListener("change", () => {
lastFilterMode = lastFilterEl.value || "all";
saveRaw(KEY_LASTFILTER, lastFilterMode);
const src = (currentObjects && currentObjects.length) ? currentObjects : (cache && cache.data && cache.data.length ? cache.data : []);
if (src && src.length) renderResults(src);
});
lastMinutesEl.addEventListener("change", () => {
lastMinutesThreshold = Number(lastMinutesEl.value || 0);
saveRaw(KEY_LASTMINUTES, lastMinutesThreshold);
const src = (currentObjects && currentObjects.length) ? currentObjects : (cache && cache.data && cache.data.length ? cache.data : []);
if (src && src.length) renderResults(src);
});
setKeyBtn.addEventListener("click", () => {
const k = prompt("Enter your Torn API key (saved locally). Leave blank to cancel.");
if (!k) return;
apiKey = k.trim();
saveRaw(KEY_API, apiKey);
alert("API key saved.");
});
if (cache && cache.input) inputEl.value = cache.input;
if (cache && cache.timestamp && (Date.now() - cache.timestamp) < CACHE_TTL && Array.isArray(cache.data) && cache.data.length) {
const counts = renderResults(cache.data);
readyBox.value = `✅ List ready (cached) — OK:${counts.ok} Hospital:${counts.hospital} Travel:${counts.travel} Errors:${counts.error}`;
statusEl.textContent = "Cached";
infoEl.textContent = `Cached: ${new Date(cache.timestamp).toLocaleTimeString()}`;
}
const observer = new MutationObserver(() => {
if (!document.getElementById("mc_panel_full_v1")) {
try { createUI(); } catch (e) { console.error("recreate UI failed", e); }
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
function applyUiStateIfPresent() {
const panelEl = document.getElementById("mc_panel_full_v1");
const iconContainer = document.getElementById("mc_icon_wrap_v1");
const showAttackableEl = document.getElementById("mc_show_attackable_v1");
const lastFilterEl = document.getElementById("mc_lastfilter_v1");
const lastMinutesEl = document.getElementById("mc_lastminutes_v1");
if (!panelEl || !iconContainer) return;
if (uiState.minimized) {
panelEl.style.display = "none";
iconContainer.style.display = "block";
if (typeof uiState.iconX === "number" && typeof uiState.iconY === "number") {
iconContainer.style.left = uiState.iconX + "px";
iconContainer.style.top = uiState.iconY + "px";
iconContainer.style.right = "auto";
}
} else {
panelEl.style.display = "block";
iconContainer.style.display = "none";
if (typeof uiState.x === "number" && typeof uiState.y === "number") {
panelEl.style.left = uiState.x + "px";
panelEl.style.top = uiState.y + "px";
panelEl.style.right = "auto";
}
}
if (showAttackableEl) showAttackableEl.checked = !!showOnlyAttackable;
if (lastFilterEl) lastFilterEl.value = lastFilterMode || "all";
if (lastMinutesEl) lastMinutesEl.value = lastMinutesThreshold || 0;
const cacheLocal = loadRaw(KEY_CACHE, null);
if (cacheLocal && cacheLocal.timestamp && (Date.now() - cacheLocal.timestamp) < CACHE_TTL && Array.isArray(cacheLocal.data) && cacheLocal.data.length) {
try {
const btn = document.getElementById("mc_toggle_v1");
if (btn) { btn.click(); btn.click(); }
} catch (e) {}
}
}
})();