From c641bda6ddbda42929ebf89dd1609f9f95df2400 Mon Sep 17 00:00:00 2001 From: SwingTheVine Date: Thu, 24 Jul 2025 05:49:08 -0400 Subject: [PATCH] Added font stack and next level status --- dist/BlueMarble.user.js | 4 ++-- docs/README.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- src/BlueMarble.meta.js | 2 +- src/apiHandler.js | 7 ++++++- src/main.js | 8 ++++++++ src/overlay.css | 16 ++++++++++++++-- src/overlay.js | 7 +++++++ 9 files changed, 42 insertions(+), 10 deletions(-) diff --git a/dist/BlueMarble.user.js b/dist/BlueMarble.user.js index df9eed3..733cdcf 100644 --- a/dist/BlueMarble.user.js +++ b/dist/BlueMarble.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Blue Marble // @namespace https://github.com/SwingTheVine/ -// @version 0.20.0 +// @version 0.20.5 // @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 @@ -20,4 +20,4 @@ // Wplace --> https://wplace.live // License --> https://www.mozilla.org/en-US/MPL/2.0/ -(()=>{var l=class{constructor(){}create(){let e=document.createElement("div");e.id="bm-overlay",e.style.top="10px",e.style.right="75px";let o=document.createElement("div"),t=document.createElement("div");t.id="bm-bar-drag",o.appendChild(t);let c=document.createElement("img");c.src="https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/src/assets/Favicon.png",c.alt="Blue Marble Icon",o.appendChild(c);let s=document.createElement("h1");s.textContent="Blue Marble",o.appendChild(s);let n=document.createElement("div"),d=document.createElement("p");d.id="bm-user-name",d.textContent="Username:",n.appendChild(d);let r=document.createElement("p");r.id="bm-user-droplets",r.textContent="Droplets:",n.appendChild(r),e.appendChild(o),e.appendChild(document.createElement("hr")),e.appendChild(n),document.body.appendChild(e),this.handleDrag(e,t)}handleDrag(e,o){let t=!1,c,s=0;o.addEventListener("mousedown",function(n){t=!0,c=n.clientX-e.getBoundingClientRect().left,s=n.clientY-e.getBoundingClientRect().top,document.body.style.userSelect="none",e.style.right="",o.classList.add("dragging")}),o.addEventListener("touchstart",function(n){t=!0;let d=n?.touches?.[0];d&&(c=d.clientX-e.getBoundingClientRect().left,s=d.clientY-e.getBoundingClientRect().top,document.body.style.userSelect="none",e.style.right="",o.classList.add("dragging"))},{passive:!1}),document.addEventListener("mousemove",function(n){t&&(e.style.left=n.clientX-c+"px",e.style.top=n.clientY-s+"px")}),document.addEventListener("touchmove",function(n){if(t){let d=n?.touches?.[0];if(!d)return;e.style.left=d.clientX-c+"px",e.style.top=d.clientY-s+"px",n.preventDefault()}},{passive:!1}),document.addEventListener("mouseup",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")}),document.addEventListener("touchend",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")}),document.addEventListener("touchcancel",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")})}};var a=class{constructor(){this.disableAll=!1}spontaneousResponseListener(e){window.addEventListener("message",o=>{let t=o.data;if(t&&t.source==="blue-marble")switch(t.endpoint){case"me":let c=document.getElementById("bm-user-name"),s=document.getElementById("bm-user-droplets");c&&(c.textContent=`Username: ${t.jsonData?.name}`),s&&(s.textContent=`Droplets: ${t.jsonData?.droplets}`);break;case"robots":this.disableAll=t.jsonData?.userscript?.toString().toLowerCase()=="false"}})}};function p(i){let e=document.createElement("script");e.textContent=`(${i})();`,document.documentElement.appendChild(e),e.remove()}p(()=>{let i=window.fetch;window.fetch=async function(...e){let o=await i.apply(this,e),t=o.clone();if((t.headers.get("content-type")||"").includes("application/json")){let s=(e[0]instanceof Request?e[0]?.url:e[0])||"ignore";s=s.split("/").filter(Boolean).pop()||"ignore",console.log(`Sending JSON message about endpoint "${s}"`),t.json().then(n=>{window.postMessage({source:"blue-marble",endpoint:s,jsonData:n},"*")}).catch(n=>{console.error("BM - Failed to parse JSON:",n)})}return o}});var m=GM_getResourceText("CSS-Overlay");GM_addStyle(m);var u=new l;u.create();var g=new a;g.spontaneousResponseListener(u);console.log("Blue Marble userscript has loaded!");})(); +(()=>{var d=class{constructor(){}create(){let e=document.createElement("div");e.id="bm-overlay",e.style.top="10px",e.style.right="75px";let o=document.createElement("div");o.id="bm-contain-header";let t=document.createElement("div");t.id="bm-bar-drag",o.appendChild(t);let c=document.createElement("img");c.src="https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/src/assets/Favicon.png",c.alt="Blue Marble Icon",o.appendChild(c);let s=document.createElement("h1");s.textContent="Blue Marble",o.appendChild(s);let n=document.createElement("div");n.id="bm-contain-userinfo";let l=document.createElement("p");l.id="bm-user-name",l.textContent="Username:",n.appendChild(l);let u=document.createElement("p");u.id="bm-user-droplets",u.textContent="Droplets:",n.appendChild(u);let m=document.createElement("p");m.id="bm-user-nextlevel",m.textContent="Next level in...",n.appendChild(m),e.appendChild(o),e.appendChild(document.createElement("hr")),e.appendChild(n),document.body.appendChild(e),this.handleDrag(e,t)}handleDrag(e,o){let t=!1,c,s=0;o.addEventListener("mousedown",function(n){t=!0,c=n.clientX-e.getBoundingClientRect().left,s=n.clientY-e.getBoundingClientRect().top,document.body.style.userSelect="none",e.style.right="",o.classList.add("dragging")}),o.addEventListener("touchstart",function(n){t=!0;let l=n?.touches?.[0];l&&(c=l.clientX-e.getBoundingClientRect().left,s=l.clientY-e.getBoundingClientRect().top,document.body.style.userSelect="none",e.style.right="",o.classList.add("dragging"))},{passive:!1}),document.addEventListener("mousemove",function(n){t&&(e.style.left=n.clientX-c+"px",e.style.top=n.clientY-s+"px")}),document.addEventListener("touchmove",function(n){if(t){let l=n?.touches?.[0];if(!l)return;e.style.left=l.clientX-c+"px",e.style.top=l.clientY-s+"px",n.preventDefault()}},{passive:!1}),document.addEventListener("mouseup",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")}),document.addEventListener("touchend",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")}),document.addEventListener("touchcancel",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")})}};var r=class{constructor(){this.disableAll=!1}spontaneousResponseListener(e){window.addEventListener("message",o=>{let t=o.data;if(t&&t.source==="blue-marble")switch(t.endpoint){case"me":let c=document.getElementById("bm-user-name"),s=document.getElementById("bm-user-droplets"),n=document.getElementById("bm-user-nextlevel");if(c&&(c.textContent=`Username: ${t.jsonData?.name}`),s&&(s.textContent=`Droplets: ${new Intl.NumberFormat().format(t.jsonData?.droplets)}`),n){let l=Math.ceil(Math.pow(Math.floor(t.jsonData?.level)*Math.pow(30,.65),1.5384615384615383)-t.jsonData?.pixelsPainted);n.textContent=`Next level in ~${new Intl.NumberFormat().format(l)} pixel${l==1?"":"s"}`}break;case"robots":this.disableAll=t.jsonData?.userscript?.toString().toLowerCase()=="false"}})}};function h(a){let e=document.createElement("script");e.textContent=`(${a})();`,document.documentElement.appendChild(e),e.remove()}h(()=>{let a=window.fetch;window.fetch=async function(...e){let o=await a.apply(this,e),t=o.clone();if((t.headers.get("content-type")||"").includes("application/json")){let s=(e[0]instanceof Request?e[0]?.url:e[0])||"ignore";s=s.split("/").filter(Boolean).pop()||"ignore",console.log(`Sending JSON message about endpoint "${s}"`),t.json().then(n=>{window.postMessage({source:"blue-marble",endpoint:s,jsonData:n},"*")}).catch(n=>{console.error("BM - Failed to parse JSON:",n)})}return o}});var f=GM_getResourceText("CSS-Overlay");GM_addStyle(f);var i=document.createElement("link");i.href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap";i.rel="preload";i.as="style";i.onload="this.onload=null;this.rel='stylesheet'";document.head.appendChild(i);var p=new d;p.create();var g=new r;g.spontaneousResponseListener(p);console.log("Blue Marble userscript has loaded!");})(); diff --git a/docs/README.md b/docs/README.md index aa91077..5baf8a1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,7 +23,7 @@ Software License: MPL-2.0 Contact Me WakaTime -Total Patches +Total Patches Total Lines of Code Total Comments Build diff --git a/package-lock.json b/package-lock.json index c306322..75d64aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "devDependencies": { "esbuild": "^0.25.0" }, - "version": "0.13.1" + "version": "0.20.5" }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.8", @@ -467,5 +467,5 @@ } } }, - "version": "0.13.1" + "version": "0.20.5" } diff --git a/package.json b/package.json index eb8bade..50ddd51 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wplace-bluemarble", - "version": "0.20.0", + "version": "0.20.5", "type": "module", "scripts": { "build": "node build/build.js", diff --git a/src/BlueMarble.meta.js b/src/BlueMarble.meta.js index 06883ae..88f9dae 100644 --- a/src/BlueMarble.meta.js +++ b/src/BlueMarble.meta.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Blue Marble // @namespace https://github.com/SwingTheVine/ -// @version 0.20.0 +// @version 0.20.5 // @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 diff --git a/src/apiHandler.js b/src/apiHandler.js index a131524..f0f89ca 100644 --- a/src/apiHandler.js +++ b/src/apiHandler.js @@ -31,8 +31,13 @@ export class ApiHandler { case 'me': const username = document.getElementById('bm-user-name'); const droplets = document.getElementById('bm-user-droplets'); + const nextLevel = document.getElementById('bm-user-nextlevel'); if (username) {username.textContent = `Username: ${data.jsonData?.name}`;} - if (droplets) {droplets.textContent = `Droplets: ${data.jsonData?.droplets}`;} + if (droplets) {droplets.textContent = `Droplets: ${new Intl.NumberFormat().format(data.jsonData?.droplets)}`;} + if (nextLevel) { + const nextLevelPixels = Math.ceil(Math.pow(Math.floor(data.jsonData?.level) * Math.pow(30, 0.65), (1/0.65)) - data.jsonData?.pixelsPainted); + nextLevel.textContent = `Next level in ~${new Intl.NumberFormat().format(nextLevelPixels)} pixel${nextLevelPixels == 1 ? '' : 's'}`; + } break; case 'robots': this.disableAll = data.jsonData?.userscript?.toString().toLowerCase() == 'false'; // Disables Blue Marble if site owner wants userscripts disabled diff --git a/src/main.js b/src/main.js index d00b4b1..91a49b7 100644 --- a/src/main.js +++ b/src/main.js @@ -60,6 +60,14 @@ inject(() => { const cssOverlay = GM_getResourceText("CSS-Overlay"); GM_addStyle(cssOverlay); +// Imports the Roboto Mono font family +var stylesheetLink = document.createElement('link'); +stylesheetLink.href = 'https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap'; +stylesheetLink.rel = 'preload'; +stylesheetLink.as = 'style'; +stylesheetLink.onload = "this.onload=null;this.rel='stylesheet'"; +document.head.appendChild(stylesheetLink); + const overlay = new Overlay(); // Constructs a new Overlay object overlay.create(); // Deploys the overlay to the page diff --git a/src/overlay.css b/src/overlay.css index cec9611..db7e89c 100644 --- a/src/overlay.css +++ b/src/overlay.css @@ -7,7 +7,16 @@ padding: 10px; border-radius: 8px; z-index: 9000; - font-family: 'sans-serif'; + + /* Font stack is as follows: + * Highest Priority (Roboto Mono) + * Windows fallback (Courier New) + * macOS fallback (Monaco) + * Linux fallback (DejaVu Sans Mono) + * Any possible monospace font (monospace) + * Last resort (Arial) */ + font-family: 'Roboto Mono', 'Courier New', 'Monaco', 'DejaVu Sans Mono', monospace, 'Arial'; + font-weight: bold; } #bm-bar-drag { @@ -23,11 +32,14 @@ cursor: grabbing; } +#bm-contain-header { + margin-bottom: 0.5em; +} + #bm-overlay img { display: inline-block; height: 2.5em; margin-right: 1ch; - margin-bottom: 0.5em; vertical-align: middle; } diff --git a/src/overlay.js b/src/overlay.js index 6487cb5..121141b 100644 --- a/src/overlay.js +++ b/src/overlay.js @@ -22,6 +22,7 @@ export class Overlay { overlay.style.right = '75px'; // Position from right of viewport const containerOverlayHeader = document.createElement('div'); + containerOverlayHeader.id = 'bm-contain-header'; const barDrag = document.createElement('div'); // Drag bar for the overlay barDrag.id = 'bm-bar-drag'; @@ -37,6 +38,7 @@ export class Overlay { containerOverlayHeader.appendChild(barHeader); // Adds the header to the overlay header container const containerUserInfo = document.createElement('div'); // User info container + containerUserInfo.id = 'bm-contain-userinfo'; const userName = document.createElement('p'); // User name field userName.id = 'bm-user-name'; @@ -47,6 +49,11 @@ export class Overlay { userDroplets.id = 'bm-user-droplets'; userDroplets.textContent = 'Droplets:'; containerUserInfo.appendChild(userDroplets); // Adds the droplet field to the user info container + + const userNextLevel = document.createElement('p'); // Amount to next level + userNextLevel.id = 'bm-user-nextlevel'; + userNextLevel.textContent = 'Next level in...'; + containerUserInfo.appendChild(userNextLevel); // Adds the "amount to next level" field to the user info container // Construction of the overlay element overlay.appendChild(containerOverlayHeader); // Adds the overlay header container to the overlay