Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Rongmario/2895b855f6644ce81c4dd307b0ab6a25 to your computer and use it in GitHub Desktop.

Select an option

Save Rongmario/2895b855f6644ce81c4dd307b0ab6a25 to your computer and use it in GitHub Desktop.
Flare Viewer Cleanroom Auto-Remapper
// ==UserScript==
// @name Flare Viewer Cleanroom Auto-Remapper
// @namespace https://cleanroommc.com/
// @version 1.0.0
// @description Auto-select MCP mappings for Flare reports on Cleanroom
// @author CleanroomMC, Rongmario
// @license MIT
// @match https://spark.lucko.me/*
// @updateURL https://gist.github.com/Rongmario/2895b855f6644ce81c4dd307b0ab6a25/raw/flare-viewer-cleanroom-autoremapper.user.js
// @downloadURL https://gist.github.com/Rongmario/2895b855f6644ce81c4dd307b0ab6a25/raw/flare-viewer-cleanroom-autoremapper.user.js
// @grant none
// ==/UserScript==
(function () {
'use strict';
const TARGET_MAPPING = 'mcp-1_12_2';
const LOG = (...args) => console.log('[Flare]', ...args);
function hasCleanroomSpan() {
const detailContent = document.querySelector('.metadata-detail-content');
if (!detailContent) {
return false;
}
const firstParagraph = detailContent.querySelector('p');
if (!firstParagraph) {
return false;
}
const spans = firstParagraph.querySelectorAll('span');
for (const span of spans) {
if (span.textContent.trim() === 'Cleanroom') {
return true;
}
}
return false;
}
function findSelectElement() {
const allSelects = document.querySelectorAll('select');
for (const sel of allSelects) {
if (Array.from(sel.options).some(o => o.value === TARGET_MAPPING)) {
LOG('Found target select via option scan:', sel);
return { el: sel, kind: 'native' };
}
}
return null;
}
function setReactSelectValue(selectEl, value) {
const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLSelectElement.prototype, 'value').set;
nativeSetter.call(selectEl, value);
selectEl.dispatchEvent(new Event('input', { bubbles: true }));
selectEl.dispatchEvent(new Event('change', { bubbles: true }));
}
let applied = false;
let settingsOpened = false;
function findSettingsButton() {
return document.querySelector('.button.textbox[title="Click to show the settings menu"]');
}
function openSettingsMenu() {
if (settingsOpened) {
return;
}
const btn = findSettingsButton();
if (!btn) {
return;
}
LOG('Opening settings menu.');
btn.click();
settingsOpened = true;
}
function closeSettingsMenu() {
const btn = findSettingsButton();
if (!btn) {
return;
}
LOG('Closing settings menu.');
btn.click();
}
function tryApply() {
if (applied) {
return true;
}
if (!hasCleanroomSpan()) {
return false;
}
LOG('Cleanroom detected.');
openSettingsMenu();
const found = findSelectElement();
if (!found) {
LOG('Mappings selector not in DOM yet.');
return false;
}
const { el: selectEl } = found;
const optionExists = Array.from(selectEl.options).some(o => o.value === TARGET_MAPPING);
if (!optionExists) {
LOG('Option', TARGET_MAPPING, 'not present yet. Available:', Array.from(selectEl.options).map(o => o.value));
return false;
}
if (selectEl.value === TARGET_MAPPING) {
LOG('Already set to', TARGET_MAPPING);
applied = true;
return true;
}
setReactSelectValue(selectEl, TARGET_MAPPING);
LOG('Set mappings selector to', TARGET_MAPPING, '— verify current value:', selectEl.value);
setTimeout(() => {
if (selectEl.value === TARGET_MAPPING) {
LOG('Confirmed: value held after React re-render.');
applied = true;
closeSettingsMenu();
} else {
LOG('React reset the value — retrying next mutation.');
}
}, 50);
return selectEl.value === TARGET_MAPPING;
}
let observer = null;
function startObserving() {
if (observer) {
observer.disconnect();
}
observer = new MutationObserver(() => {
if (tryApply()) {
observer.disconnect();
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
function onNavigate() {
applied = false;
settingsOpened = false;
startObserving();
tryApply();
}
function hookNavigation() {
const patch = (original) => function (...args) {
const result = original.apply(this, args);
onNavigate();
return result;
};
history.pushState = patch(history.pushState);
history.replaceState = patch(history.replaceState);
window.addEventListener('popstate', onNavigate);
}
hookNavigation();
startObserving();
tryApply();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment