Adds [<10], [10-99], [100-999], [>1000], Not dungeon and D1-4 filter buttons to Collections
目前為
// ==UserScript==
// @name Filter Collections
// @namespace http://tampermonkey.net/
// @version 2025-12-06-2
// @description Adds [<10], [10-99], [100-999], [>1000], Not dungeon and D1-4 filter buttons to Collections
// @license MIT
// @author sentientmilk
// @match https://www.milkywayidle.com/*
// @icon https://www.milkywayidle.com/favicon.svg
// @grant none
// ==/UserScript==
/*
Changelog
=========
v2025-12-06
- Initial version
v2025-12-06-2
- FIXED: "<10" button was showing 10s
TODO
====================
*/
(function() {
async function waitFnRepeatedFor (cond, callback) {
let notified = false;
return new Promise((resolve) => {
function check () {
const r = cond();
setTimeout(check, 1000/30); // Schedule first to allow the callback to throw
if (r && !notified) {
notified = true;
resolve();
if (callback) {
callback();
}
} else if (r && notified) {
// Skip, wait for cond to be false again
} else {
notified = false;
}
}
check();
});
}
function unformatNumber (s) {
if (s.endsWith("M")) {
return parseFloat(s) * 1000 * 1000;
} else if (s.endsWith("K")) {
return parseFloat(s) * 1000;
} else if (("" + parseFloat(s)) == s) {
return parseFloat(s);
}
}
const dungeonsItems = {
"d1": new Set([
"chimerical_chest",
"chimerical_refinement_chest",
"chimerical_token",
"chimerical_quiver",
"chimerical_quiver_refined",
"griffin_leather",
"manticore_sting",
"jackalope_antler",
"dodocamel_plume",
"griffin_talon",
"chimerical_refinement_shard",
"chimerical_essence",
"shield_bash",
"crippling_slash",
"pestilent_shot",
"griffin_tunic",
"griffin_chaps",
"manticore_shield",
"jackalope_staff",
"dodocamel_gauntlets",
"griffin_bulwark",
]),
"d2": new Set([
"sinister_chest",
"sinister_refinement_chest",
"sinister_token",
"sinister_cape",
"sinister_cape_refined",
"acrobats_ribbon",
"magicians_cloth",
"chaotic_chain",
"cursed_ball",
"sinister_refinement_shard",
"sinister_essence",
"penetrating_strike",
"pestilent_shot",
"smoke_burst",
"acrobatic_hood",
"magicians_hat",
"chaotic_flail",
"cursed_bow",
]),
"d3": new Set([
"enchanted_chest",
"enchanted_refinement_chest",
"enchanted_token",
"enchanted_cloak",
"enchanted_cloak_refined",
"royal_cloth",
"knights_ingot",
"bishops_scroll",
"regal_jewel",
"sundering_jewel",
"enchanted_refinement_shard",
"enchanted_essence",
"crippling_slash",
"penetrating_shot",
"retribution",
"mana_spring",
"knights_aegis",
"bishops_codex",
"royal_water_robe_top",
"royal_water_robe_bottoms",
"royal_nature_robe_top",
"royal_nature_robe_bottoms",
"royal_fire_robe_top",
"royal_fire_robe_bottoms",
"furious_spear",
"regal_sword",
"sundering_crossbow",
]),
"d4": new Set([
"pirate_chest",
"pirate_refinement_chest",
"pirate_token",
"marksman_brooch",
"corsair_crest",
"damaged_anchor",
"maelstrom_plating",
"kraken_leather",
"kraken_fang",
"pirate_refinement_shard",
"pirate_essence",
"shield_bash",
"fracturing_impact",
"life_drain",
"marksman_bracers",
"corsair_helmet",
"anchorbound_plate_body",
"anchorbound_plate_legs",
"maelstrom_plate_body",
"maelstrom_plate_legs",
"kraken_tunic",
"kraken_chaps",
"rippling_trident",
"blooming_trident",
"blazing_trident",
]),
};
function checkbox ({ label, className, checked }) {
return `<div class="AchievementsPanel_checkboxControl__3e6CJ ${className}">
<label class="MuiFormControlLabel-root MuiFormControlLabel-labelPlacementEnd Checkbox_checkbox__dP0DH css-1jaw3da">
<span class="MuiButtonBase-root MuiCheckbox-root MuiCheckbox-colorPrimary MuiCheckbox-sizeSmall PrivateSwitchBase-root MuiCheckbox-root MuiCheckbox-colorPrimary MuiCheckbox-sizeSmall ${checked ? "Mui-checked" : ""} MuiCheckbox-root MuiCheckbox-colorPrimary MuiCheckbox-sizeSmall css-zun73v">
<input class="PrivateSwitchBase-input css-1m9pwf3" type="checkbox" data-indeterminate="false">`
+ (checked ? `<svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeSmall css-1k33q06" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="CheckBoxIcon">
<path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"></path>
</svg>` : "")
+ (!checked ? `<svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeSmall css-1k33q06" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="CheckBoxOutlineBlankIcon">
<path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"></path>
</svg>` : "")
+ `</span>
<span class="MuiTypography-root MuiTypography-body1 MuiFormControlLabel-label css-9l3uo3">${label}</span>
</label>
</div>`;
}
let flags = [
{ label: "<10", className: "c10", from: 0, to: 9, checked: true },
{ label: "10-99", className: "c99", from: 10, to: 99, checked: true },
{ label: "100-999", className: "c999", from: 100, to: 999, checked: true },
{ label: "1000+", className: "c1000", from: 1000, to: Infinity, checked: true },
{ label: "Not dungeon", className: "nod", checked: true },
{ label: "D1", className: "d1", dungeon: "d1", checked: true },
{ label: "D2", className: "d2", dungeon: "d2", checked: true },
{ label: "D3", className: "d3", dungeon: "d3", checked: true },
{ label: "D4", className: "d4", dungeon: "d4", checked: true },
];
function rerenderCollectionsFiltering (panelEl) {
const catsEl = panelEl.parentElement.querySelector(".AchievementsPanel_categories__34hno");
catsEl.querySelectorAll(".Collection_collectionContainer__3ZlUO").forEach((el) => {
const n = unformatNumber(el.querySelector(".Collection_count__3oj-t")?.textContent ?? "0");
const f = flags.find((f) => (f.from || f.to) && f.from <= n && n <= f.to);
el.dataset.count = f.className;
for (let d in dungeonsItems) {
const itemId = el.querySelector("use").getAttribute("href").split("#")[1];
if (dungeonsItems[d].has(itemId)) {
el.dataset.dungeon = d;
}
}
if (!el.dataset.dungeon) {
el.dataset.dungeon = "nod";
}
});
let containerEl = panelEl.querySelector(".ucf-userscript");
if (!containerEl) {
panelEl.insertAdjacentHTML("beforeend", `<div class="ucf-userscript" style="display: flex"></div>`);
containerEl = panelEl.querySelector(".ucf-userscript");
}
function updateShowClass (f) {
if (f.checked) {
catsEl.classList.add(`show-${f.className}-ucf-userscript`);
} else {
catsEl.classList.remove(`show-${f.className}-ucf-userscript`);
}
}
containerEl.innerHTML = flags.map(checkbox).join("");
flags.forEach((f) => {
containerEl.querySelector("." + f.className).onclick = () => {
f.checked = !f.checked;
rerenderCollectionsFiltering(panelEl);
};
updateShowClass(f);
});
catsEl.classList.add("ucf-userscript");
}
function isCollectionsOnScreen () {
return document.querySelector(".TabPanel_tabPanel__tXMJF:not(.TabPanel_hidden__26UM3) .AchievementsPanel_collections__qA6CY .Collection_collectionContainer__3ZlUO");
}
function addCollectionsFiltering () {
const panelEl = document.querySelector(".TabPanel_tabPanel__tXMJF:not(.TabPanel_hidden__26UM3) .AchievementsPanel_collections__qA6CY .AchievementsPanel_controls__3bGFT");
if (!panelEl.classList.contains("ucf-userscript")) {
rerenderCollectionsFiltering(panelEl);
panelEl.querySelector(".AchievementsPanel_refreshButton__3RYCh").onclick = () => {
setTimeout(() => {
rerenderCollectionsFiltering(panelEl);
}, 500);
};
panelEl.parentElement.querySelector(".AchievementsPanel_controls__3bGFT > .AchievementsPanel_checkboxControl__3e6CJ").onclick = () => {
requestAnimationFrame(() => {
rerenderCollectionsFiltering(panelEl);
});
};
}
}
document.body.insertAdjacentHTML("beforeend", `<style class="ucf-userscript">`
+ `.AchievementsPanel_categories__34hno.ucf-userscript .Collection_collectionContainer__3ZlUO { display: none; }\n`
+ flags.map((f) => {
return ["nod", ...Object.keys(dungeonsItems)].map((d) => {
return `.AchievementsPanel_categories__34hno.ucf-userscript.show-${f.className}-ucf-userscript.show-${d}-ucf-userscript .Collection_collectionContainer__3ZlUO[data-count="${f.className}"][data-dungeon="${d}"] { display: initial; }\n`
}).join("");
}).join("")
+ ` </style>`);
waitFnRepeatedFor(isCollectionsOnScreen, addCollectionsFiltering);
})();