Added css map for consistent mapping during patches

This commit is contained in:
SwingTheVine 2025-07-29 13:39:26 -04:00
parent d171900fd5
commit 899680ef97
11 changed files with 129 additions and 24 deletions

2
.gitignore vendored
View file

@ -4,8 +4,6 @@
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User specified files
## Generated files
mapCSS.json
# User-specific files
*.rsuser

View file

@ -92,11 +92,32 @@ let resultTerser = await terser.minify(resultEsbuildJS.text, {
// Writes the obfuscated/mangled JS code to a file
fs.writeFileSync('dist/BlueMarble.user.js', resultTerser.code, 'utf8');
let importedMapCSS = {}; // The imported CSS map
// Only import a CSS map if we are NOT in production (GitHub Workflow)
// Theoretically, if the previous map is always imported, the names would not scramble. However, the names would never decrease in number...
if (isGitHub) {
try {
importedMapCSS = JSON.parse(fs.readFileSync('dist/BlueMarble.user.css.map.json', 'utf8'));
} catch {
console.log(`${consoleStyle.YELLOW}Warning! Could not find a CSS map to import. A 100% new CSS map will be generated...${consoleStyle.RESET}`);
}
}
// Mangles the CSS selectors
// If we are NOT in production (GitHub Workflow), then generate the CSS mapping
const mapCSS = mangleSelectors('bm-', 'bm-', 'dist/BlueMarble.user.js', 'dist/BlueMarble.user.css', !isGitHub);
// If we are in production (GitHub Workflow), then generate the CSS mapping
const mapCSS = mangleSelectors({
inputPrefix: 'bm-',
outputPrefix: 'bm-',
pathJS: 'dist/BlueMarble.user.js',
pathCSS: 'dist/BlueMarble.user.css',
importMap: importedMapCSS,
returnMap: isGitHub
});
// If a map was returned, write it to the file
if (mapCSS) {
fs.writeFileSync('dist/mapCSS.json', JSON.stringify(mapCSS, null, 2));
fs.writeFileSync('dist/BlueMarble.user.css.map.json', JSON.stringify(mapCSS, null, 2));
}
// Adds the banner

View file

@ -47,12 +47,15 @@ import fs from 'fs';
*
* The default mangling is all valid single byte characters for CSS selectors (which is, ironically, 64 characters).
* You can optionally return the key-value mapping of all selector names.
* @param {string} inputPrefix - The prefix to search for.
* @param {string} outputPrefix - The prefix to replace with.
* @param {string} pathJS - The path to the JS file.
* @param {string} pathCSS - The path to the CSS file.
* @param {boolean} [returnMap=false] - Should this function return the key-value map Object?
* @param {string} [encoding=''] - The characters you want the mangled selectors to consist of.
* You can optionally pass in a map to be used. If you do, any selectors not in the map will be added.
* @param {Object} params - The parameters object.
* @param {string} params.inputPrefix - The prefix to search for.
* @param {string} params.outputPrefix - The prefix to replace with.
* @param {string} params.pathJS - The path to the JS file.
* @param {string} params.pathCSS - The path to the CSS file.
* @param {Object<string, string>} [params.importMap={}] - Imported map to use.
* @param {boolean} [params.returnMap=false] - Should this function return the key-value map Object?
* @param {string} [params.encoding=''] - The characters you want the mangled selectors to consist of.
* @returns {Object<string, string>|undefined} A mapping of the mangled CSS selectors as an Object, or `undefined` if `returnMap` is not `true`.
* @since 0.56.1
* @example
@ -75,19 +78,63 @@ import fs from 'fs';
* #b-1 {color:red;}
* .b-0 {background-color:blue;}
* // Optional returned map Object:
* console.log(JSON.stringify(mangleSelectors('bm-', 'b-', 'bundled.js', 'bundled.css', true), null, 2));
* console.log(
* JSON.stringify(
* mangleSelectors({
* inputPrefix: 'bm-',
* outputPrefix: 'b-',
* pathJS: 'bundled.js',
* pathCSS: 'bundled.css',
* returnMap: true
* }),
* null, 2
* )
* );
* // Return Map:
* {
* "bm-paragraph-class": "b-0",
* "bm-paragraph-id": "b-1",
* }
*/
export default function mangleSelectors(inputPrefix, outputPrefix, pathJS, pathCSS, returnMap=false, encoding='') {
export default function mangleSelectors({
inputPrefix = '',
outputPrefix = '',
pathJS = '',
pathCSS = '',
importMap = {},
returnMap = false,
encoding = ''
} = {}) {
if (!inputPrefix || !outputPrefix || !pathJS || !pathCSS) {
throw new Error(`mangleSelectors() was called without the required variables: ${!inputPrefix ? 'inputPrefix ' : ''}${!outputPrefix ? 'outputPrefix ' : ''}${!pathJS ? 'pathJS ' : ''}${!pathCSS ? 'pathCSS' : ''}`);
}
encoding = encoding || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'; // Default encoding
const fileInputJS = fs.readFileSync(pathJS, 'utf8'); // The JS file
const fileInputCSS = fs.readFileSync(pathCSS, 'utf8'); // The CSS file
/** How many keys-value pairs there are in the in the imported map, along with an "index shift" if a map was imported.
* @example
* // Assume the imported map has 10 keys.
* // The first key of the new map has an index of 0.
* // If we add 10 + 0, we get an index collision.
* // Therefore, we add 10 + 2 + 0.
* // However, if no map is imported, the map will start it's index at 2. We don't want that.
* // Therefore, if we abuse the fact that `true` is `1`, and `false` is `0`, we can add `2 * !!importMap.length` which will only shift the index by 2 if a map was imported.
* @example
* const importMap = {};
* console.log(importMapLength); // 0
* @example
* const importMap = {'foo': 'bar'};
* console.log(importMapLength); // 3
* @example
* const importMap = {'foo': 'bar', 'bar': 'foo'};
* console.log(importMapLength); // 4
*/
const importMapLength = Object.keys(importMap).length + (2 * !!Object.keys(importMap).length);
// One of each of all matching selectors
// File -> RegEx -> Array (Duplicates) -> Set (Unique) -> Array (Unique)
let matchedSelectors = [...new Set([...fileInputJS.matchAll(new RegExp(`\\b${escapeRegex(inputPrefix)}[a-zA-Z0-9_-]+`, 'g'))].map(match => match[0]))];
@ -98,8 +145,16 @@ export default function mangleSelectors(inputPrefix, outputPrefix, pathJS, pathC
matchedSelectors.sort((a, b) => b.length - a.length);
// Converts the string[] to an Object (key-value)
matchedSelectors = Object.fromEntries(matchedSelectors.map((key, value) => [key, outputPrefix + numberToEncoded(matchedSelectors.indexOf(key), encoding)]));
matchedSelectors = {
...importMap,
...Object.fromEntries(
matchedSelectors
.filter(key => !(key in importMap))
.map(key => [key, outputPrefix + numberToEncoded(importMapLength + matchedSelectors.indexOf(key), encoding)]
)
)
};
// Compile the RegEx from the selector map
const regex = new RegExp(Object.keys(matchedSelectors).map(selector => escapeRegex(selector)).join('|'), 'g');

View file

@ -1 +1 @@
#bm-p{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000}div#bm-p{font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-k{margin-bottom:.5em;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue" /></svg>') repeat;cursor:grab;width:100%;height:1em}#bm-k.dragging{cursor:grabbing}#bm-b{margin-bottom:.5em}#bm-p img{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle}#bm-p h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}#bm-3 input[type=checkbox]{vertical-align:middle;margin-right:.5ch}#bm-3 label{margin-right:.5ch}.bm-s{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}#bm-h{vertical-align:middle}#bm-h svg{width:50%;margin:0 auto;fill:#111}div>#bm-7{display:flex;gap:.5ch}#bm-8 svg,#bm-button-template svg{height:1em;margin:2px auto 0;text-align:center;line-height:1em;vertical-align:bottom}#bm-c input[type=number]{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}#bm-c input[type=number]::-webkit-outer-spin-button,#bm-c input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}#bm-0{display:flex;flex-direction:row;flex-wrap:wrap;align-content:center;justify-content:center;align-items:center;gap:1ch}div:has(>#bm-2)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#bm-e{font-size:small;background-color:#0003;padding:0 .5ch;height:3.75em;width:100%}#bm-1{display:flex;justify-content:space-between}#bm-p small{font-size:x-small;color:#d3d3d3}#bm-4,#bm-3,#bm-c,#bm-0,div:has(>#bm-2),#bm-e{margin-top:.5em}#bm-p button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}#bm-p button:hover,#bm-p button:focus-visible{background-color:#1061e5}#bm-p button:active,#bm-p button:disabled{background-color:#2e97ff}#bm-p button:disabled{text-decoration:line-through}
#bm-p{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000}div#bm-p{font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-k{margin-bottom:.5em;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue" /></svg>') repeat;cursor:grab;width:100%;height:1em}#bm-k.dragging{cursor:grabbing}#bm-b{margin-bottom:.5em}#bm-p img{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle}#bm-p h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}#bm-3 input[type=checkbox]{vertical-align:middle;margin-right:.5ch}#bm-3 label{margin-right:.5ch}.bm-s{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}#bm-h{vertical-align:middle}#bm-h svg{width:50%;margin:0 auto;fill:#111}div:has(>#bm-7){display:flex;gap:.5ch}#bm-8 svg,#bm-button-template svg{height:1em;margin:2px auto 0;text-align:center;line-height:1em;vertical-align:bottom}#bm-c input[type=number]{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}#bm-c input[type=number]::-webkit-outer-spin-button,#bm-c input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}#bm-0{display:flex;flex-direction:row;flex-wrap:wrap;align-content:center;justify-content:center;align-items:center;gap:1ch}div:has(>#bm-2)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#bm-e{font-size:small;background-color:#0003;padding:0 .5ch;height:3.75em;width:100%}#bm-1{display:flex;justify-content:space-between}#bm-p small{font-size:x-small;color:#d3d3d3}#bm-4,#bm-3,#bm-c,#bm-0,div:has(>#bm-2),#bm-e{margin-top:.5em}#bm-p button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}#bm-p button:hover,#bm-p button:focus-visible{background-color:#1061e5}#bm-p button:active,#bm-p button:disabled{background-color:#2e97ff}#bm-p button:disabled{text-decoration:line-through}

31
dist/BlueMarble.user.css.map.json vendored Normal file
View file

@ -0,0 +1,31 @@
{
"bm-contain-buttons-template": "bm-0",
"bm-contain-buttons-action": "bm-1",
"bm-input-file-template": "bm-2",
"bm-contain-automation": "bm-3",
"bm-contain-userinfo": "bm-4",
"bm-button-templates": "bm-5",
"bm-input-possessed": "bm-6",
"bm-button-teleport": "bm-7",
"bm-button-favorite": "bm-8",
"bm-display-coords": "bm-9",
"bm-user-nextlevel": "bm-a",
"bm-contain-header": "bm-b",
"bm-contain-coords": "bm-c",
"bm-button-disable": "bm-d",
"bm-output-status": "bm-e",
"bm-user-droplets": "bm-f",
"bm-input-stealth": "bm-g",
"bm-button-coords": "bm-h",
"bm-button-enable": "bm-i",
"bm-user-name": "bm-j",
"bm-bar-drag": "bm-k",
"bm-input-tx": "bm-l",
"bm-input-ty": "bm-m",
"bm-input-px": "bm-n",
"bm-input-py": "bm-o",
"bm-overlay": "bm-p",
"bm-cStyle": "bm-q",
"bm-name": "bm-r",
"bm-help": "bm-s"
}

File diff suppressed because one or more lines are too long

View file

@ -35,7 +35,7 @@
<a href="https://github.com/SwingTheVine/Wplace-BlueMarble/blob/main/LICENSE.txt" target="_blank"><img alt="Software License: MPL-2.0" src="https://img.shields.io/badge/Software_License-MPL--2.0-brightgreen?style=flat"></a>
<a href="https://discord.gg/tpeBPy46hf" target="_blank"><img alt="Contact Me" src="https://img.shields.io/badge/Contact_Me-gray?style=flat&logo=Discord&logoColor=white&logoSize=auto&labelColor=cornflowerblue"></a>
<a href="" target="_blank"><img alt="WakaTime" src="https://img.shields.io/badge/Coding_Time-35hrs_30mins-blue?style=flat&logo=wakatime&logoColor=black&logoSize=auto&labelColor=white"></a>
<a href="" target="_blank"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-252-black?style=flat"></a>
<a href="" target="_blank"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-260-black?style=flat"></a>
<a href="" target="_blank"><img alt="Total Lines of Code" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=code"></a>
<a href="" target="_blank"><img alt="Total Comments" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=comments"></a>
<a href="" target="_blank"><img alt="Compression" src="https://img.shields.io/badge/Compression-71.76%25-blue"></a>

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "wplace-bluemarble",
"version": "0.58.25",
"version": "0.59.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wplace-bluemarble",
"version": "0.58.25",
"version": "0.59.8",
"devDependencies": {
"esbuild": "^0.25.0",
"terser": "^5.43.1"

View file

@ -1,6 +1,6 @@
{
"name": "wplace-bluemarble",
"version": "0.59.0",
"version": "0.59.8",
"type": "module",
"scripts": {
"build": "node build/build.js",

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name Blue Marble
// @namespace https://github.com/SwingTheVine/
// @version 0.59.0
// @version 0.59.8
// @description A userscript to automate and/or enhance the user experience on Wplace.live. Make sure to comply with the site's Terms of Service, and rules! This script is not affiliated with Wplace.live in any way, use at your own risk. This script is not affiliated with TamperMonkey. The author of this userscript is not responsible for any damages, issues, loss of data, or punishment that may occur as a result of using this script. This script is provided "as is" under the MPL-2.0 license. The "Blue Marble" icon is licensed under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. The image is owned by NASA.
// @author SwingTheVine
// @license MPL-2.0

View file

@ -94,7 +94,7 @@ div#bm-overlay {
}
/* Container for action buttons, that is inside the action button container */
div > #bm-button-teleport {
div:has(> #bm-button-teleport) {
display: flex;
gap: 0.5ch;
}