diff --git a/build/build.js b/build/build.js
index 488e83b..d21237a 100644
--- a/build/build.js
+++ b/build/build.js
@@ -10,6 +10,7 @@
// ES Module imports
import esbuild from 'esbuild';
+import crypto from 'crypto';
import fs from 'fs';
import { execSync } from 'child_process';
import { consoleStyle } from './utils.js';
@@ -22,6 +23,24 @@ const terser = require('terser');
const isGitHub = !!process.env?.GITHUB_ACTIONS; // Is this running in a GitHub Action Workflow?'
+/** Appends a build hash comment to an output file.
+ * The hash is based on the file contents before the hash comment is added.
+ * @param {string} path - Path to the file
+ * @param {'js' | 'css'} type - Output type for comment syntax
+ * @returns {string} The short build hash
+ * @since 0.92.0
+ */
+function appendBuildHashComment(path, type = 'js') {
+ const content = fs.readFileSync(path, 'utf8').trimEnd();
+ const hash = crypto.createHash('sha256').update(content, 'utf8').digest('hex').slice(0, 12);
+ const comment = (type == 'css')
+ ? `/* Build Hash: ${hash} */`
+ : `// Build Hash: ${hash}`;
+
+ fs.writeFileSync(path, `${content}\n\n${comment}\n`, 'utf8');
+ return hash;
+}
+
console.log(`${consoleStyle.BLUE}Starting build...${consoleStyle.RESET}`);
// Tries to build the wiki if build.js is run in a GitHub Workflow
@@ -214,4 +233,16 @@ esbuild.build({
fs.writeFileSync(`dist/${greasyForkName}.user.js`, greasyForkBMjs, 'utf-8');
+const buildHashes = {
+ 'BlueMarble.user.css': appendBuildHashComment('dist/BlueMarble.user.css', 'css'),
+ 'BlueMarble.user.js': appendBuildHashComment('dist/BlueMarble.user.js', 'js'),
+ [`${standaloneName}.user.js`]: appendBuildHashComment(`dist/${standaloneName}.user.js`, 'js'),
+ [`${greasyForkName}.user.css`]: appendBuildHashComment(`dist/${greasyForkName}.user.css`, 'css'),
+ [`${greasyForkName}.user.js`]: appendBuildHashComment(`dist/${greasyForkName}.user.js`, 'js')
+};
+
console.log(`${consoleStyle.GREEN + consoleStyle.BOLD + consoleStyle.UNDERLINE}Building complete!${consoleStyle.RESET}`);
+console.log(`Build hashes:`);
+for (const [file, hash] of Object.entries(buildHashes)) {
+ console.log(`- ${file}: ${hash}`);
+}
diff --git a/dist/BlueMarble-For-GreasyFork.user.css b/dist/BlueMarble-For-GreasyFork.user.css
index bb285a5..6accc48 100644
--- a/dist/BlueMarble-For-GreasyFork.user.css
+++ b/dist/BlueMarble-For-GreasyFork.user.css
@@ -1,192 +1,3 @@
-/* src/WindowFilter.css */
-#bm-window-filter p svg {
- display: inline;
- height: 1em;
- fill: white;
-}
-#bm-filter-flex {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- justify-content: center;
- gap: 1em 3ch;
-}
-#bm-window-filter .bm-filter-color {
- width: fit-content;
- max-width: 35ch;
- background-color: rgba(21, 48, 99, 0.9);
- border-radius: 1em;
- padding: 0.5em;
- gap: 1ch;
- transition: background-color 0.3s ease;
-}
-#bm-window-filter .bm-filter-color:hover,
-#bm-window-filter.bm-filter-color:focus-within {
- background-color: rgba(17, 40, 85, 0.9);
-}
-#bm-window-filter .bm-filter-container-rgb {
- display: block;
- border: thick double darkslategray;
- width: fit-content;
- height: fit-content;
- padding: 1ch;
-}
-#bm-window-filter .bm-filter-color[data-id="-2"] .bm-filter-container-rgb {
- background:
- conic-gradient(
- #aa0000 0%,
- #aaaa00 16.6%,
- #00aa00 33.3%,
- #00aaaa 50%,
- #0000aa 66.6%,
- #aa00aa 83.3%,
- #aa0000 100%);
-}
-#bm-window-filter .bm-filter-color[data-id="-1"] .bm-filter-container-rgb {
- background: url('data:image/svg+xml;utf8,') repeat;
- background-color: transparent !important;
-}
-#bm-window-filter .bm-filter-color[data-id="-1"] .bm-filter-container-rgb svg {
- fill: white !important;
-}
-#bm-window-filter .bm-filter-color[data-id="0"] .bm-filter-container-rgb {
- background-color: transparent !important;
-}
-#bm-window-filter .bm-filter-container-rgb button {
- padding: 0.75em 0.5ch;
-}
-#bm-window-filter .bm-filter-container-rgb svg {
- width: 4ch;
-}
-#bm-window-filter .bm-filter-color > .bm-flex-between {
- flex-direction: column;
- align-items: flex-start;
- gap: 0;
-}
-#bm-window-filter .bm-filter-color small {
- font-size: 0.75em;
-}
-#bm-window-filter .bm-filter-color.bm-color-hide {
- display: none;
-}
-#bm-window-filter.bm-windowed #bm-filter-flex {
- flex-direction: column;
- gap: 0.25em;
-}
-#bm-window-filter.bm-windowed .bm-filter-color {
- width: auto;
- margin: 0;
- padding: 0;
-}
-#bm-window-filter.bm-windowed .bm-filter-container-rgb {
- display: flex;
- width: 100%;
- gap: 0.5ch;
- align-items: center;
- padding: 0.1em 0.5ch;
- border: none;
- border-radius: 1em;
-}
-#bm-window-filter.bm-windowed .bm-filter-container-rgb button {
- padding: 0.5em 0.25ch;
-}
-#bm-window-filter.bm-windowed .bm-filter-container-rgb svg {
- width: 3ch;
-}
-#bm-window-filter.bm-windowed .bm-filter-color h2 {
- font-size: 0.75em;
-}
-#bm-window-filter #bm-filter-windowed-color-totals {
- font-size: 1em;
-}
-
-/* src/WindowSettings.css */
-#bm-window-settings div:has(> .bm-highlight-preset-container) {
- width: fit-content;
- justify-content: flex-start;
-}
-#bm-window-settings .bm-highlight-preset-container {
- display: flex;
- flex-direction: column;
- width: 13%;
-}
-#bm-window-settings .bm-highlight-preset-container span {
- width: fit-content;
- margin: auto;
- font-size: 0.7em;
-}
-#bm-window-settings .bm-highlight-preset-container button {
- width: fit-content;
- padding: 0;
- border-radius: 0;
-}
-#bm-window-settings .bm-highlight-preset-container svg {
- stroke: #333;
- stroke-width: 0.02px;
- width: 100%;
- min-width: 1.5ch;
- max-width: 14.5ch;
-}
-#bm-window-settings .bm-highlight-preset-container button:hover svg,
-#bm-window-settings .bm-highlight-preset-container button:focus svg {
- opacity: 0.9;
-}
-#bm-window-settings .bm-highlight-grid {
- display: grid;
- grid-template-columns: 1fr 1fr 1fr;
- width: 25%;
- min-width: 3ch;
- max-width: 15ch;
-}
-#bm-window-settings .bm-highlight-grid > button {
- width: 100%;
- padding: 0;
- aspect-ratio: 1 / 1;
- background-color: white;
- border: #333 1px solid;
- border-radius: 0;
- box-sizing: border-box;
-}
-#bm-window-settings .bm-highlight-grid > button[data-status=Incorrect] {
- background-color: brown;
-}
-#bm-window-settings .bm-highlight-grid > button[data-status=Template] {
- background-color: darkslategray;
-}
-#bm-window-settings .bm-highlight-grid > button:hover,
-#bm-window-settings .bm-highlight-grid > button:focus {
- opacity: 0.8;
-}
-
-/* src/WindowWizard.css */
-#bm-wizard-tlist {
- display: flex;
- flex-direction: column;
- justify-content: flex-start;
- align-items: flex-start;
-}
-#bm-wizard-tlist > .bm-container {
- width: 100%;
- justify-content: flex-start;
- background-color: rgba(21, 48, 99, 0.9);
- border-radius: 1em;
- padding: 0.5em;
- transition: background-color 0.3s ease;
-}
-#bm-wizard-tlist > .bm-container:hover,
-#bm-wizard-tlist > .bm-container:focus-within {
- background-color: rgba(17, 40, 85, 0.9);
-}
-#bm-wizard-tlist .bm-wizard-template-container-image {
- height: 100%;
- font-size: xxx-large;
-}
-#bm-wizard-tlist .bm-wizard-template-container-flavor {
- flex-direction: column;
- align-items: flex-start;
- gap: 0;
-}
-
/* src/confettiManager.css */
div:has(> confetti-piece) {
position: absolute;
@@ -245,9 +56,6 @@ confetti-piece {
"Arial";
letter-spacing: 0.05em;
}
-.bm-window.bm-windowed {
- max-width: 300px;
-}
.bm-dragbar {
display: grid;
grid-template-columns: auto 1fr auto;
@@ -423,6 +231,7 @@ input[type=file] {
}
.bm-window-content {
overflow: hidden;
+ max-height: calc(100% - 5px);
transition: height 300ms cubic-bezier(.4, 0, .2, 1);
}
.bm-window textarea {
@@ -444,7 +253,7 @@ input[type=file] {
margin-left: 5ch;
}
.bm-window .bm-container.bm-scrollable {
- max-height: calc(80vh - 150px);
+ max-height: var(--bm-scrollable-max-height, calc(80vh - 150px));
overflow: auto;
}
.bm-flex-between {
@@ -476,4 +285,273 @@ input[type=file] {
font-size: 1em;
}
+/* src/WindowFilter.css */
+#bm-window-filter p svg {
+ display: inline;
+ height: 1em;
+ fill: white;
+}
+#bm-filter-flex {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 1em 3ch;
+}
+#bm-window-filter .bm-filter-color {
+ width: fit-content;
+ max-width: 35ch;
+ background-color: rgba(21, 48, 99, 0.9);
+ border-radius: 1em;
+ padding: 0.5em;
+ gap: 1ch;
+ transition: background-color 0.3s ease;
+}
+#bm-window-filter .bm-filter-color:hover,
+#bm-window-filter.bm-filter-color:focus-within {
+ background-color: rgba(17, 40, 85, 0.9);
+}
+#bm-window-filter .bm-filter-container-rgb {
+ display: block;
+ border: thick double darkslategray;
+ width: fit-content;
+ height: fit-content;
+ padding: 1ch;
+}
+#bm-window-filter .bm-filter-color[data-id="-2"] .bm-filter-container-rgb {
+ background:
+ conic-gradient(
+ #aa0000 0%,
+ #aaaa00 16.6%,
+ #00aa00 33.3%,
+ #00aaaa 50%,
+ #0000aa 66.6%,
+ #aa00aa 83.3%,
+ #aa0000 100%);
+}
+#bm-window-filter .bm-filter-color[data-id="-1"] .bm-filter-container-rgb {
+ background: url('data:image/svg+xml;utf8,') repeat;
+ background-color: transparent !important;
+}
+#bm-window-filter .bm-filter-color[data-id="-1"] .bm-filter-container-rgb svg {
+ fill: white !important;
+}
+#bm-window-filter .bm-filter-color[data-id="0"] .bm-filter-container-rgb {
+ background-color: transparent !important;
+}
+#bm-window-filter .bm-filter-container-rgb button {
+ padding: 0.75em 0.5ch;
+}
+#bm-window-filter .bm-filter-container-rgb svg {
+ width: 4ch;
+}
+#bm-window-filter .bm-filter-color > .bm-flex-between {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0;
+}
+#bm-window-filter .bm-filter-color small {
+ font-size: 0.75em;
+}
+#bm-window-filter .bm-filter-color.bm-color-hide {
+ display: none;
+}
+#bm-window-filter.bm-windowed {
+ --bm-scrollable-max-height: 100%;
+ display: grid;
+ grid-template-rows: auto minmax(0, 1fr);
+ width: 300px;
+ height: min(70vh, 32rem);
+ min-width: 260px;
+ min-height: 220px;
+ max-width: min(1000px, calc(100vw - 16px)) !important;
+ max-height: min(1400px, calc(100vh - 16px)) !important;
+ overflow: hidden;
+ box-sizing: border-box;
+ position: fixed;
+ transition: transform 0s;
+}
+#bm-window-filter.bm-windowed .bm-window-content {
+ display: grid;
+ grid-template-rows: auto auto auto minmax(0, 1fr);
+ grid-row: 2;
+ min-height: 0;
+ min-width: 0;
+ overflow: hidden;
+}
+#bm-window-filter.bm-windowed #bm-filter-flex {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 0.25em;
+ width: 100%;
+ align-self: stretch;
+ min-width: 0;
+ box-sizing: border-box;
+}
+#bm-window-filter.bm-windowed .bm-filter-color {
+ width: 100%;
+ max-width: none;
+ align-self: stretch;
+ flex: 1 1 auto;
+ min-width: 0;
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+#bm-window-filter.bm-windowed .bm-filter-color > .bm-flex-between {
+ width: 100%;
+ min-width: 0;
+ flex: 1 1 auto;
+}
+#bm-window-filter.bm-windowed .bm-container.bm-scrollable {
+ display: block;
+ grid-row: 4;
+ min-height: 0;
+ min-width: 0;
+ height: 100%;
+ width: 100%;
+ max-height: 100% !important;
+ overflow: auto;
+ box-sizing: border-box;
+}
+#bm-window-filter.bm-windowed .bm-resize-corner {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ display: block;
+ width: 28px;
+ height: 28px;
+ z-index: 5;
+ cursor: nwse-resize;
+ pointer-events: auto;
+ opacity: 1;
+ touch-action: none;
+ user-select: none;
+ background: transparent;
+ border: none;
+ box-shadow: none;
+}
+#bm-window-filter.bm-windowed .bm-resize-corner:hover,
+#bm-window-filter.bm-windowed .bm-resize-corner.bm-resizing {
+ opacity: 1;
+}
+#bm-window-filter.bm-windowed .bm-filter-container-rgb {
+ display: flex;
+ width: 100%;
+ min-width: 0;
+ flex: 1 1 auto;
+ gap: 0.5ch;
+ align-items: center;
+ padding: 0.1em 0.5ch;
+ border: none;
+ border-radius: 1em;
+ box-sizing: border-box;
+}
+#bm-window-filter.bm-windowed .bm-filter-container-rgb button {
+ padding: 0.5em 0.25ch;
+ flex: 0 0 auto;
+}
+#bm-window-filter.bm-windowed .bm-filter-container-rgb svg {
+ width: 3ch;
+}
+#bm-window-filter.bm-windowed .bm-filter-color h2 {
+ font-size: 0.75em;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+#bm-window-filter #bm-filter-windowed-color-totals {
+ font-size: 1em;
+}
+
+/* src/WindowSettings.css */
+#bm-window-settings div:has(> .bm-highlight-preset-container) {
+ width: fit-content;
+ justify-content: flex-start;
+}
+#bm-window-settings .bm-highlight-preset-container {
+ display: flex;
+ flex-direction: column;
+ width: 13%;
+}
+#bm-window-settings .bm-highlight-preset-container span {
+ width: fit-content;
+ margin: auto;
+ font-size: 0.7em;
+}
+#bm-window-settings .bm-highlight-preset-container button {
+ width: fit-content;
+ padding: 0;
+ border-radius: 0;
+}
+#bm-window-settings .bm-highlight-preset-container svg {
+ stroke: #333;
+ stroke-width: 0.02px;
+ width: 100%;
+ min-width: 1.5ch;
+ max-width: 14.5ch;
+}
+#bm-window-settings .bm-highlight-preset-container button:hover svg,
+#bm-window-settings .bm-highlight-preset-container button:focus svg {
+ opacity: 0.9;
+}
+#bm-window-settings .bm-highlight-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ width: 25%;
+ min-width: 3ch;
+ max-width: 15ch;
+}
+#bm-window-settings .bm-highlight-grid > button {
+ width: 100%;
+ padding: 0;
+ aspect-ratio: 1 / 1;
+ background-color: white;
+ border: #333 1px solid;
+ border-radius: 0;
+ box-sizing: border-box;
+}
+#bm-window-settings .bm-highlight-grid > button[data-status=Incorrect] {
+ background-color: brown;
+}
+#bm-window-settings .bm-highlight-grid > button[data-status=Template] {
+ background-color: darkslategray;
+}
+#bm-window-settings .bm-highlight-grid > button:hover,
+#bm-window-settings .bm-highlight-grid > button:focus {
+ opacity: 0.8;
+}
+
+/* src/WindowWizard.css */
+#bm-wizard-tlist {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: flex-start;
+}
+#bm-wizard-tlist > .bm-container {
+ width: 100%;
+ justify-content: flex-start;
+ background-color: rgba(21, 48, 99, 0.9);
+ border-radius: 1em;
+ padding: 0.5em;
+ transition: background-color 0.3s ease;
+}
+#bm-wizard-tlist > .bm-container:hover,
+#bm-wizard-tlist > .bm-container:focus-within {
+ background-color: rgba(17, 40, 85, 0.9);
+}
+#bm-wizard-tlist .bm-wizard-template-container-image {
+ height: 100%;
+ font-size: xxx-large;
+}
+#bm-wizard-tlist .bm-wizard-template-container-flavor {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0;
+}
+
/* src/main.css */
+
+/* Build Hash: 4336138174a3 */
diff --git a/dist/BlueMarble-For-GreasyFork.user.js b/dist/BlueMarble-For-GreasyFork.user.js
index 6eaa865..3e3c89b 100644
--- a/dist/BlueMarble-For-GreasyFork.user.js
+++ b/dist/BlueMarble-For-GreasyFork.user.js
@@ -2,14 +2,14 @@
// @name Blue Marble
// @name:en Blue Marble
// @namespace https://github.com/SwingTheVine/
-// @version 0.92.0
+// @version 0.92.1
// @description A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @description:en A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @author SwingTheVine
// @license MPL-2.0
// @supportURL https://discord.gg/tpeBPy46hf
// @homepageURL https://bluemarble.lol/
-// @icon https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/78477321232b29c09e3794c360068d7d23a0172c/dist/assets/Favicon.png
+// @icon https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/2cd51bf91944ae2acb253ea5bbd76f79b7a2edd3/dist/assets/Favicon.png
// @updateURL https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/dist/BlueMarble-For-GreasyFork.user.js
// @downloadURL https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/dist/BlueMarble-For-GreasyFork.user.js
// @match https://wplace.live/*
@@ -21,7 +21,7 @@
// @grant GM_xmlhttpRequest
// @grant GM.download
// @connect telemetry.thebluecorner.net
-// @resource CSS-BM-File https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/78477321232b29c09e3794c360068d7d23a0172c/dist/BlueMarble-For-GreasyFork.user.css
+// @resource CSS-BM-File https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/2cd51bf91944ae2acb253ea5bbd76f79b7a2edd3/dist/BlueMarble-For-GreasyFork.user.css
// @antifeature tracking Anonymous opt-in telemetry data
// @noframes
// ==/UserScript==
@@ -1366,9 +1366,11 @@
* @param {string} iMoveThingsSelector - The drag handle element
* @since 0.8.2
*/
- handleDrag(moveMeSelector, iMoveThingsSelector) {
+ handleDrag(moveMeSelector, iMoveThingsSelector, options = {}) {
const moveMe = document.querySelector(moveMeSelector);
const iMoveThings = document.querySelector(iMoveThingsSelector);
+ const onEnd = options?.onEnd ?? (() => {
+ });
if (!moveMe || !iMoveThings) {
this.handleDisplayError(`Can not drag! ${!moveMe ? "moveMe" : ""} ${!moveMe && !iMoveThings ? "and " : ""}${!iMoveThings ? "iMoveThings " : ""}was not found!`);
return;
@@ -1438,6 +1440,12 @@
document.removeEventListener("mouseup", endDrag);
document.removeEventListener("touchend", endDrag);
document.removeEventListener("touchcancel", endDrag);
+ onEnd({
+ element: moveMe,
+ x: currentX,
+ y: currentY
+ });
+ initialRect = null;
};
const onMouseMove = (event) => {
if (isDragging && initialRect) {
@@ -1467,6 +1475,124 @@
event.preventDefault();
}, { passive: false });
}
+ /** Handles resizing of an overlay window from a resize handle.
+ * @param {string} resizeMeSelector - The element to resize
+ * @param {string} iResizeThingsSelector - The resize handle element
+ * @param {{onEnd?: function({element: HTMLElement, width: number, height: number}): void, minWidth?: number, minHeight?: number, maxWidth?: number, maxHeight?: number}} [options={}]
+ * @since 0.92.0
+ */
+ handleResize(resizeMeSelector, iResizeThingsSelector, options = {}) {
+ const resizeMe = document.querySelector(resizeMeSelector);
+ const iResizeThings = document.querySelector(iResizeThingsSelector);
+ const onEnd = options?.onEnd ?? (() => {
+ });
+ if (!resizeMe || !iResizeThings) {
+ this.handleDisplayError(`Can not resize! ${!resizeMe ? "resizeMe" : ""} ${!resizeMe && !iResizeThings ? "and " : ""}${!iResizeThings ? "iResizeThings " : ""}was not found!`);
+ return;
+ }
+ let isResizing = false;
+ let startX = 0;
+ let startY = 0;
+ let startWidth = 0;
+ let startHeight = 0;
+ let currentWidth = 0;
+ let currentHeight = 0;
+ let targetWidth = 0;
+ let targetHeight = 0;
+ let animationFrame = null;
+ const getMaximumWidth = () => Number.isFinite(options?.maxWidth) ? options.maxWidth : window.innerWidth - 16;
+ const getMaximumHeight = () => Number.isFinite(options?.maxHeight) ? options.maxHeight : window.innerHeight - 16;
+ const minimumWidth = Number.isFinite(options?.minWidth) ? options.minWidth : 200;
+ const minimumHeight = Number.isFinite(options?.minHeight) ? options.minHeight : 160;
+ const clamp = (value, minimum, maximum) => Math.min(Math.max(value, minimum), Math.max(minimum, maximum));
+ const updateSize = () => {
+ if (isResizing) {
+ const deltaWidth = Math.abs(currentWidth - targetWidth);
+ const deltaHeight = Math.abs(currentHeight - targetHeight);
+ if (deltaWidth > 0.5 || deltaHeight > 0.5) {
+ currentWidth = targetWidth;
+ currentHeight = targetHeight;
+ resizeMe.style.width = `${currentWidth}px`;
+ resizeMe.style.height = `${currentHeight}px`;
+ }
+ animationFrame = requestAnimationFrame(updateSize);
+ }
+ };
+ const startResize = (clientX, clientY) => {
+ isResizing = true;
+ startX = clientX;
+ startY = clientY;
+ startWidth = resizeMe.offsetWidth;
+ startHeight = resizeMe.offsetHeight;
+ currentWidth = startWidth;
+ currentHeight = startHeight;
+ targetWidth = startWidth;
+ targetHeight = startHeight;
+ document.body.style.userSelect = "none";
+ iResizeThings.classList.add("bm-resizing");
+ document.addEventListener("mousemove", onMouseMove);
+ document.addEventListener("touchmove", onTouchMove, { passive: false });
+ document.addEventListener("mouseup", endResize);
+ document.addEventListener("touchend", endResize);
+ document.addEventListener("touchcancel", endResize);
+ if (animationFrame) {
+ cancelAnimationFrame(animationFrame);
+ }
+ updateSize();
+ };
+ const endResize = () => {
+ isResizing = false;
+ if (animationFrame) {
+ cancelAnimationFrame(animationFrame);
+ animationFrame = null;
+ }
+ document.body.style.userSelect = "";
+ iResizeThings.classList.remove("bm-resizing");
+ document.removeEventListener("mousemove", onMouseMove);
+ document.removeEventListener("touchmove", onTouchMove);
+ document.removeEventListener("mouseup", endResize);
+ document.removeEventListener("touchend", endResize);
+ document.removeEventListener("touchcancel", endResize);
+ onEnd({
+ element: resizeMe,
+ width: currentWidth,
+ height: currentHeight
+ });
+ };
+ const onMouseMove = (event) => {
+ if (!isResizing) {
+ return;
+ }
+ targetWidth = clamp(startWidth + (event.clientX - startX), minimumWidth, getMaximumWidth());
+ targetHeight = clamp(startHeight + (event.clientY - startY), minimumHeight, getMaximumHeight());
+ };
+ const onTouchMove = (event) => {
+ if (!isResizing) {
+ return;
+ }
+ const touch = event?.touches?.[0];
+ if (!touch) {
+ return;
+ }
+ targetWidth = clamp(startWidth + (touch.clientX - startX), minimumWidth, getMaximumWidth());
+ targetHeight = clamp(startHeight + (touch.clientY - startY), minimumHeight, getMaximumHeight());
+ event.preventDefault();
+ };
+ iResizeThings.addEventListener("mousedown", (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ startResize(event.clientX, event.clientY);
+ });
+ iResizeThings.addEventListener("touchstart", (event) => {
+ const touch = event?.touches?.[0];
+ if (!touch) {
+ return;
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ startResize(touch.clientX, touch.clientY);
+ }, { passive: false });
+ }
/** Handles status display.
* This will output plain text into the output Status box.
* Additionally, this will output an info message to the console.
@@ -1637,15 +1763,28 @@
* @since 0.91.39
*/
async updateUserStorage() {
+ await this.saveUserStorage();
+ }
+ /** Saves the user settings in userscript storage.
+ * @param {boolean} [force=false] - Should the throttle be ignored?
+ * @since 0.92.0
+ */
+ async saveUserStorage(force = false) {
const userSettingsCurrent = JSON.stringify(this.userSettings);
const userSettingsOld = JSON.stringify(this.userSettingsOld);
- if (userSettingsCurrent != userSettingsOld && Date.now() - this.lastUpdateTime > this.updateFrequency) {
+ if (userSettingsCurrent != userSettingsOld && (force || Date.now() - this.lastUpdateTime > this.updateFrequency)) {
await GM.setValue(this.userSettingsSaveLocation, userSettingsCurrent);
this.userSettingsOld = structuredClone(this.userSettings);
this.lastUpdateTime = Date.now();
console.log(userSettingsCurrent);
}
}
+ /** Immediately saves the user settings in userscript storage.
+ * @since 0.92.0
+ */
+ async saveUserStorageNow() {
+ await this.saveUserStorage(true);
+ }
/** Toggles a boolean flag to the state that was passed in.
* If no state was passed in, the flag will flip to the opposite state.
* The existence of the flag determines its state. If it exists, it is `true`.
@@ -2219,7 +2358,7 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
};
// src/WindowFilter.js
- var _WindowFilter_instances, buildColorList_fn, sortColorList_fn, selectColorList_fn, calculatePixelStatistics_fn;
+ var _WindowFilter_instances, getWindowState_fn, setWindowModePreference_fn, closeWindow_fn, cleanupWindowPersistence_fn, clampWindowDimension_fn, clampWindowPosition_fn, restoreWindowState_fn, saveWindowState_fn, scheduleWindowStateSave_fn, initializeWindowedPersistence_fn, buildColorList_fn, sortColorList_fn, selectColorList_fn, calculatePixelStatistics_fn;
var WindowFilter = class extends Overlay {
/** Constructor for the color filter window
* @param {*} executor - The executing class
@@ -2233,6 +2372,16 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
this.windowID = "bm-window-filter";
this.colorListID = "bm-filter-flex";
this.windowParent = document.body;
+ this.settingsManager = executor.settingsManager ?? null;
+ this.windowModeFlag = "ftr-oWin";
+ this.windowStateKey = "windowFilter";
+ this.windowResizeObserver = null;
+ this.windowViewportResizeHandler = null;
+ this.windowSaveTimeout = null;
+ this.windowMinWidth = 260;
+ this.windowMinHeight = 220;
+ this.windowMaxWidth = 1e3;
+ this.windowMaxHeight = 1400;
this.templateManager = executor.apiManager?.templateManager;
this.eyeOpen = '';
this.eyeClosed = '';
@@ -2250,6 +2399,16 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
this.sortSecondary = "ascending";
this.showUnused = false;
}
+ /** Builds the preferred filter window mode for the user.
+ * @since 0.92.0
+ */
+ buildPreferredWindow() {
+ if (this.settingsManager?.userSettings?.flags?.includes(this.windowModeFlag)) {
+ this.buildWindowed();
+ return;
+ }
+ this.buildWindow();
+ }
/** Spawns a Color Filter window.
* If another color filter window already exists, we DON'T spawn another!
* Parent/child relationships in the DOM structure below are indicated by indentation.
@@ -2257,7 +2416,7 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
*/
buildWindow() {
if (document.querySelector(`#${this.windowID}`)) {
- document.querySelector(`#${this.windowID}`).remove();
+ __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
return;
}
this.window = this.addDiv({ "id": this.windowID, "class": "bm-window" }, (instance, div) => {
@@ -2268,16 +2427,15 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
};
}).buildElement().addDiv().buildElement().addDiv({ "class": "bm-flex-center" }).addButton({ "class": "bm-button-circle", "textContent": "\u{1F5D7}", "aria-label": 'Switch to windowed mode for "Color Filter"' }, (instance, button) => {
button.onclick = () => {
- document.querySelector(`#${this.windowID}`)?.remove();
+ __privateMethod(this, _WindowFilter_instances, setWindowModePreference_fn).call(this, true);
+ __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
this.buildWindowed();
};
button.ontouchend = () => {
button.click();
};
}).buildElement().addButton({ "class": "bm-button-circle", "textContent": "\u2716", "aria-label": 'Close window "Color Filter"' }, (instance, button) => {
- button.onclick = () => {
- document.querySelector(`#${this.windowID}`)?.remove();
- };
+ button.onclick = () => __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
button.ontouchend = () => {
button.click();
};
@@ -2320,10 +2478,14 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
*/
buildWindowed() {
if (document.querySelector(`#${this.windowID}`)) {
- document.querySelector(`#${this.windowID}`).remove();
+ __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
return;
}
- this.window = this.addDiv({ "id": this.windowID, "class": "bm-window bm-windowed" }).addDragbar().addButton({ "class": "bm-button-circle", "textContent": "\u25BC", "aria-label": 'Minimize window "Color Filter"', "data-button-status": "expanded" }, (instance, button) => {
+ this.window = this.addDiv({
+ "id": this.windowID,
+ "class": "bm-window bm-windowed",
+ "style": `width: 300px; height: min(70vh, 32rem); min-width: ${this.windowMinWidth}px; min-height: ${this.windowMinHeight}px; max-width: min(${this.windowMaxWidth}px, calc(100vw - 16px)); max-height: min(${this.windowMaxHeight}px, calc(100vh - 16px));`
+ }).addDragbar().addButton({ "class": "bm-button-circle", "textContent": "\u25BC", "aria-label": 'Minimize window "Color Filter"', "data-button-status": "expanded" }, (instance, button) => {
button.onclick = () => {
const windowedColorTotals = document.querySelector("#bm-filter-windowed-color-totals");
if (windowedColorTotals) {
@@ -2336,16 +2498,15 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
};
}).buildElement().addDiv().addSpan({ "id": "bm-filter-windowed-color-totals", "class": "bm-dragbar-text", "style": "font-weight: 700;" }).buildElement().buildElement().addDiv({ "class": "bm-flex-center" }).addButton({ "class": "bm-button-circle", "textContent": "\u{1F5D6}", "aria-label": 'Switch to fullscreen mode for "Color Filter"' }, (instance, button) => {
button.onclick = () => {
- document.querySelector(`#${this.windowID}`)?.remove();
+ __privateMethod(this, _WindowFilter_instances, setWindowModePreference_fn).call(this, false);
+ __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
this.buildWindow();
};
button.ontouchend = () => {
button.click();
};
}).buildElement().addButton({ "class": "bm-button-circle", "textContent": "\u2716", "aria-label": 'Close window "Color Filter"' }, (instance, button) => {
- button.onclick = () => {
- document.querySelector(`#${this.windowID}`)?.remove();
- };
+ button.onclick = () => __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
button.ontouchend = () => {
button.click();
};
@@ -2359,8 +2520,15 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
};
}).buildElement().addButton({ "textContent": "All" }, (instance, button) => {
button.onclick = () => __privateMethod(this, _WindowFilter_instances, selectColorList_fn).call(this, true);
- }).buildElement().buildElement().addDiv({ "class": "bm-container bm-scrollable" }).buildElement().buildElement().buildElement().buildOverlay(this.windowParent);
- this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`);
+ }).buildElement().buildElement().addDiv({ "class": "bm-container bm-scrollable" }).buildElement().buildElement().addDiv({
+ "class": "bm-resize-corner",
+ "title": "Resize Color Filter window",
+ "aria-label": "Resize Color Filter window",
+ "role": "presentation",
+ "textContent": "\u25E2",
+ "style": "position: absolute; right: 0; bottom: 0; width: 28px; height: 28px; display: flex; align-items: flex-end; justify-content: flex-end; padding-right: 4px; padding-bottom: 4px; box-sizing: border-box; z-index: 5; cursor: nwse-resize; pointer-events: auto; touch-action: none; user-select: none; font-size: 8px; line-height: 1; color: rgba(255,255,255,0.95); background: transparent; border: none; box-shadow: none;"
+ }).buildElement().buildElement().buildOverlay(this.windowParent);
+ __privateMethod(this, _WindowFilter_instances, initializeWindowedPersistence_fn).call(this);
const scrollableContainer = document.querySelector(`#${this.windowID} .bm-container.bm-scrollable`);
__privateMethod(this, _WindowFilter_instances, buildColorList_fn).call(this, scrollableContainer);
__privateMethod(this, _WindowFilter_instances, sortColorList_fn).call(this, this.sortPrimary, this.sortSecondary, this.showUnused);
@@ -2445,6 +2613,196 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
}
};
_WindowFilter_instances = new WeakSet();
+ /** Retrieves the persisted window state object.
+ * @returns {Object | null}
+ * @since 0.92.0
+ */
+ getWindowState_fn = function() {
+ var _a, _b;
+ if (!this.settingsManager) {
+ return null;
+ }
+ (_a = this.settingsManager.userSettings)[_b = this.windowStateKey] ?? (_a[_b] = {});
+ return this.settingsManager.userSettings[this.windowStateKey];
+ };
+ /** Updates the preferred window mode setting.
+ * @param {boolean} shouldBeWindowed
+ * @since 0.92.0
+ */
+ setWindowModePreference_fn = function(shouldBeWindowed) {
+ if (!this.settingsManager) {
+ return;
+ }
+ this.settingsManager.toggleFlag(this.windowModeFlag, shouldBeWindowed);
+ void this.settingsManager.saveUserStorageNow();
+ };
+ /** Immediately closes the filter window and cleans up persistence observers.
+ * @since 0.92.0
+ */
+ closeWindow_fn = function() {
+ const windowElement = document.querySelector(`#${this.windowID}`);
+ if (windowElement?.classList.contains("bm-windowed")) {
+ __privateMethod(this, _WindowFilter_instances, saveWindowState_fn).call(this, windowElement);
+ }
+ __privateMethod(this, _WindowFilter_instances, cleanupWindowPersistence_fn).call(this);
+ windowElement?.remove();
+ };
+ /** Disconnects live observers used for window persistence.
+ * @since 0.92.0
+ */
+ cleanupWindowPersistence_fn = function() {
+ if (this.windowResizeObserver) {
+ this.windowResizeObserver.disconnect();
+ this.windowResizeObserver = null;
+ }
+ if (this.windowViewportResizeHandler) {
+ window.removeEventListener("resize", this.windowViewportResizeHandler);
+ this.windowViewportResizeHandler = null;
+ }
+ if (this.windowSaveTimeout) {
+ clearTimeout(this.windowSaveTimeout);
+ this.windowSaveTimeout = null;
+ }
+ };
+ /** Returns a clamped dimension value for the window.
+ * @param {number} size - The size in pixels
+ * @param {number} minimum - Minimum allowed size
+ * @param {number} maximum - Maximum allowed size
+ * @returns {number}
+ * @since 0.92.0
+ */
+ clampWindowDimension_fn = function(size, minimum, maximum) {
+ const resolvedMaximum = Math.max(minimum, maximum);
+ return Math.min(Math.max(Math.round(Number(size) || minimum), minimum), resolvedMaximum);
+ };
+ /** Returns a viewport-safe position for the window.
+ * @param {HTMLElement} windowElement
+ * @param {number} x
+ * @param {number} y
+ * @returns {{x: number, y: number}}
+ * @since 0.92.0
+ */
+ clampWindowPosition_fn = function(windowElement, x, y) {
+ const margin = 8;
+ const maxX = Math.max(margin, window.innerWidth - windowElement.offsetWidth - margin);
+ const maxY = Math.max(margin, window.innerHeight - windowElement.offsetHeight - margin);
+ return {
+ x: Math.min(Math.max(Math.round(Number(x) || margin), margin), maxX),
+ y: Math.min(Math.max(Math.round(Number(y) || margin), margin), maxY)
+ };
+ };
+ /** Applies the persisted size and position to the windowed filter.
+ * @param {HTMLElement} windowElement
+ * @since 0.92.0
+ */
+ restoreWindowState_fn = function(windowElement) {
+ const windowState = __privateMethod(this, _WindowFilter_instances, getWindowState_fn).call(this);
+ if (!windowState || !windowElement) {
+ return;
+ }
+ const width = Number(windowState.width);
+ const height = Number(windowState.height);
+ const hasWidth = Number.isFinite(width);
+ const hasHeight = Number.isFinite(height);
+ if (hasWidth) {
+ windowState.width = __privateMethod(this, _WindowFilter_instances, clampWindowDimension_fn).call(this, width, this.windowMinWidth, Math.min(this.windowMaxWidth, window.innerWidth - 16));
+ windowElement.style.width = `${windowState.width}px`;
+ }
+ if (hasHeight) {
+ windowState.height = __privateMethod(this, _WindowFilter_instances, clampWindowDimension_fn).call(this, height, this.windowMinHeight, Math.min(this.windowMaxHeight, window.innerHeight - 16));
+ windowElement.style.height = `${windowState.height}px`;
+ }
+ requestAnimationFrame(() => {
+ if (!windowElement.isConnected) {
+ return;
+ }
+ const x = Number(windowState.x);
+ const y = Number(windowState.y);
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
+ return;
+ }
+ const clampedPosition = __privateMethod(this, _WindowFilter_instances, clampWindowPosition_fn).call(this, windowElement, x, y);
+ windowElement.style.left = "0px";
+ windowElement.style.top = "0px";
+ windowElement.style.right = "";
+ windowElement.style.transform = `translate(${clampedPosition.x}px, ${clampedPosition.y}px)`;
+ if (clampedPosition.x != x || clampedPosition.y != y) {
+ windowState.x = clampedPosition.x;
+ windowState.y = clampedPosition.y;
+ void this.settingsManager?.saveUserStorageNow();
+ }
+ });
+ };
+ /** Saves the current size and position of the windowed filter.
+ * @param {HTMLElement} windowElement
+ * @since 0.92.0
+ */
+ saveWindowState_fn = function(windowElement) {
+ const windowState = __privateMethod(this, _WindowFilter_instances, getWindowState_fn).call(this);
+ if (!windowState || !windowElement?.isConnected || !windowElement.classList.contains("bm-windowed")) {
+ return;
+ }
+ const rect = windowElement.getBoundingClientRect();
+ const width = __privateMethod(this, _WindowFilter_instances, clampWindowDimension_fn).call(this, rect.width, this.windowMinWidth, Math.min(this.windowMaxWidth, window.innerWidth - 16));
+ const height = __privateMethod(this, _WindowFilter_instances, clampWindowDimension_fn).call(this, rect.height, this.windowMinHeight, Math.min(this.windowMaxHeight, window.innerHeight - 16));
+ if (Math.round(rect.width) != width) {
+ windowElement.style.width = `${width}px`;
+ }
+ if (Math.round(rect.height) != height) {
+ windowElement.style.height = `${height}px`;
+ }
+ const clampedPosition = __privateMethod(this, _WindowFilter_instances, clampWindowPosition_fn).call(this, windowElement, rect.left, rect.top);
+ windowElement.style.left = "0px";
+ windowElement.style.top = "0px";
+ windowElement.style.right = "";
+ windowElement.style.transform = `translate(${clampedPosition.x}px, ${clampedPosition.y}px)`;
+ windowState.x = clampedPosition.x;
+ windowState.y = clampedPosition.y;
+ windowState.width = width;
+ windowState.height = height;
+ void this.settingsManager?.saveUserStorageNow();
+ };
+ /** Debounces persisting the current window size and position.
+ * @param {HTMLElement} windowElement
+ * @param {number} [delay=150]
+ * @since 0.92.0
+ */
+ scheduleWindowStateSave_fn = function(windowElement, delay = 150) {
+ if (this.windowSaveTimeout) {
+ clearTimeout(this.windowSaveTimeout);
+ }
+ this.windowSaveTimeout = setTimeout(() => {
+ this.windowSaveTimeout = null;
+ __privateMethod(this, _WindowFilter_instances, saveWindowState_fn).call(this, windowElement);
+ }, delay);
+ };
+ /** Enables persistence and resize handling for the windowed filter.
+ * @since 0.92.0
+ */
+ initializeWindowedPersistence_fn = function() {
+ const windowElement = document.querySelector(`#${this.windowID}.bm-window`);
+ if (!windowElement) {
+ return;
+ }
+ __privateMethod(this, _WindowFilter_instances, cleanupWindowPersistence_fn).call(this);
+ __privateMethod(this, _WindowFilter_instances, restoreWindowState_fn).call(this, windowElement);
+ this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`, {
+ onEnd: ({ element }) => __privateMethod(this, _WindowFilter_instances, saveWindowState_fn).call(this, element)
+ });
+ this.handleResize(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-resize-corner`, {
+ minWidth: this.windowMinWidth,
+ minHeight: this.windowMinHeight,
+ maxWidth: Math.min(this.windowMaxWidth, window.innerWidth - 16),
+ maxHeight: Math.min(this.windowMaxHeight, window.innerHeight - 16),
+ onEnd: ({ element }) => __privateMethod(this, _WindowFilter_instances, saveWindowState_fn).call(this, element)
+ });
+ if (typeof ResizeObserver == "function") {
+ this.windowResizeObserver = new ResizeObserver(() => __privateMethod(this, _WindowFilter_instances, scheduleWindowStateSave_fn).call(this, windowElement));
+ this.windowResizeObserver.observe(windowElement);
+ }
+ this.windowViewportResizeHandler = () => __privateMethod(this, _WindowFilter_instances, scheduleWindowStateSave_fn).call(this, windowElement, 0);
+ window.addEventListener("resize", this.windowViewportResizeHandler);
+ };
/** Creates the color list container.
* @param {HTMLElement} parentElement - Parent element to add the color list to as a child
* @since 0.88.222
@@ -2977,7 +3335,7 @@ Version: ${this.version}`, "readOnly": true }).buildElement().buildElement().add
*/
buildWindowFilter_fn = function() {
const windowFilter = new WindowFilter(this);
- windowFilter.buildWindow();
+ windowFilter.buildPreferredWindow();
};
coordinateInputPaste_fn = async function(instance, input, event) {
event.preventDefault();
@@ -4014,3 +4372,5 @@ Time Since Blink: ${String(Math.floor(elapsed / 6e4)).padStart(2, "0")}:${String
observer.observe(document.body, { childList: true, subtree: true });
}
})();
+
+// Build Hash: f5ff285a601e
diff --git a/dist/BlueMarble-Standalone.user.js b/dist/BlueMarble-Standalone.user.js
index 8401e77..00ff70a 100644
--- a/dist/BlueMarble-Standalone.user.js
+++ b/dist/BlueMarble-Standalone.user.js
@@ -2,7 +2,7 @@
// @name Blue Marble
// @name:en Blue Marble
// @namespace https://github.com/SwingTheVine/
-// @version 0.92.0
+// @version 0.92.1
// @description A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @description:en A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @author SwingTheVine
@@ -38,4 +38,6 @@
The "Blue Marble" image is owned by NASA.
*/
-(()=>{var t=t=>{throw TypeError(t)},e=(e,i,n)=>i.has(e)?t("Cannot add the same private member more than once"):i instanceof WeakSet?i.add(e):i.set(e,n),i=(e,i,n)=>(((e,i)=>{i.has(e)||t("Cannot access private method")})(e,i),n);function n(t){return new Promise(e=>setTimeout(e,t))}function s(t){return(new Intl.NumberFormat).format(t)}function o(t){return new Intl.NumberFormat(void 0,{style:"percent",t:2,i:2}).format(t)}function a(t){return t.toLocaleString(void 0,{o:"long",l:"numeric",h:"2-digit",m:"2-digit",u:"2-digit"})}function r(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}function l(...t){(0,console.log)(...t)}function h(...t){(0,console.error)(...t)}function c(...t){(0,console.warn)(...t)}function m(t,e){if(0===t)return e[0];let i="";const n=e.length;for(;t>0;)i=e[t%n]+i,t=Math.floor(t/n);return i}function d(t,e){let i=0;const n=e.length;for(const s of t){const t=e.indexOf(s);-1==t&&h(`Invalid character '${s}' encountered whilst decoding! Is the decode alphabet/base incorrect?`),i=i*n+t}return i}function u(t){let e="";for(let i=0;i(t/=255)<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4));return.2126*e[0]+.7152*e[1]+.0722*e[2]}function f(t,e,i){return Array.isArray(t)&&([t,e,i]=t),(1<<24|t<<16|e<<8|i).toString(16).slice(1)}var g,w,x,y,v,C=[{id:0,premium:!1,name:"Transparent",rgb:[0,0,0]},{id:1,premium:!1,name:"Black",rgb:[0,0,0]},{id:2,premium:!1,name:"Dark Gray",rgb:[60,60,60]},{id:3,premium:!1,name:"Gray",rgb:[120,120,120]},{id:4,premium:!1,name:"Light Gray",rgb:[210,210,210]},{id:5,premium:!1,name:"White",rgb:[255,255,255]},{id:6,premium:!1,name:"Deep Red",rgb:[96,0,24]},{id:7,premium:!1,name:"Red",rgb:[237,28,36]},{id:8,premium:!1,name:"Orange",rgb:[255,127,39]},{id:9,premium:!1,name:"Gold",rgb:[246,170,9]},{id:10,premium:!1,name:"Yellow",rgb:[249,221,59]},{id:11,premium:!1,name:"Light Yellow",rgb:[255,250,188]},{id:12,premium:!1,name:"Dark Green",rgb:[14,185,104]},{id:13,premium:!1,name:"Green",rgb:[19,230,123]},{id:14,premium:!1,name:"Light Green",rgb:[135,255,94]},{id:15,premium:!1,name:"Dark Teal",rgb:[12,129,110]},{id:16,premium:!1,name:"Teal",rgb:[16,174,166]},{id:17,premium:!1,name:"Light Teal",rgb:[19,225,190]},{id:18,premium:!1,name:"Dark Blue",rgb:[40,80,158]},{id:19,premium:!1,name:"Blue",rgb:[64,147,228]},{id:20,premium:!1,name:"Cyan",rgb:[96,247,242]},{id:21,premium:!1,name:"Indigo",rgb:[107,80,246]},{id:22,premium:!1,name:"Light Indigo",rgb:[153,177,251]},{id:23,premium:!1,name:"Dark Purple",rgb:[120,12,153]},{id:24,premium:!1,name:"Purple",rgb:[170,56,185]},{id:25,premium:!1,name:"Light Purple",rgb:[224,159,249]},{id:26,premium:!1,name:"Dark Pink",rgb:[203,0,122]},{id:27,premium:!1,name:"Pink",rgb:[236,31,128]},{id:28,premium:!1,name:"Light Pink",rgb:[243,141,169]},{id:29,premium:!1,name:"Dark Brown",rgb:[104,70,52]},{id:30,premium:!1,name:"Brown",rgb:[149,104,42]},{id:31,premium:!1,name:"Beige",rgb:[248,178,119]},{id:32,premium:!0,name:"Medium Gray",rgb:[170,170,170]},{id:33,premium:!0,name:"Dark Red",rgb:[165,14,30]},{id:34,premium:!0,name:"Light Red",rgb:[250,128,114]},{id:35,premium:!0,name:"Dark Orange",rgb:[228,92,26]},{id:36,premium:!0,name:"Light Tan",rgb:[214,181,148]},{id:37,premium:!0,name:"Dark Goldenrod",rgb:[156,132,49]},{id:38,premium:!0,name:"Goldenrod",rgb:[197,173,49]},{id:39,premium:!0,name:"Light Goldenrod",rgb:[232,212,95]},{id:40,premium:!0,name:"Dark Olive",rgb:[74,107,58]},{id:41,premium:!0,name:"Olive",rgb:[90,148,74]},{id:42,premium:!0,name:"Light Olive",rgb:[132,197,115]},{id:43,premium:!0,name:"Dark Cyan",rgb:[15,121,159]},{id:44,premium:!0,name:"Light Cyan",rgb:[187,250,242]},{id:45,premium:!0,name:"Light Blue",rgb:[125,199,255]},{id:46,premium:!0,name:"Dark Indigo",rgb:[77,49,184]},{id:47,premium:!0,name:"Dark Slate Blue",rgb:[74,66,132]},{id:48,premium:!0,name:"Slate Blue",rgb:[122,113,196]},{id:49,premium:!0,name:"Light Slate Blue",rgb:[181,174,241]},{id:50,premium:!0,name:"Light Brown",rgb:[219,164,99]},{id:51,premium:!0,name:"Dark Beige",rgb:[209,128,81]},{id:52,premium:!0,name:"Light Beige",rgb:[255,197,165]},{id:53,premium:!0,name:"Dark Peach",rgb:[155,82,73]},{id:54,premium:!0,name:"Peach",rgb:[209,128,120]},{id:55,premium:!0,name:"Light Peach",rgb:[250,182,164]},{id:56,premium:!0,name:"Dark Tan",rgb:[123,99,82]},{id:57,premium:!0,name:"Tan",rgb:[156,132,107]},{id:58,premium:!0,name:"Dark Slate",rgb:[51,57,65]},{id:59,premium:!0,name:"Slate",rgb:[109,117,141]},{id:60,premium:!0,name:"Light Slate",rgb:[179,185,209]},{id:61,premium:!0,name:"Dark Stone",rgb:[109,100,63]},{id:62,premium:!0,name:"Stone",rgb:[148,140,107]},{id:63,premium:!0,name:"Light Stone",rgb:[205,197,158]}],M=class{constructor(t,i){e(this,g),this.name=t,this.version=i,this.p=null,this.v=null,this.C="bm-r",this.M=null,this.T=null,this.$=[]}S(t){this.p=t}k(t){this.v=t}D(){return this.$.length>0&&(this.T=this.$.pop()),this}L(t){t?.appendChild(this.M),this.M=null,this.T=null,this.$=[]}H(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"div",{},t)),this}N(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"p",{},t)),this}O(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"small",{},t)),this}B(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"span",{},t)),this}I(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"details",{},t)),this}P(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"summary",{},t)),this}A(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"img",{},t)),this}W(t,e={},n=()=>{}){return n(this,i(this,g,w).call(this,"h"+t,{},e)),this}V(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"hr",{},t)),this}_(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"br",{},t)),this}F(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"form",{},t)),this}U(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"fieldset",{},t)),this}G(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"legend",{},t)),this}R(t={},e=()=>{}){const n={};t.textContent?(n.textContent=t.textContent,delete t.textContent):t.innerHTML&&(n.innerHTML=t.innerHTML,delete t.textContent);const s=i(this,g,w).call(this,"label",n),o=i(this,g,w).call(this,"input",{type:"checkbox"},t);return s.insertBefore(o,s.firstChild),this.D(),e(this,s,o),this}j(t={},e=()=>{}){const n=i(this,g,w).call(this,"label",{textContent:t.textContent??"",for:t.id??""});return delete t.textContent,this.D(),e(this,n,i(this,g,w).call(this,"select",{},t)),this}Y(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"option",{},t)),this}X(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"ol",{},t)),this}J(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"ul",{},t)),this}q(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"menu",{},t)),this}Z(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"li",{},t)),this}K(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"table",{},t)),this}tt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"caption",{},t)),this}et(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"thead",{},t)),this}it(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tbody",{},t)),this}nt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tfoot",{},t)),this}st(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tr",{},t)),this}ot(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"th",{},t)),this}rt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"td",{},t)),this}lt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"button",{},t)),this}ht(t={},e=()=>{}){const n=t.title??t.textContent??"Help: No info";delete t.textContent,t.title=`Help: ${n}`;const s={textContent:"?",className:"bm-10",onclick:()=>{this.ct(this.C,n)}};return e(this,i(this,g,w).call(this,"button",s,t)),this}dt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"input",{},t)),this}ut(t={},e=()=>{}){const n=t.textContent??"";delete t.textContent;const s=i(this,g,w).call(this,"div"),o=i(this,g,w).call(this,"input",{type:"file",tabindex:"-1","aria-hidden":"true"},t);this.D();const a=i(this,g,w).call(this,"button",{textContent:n});return this.D(),this.D(),a.addEventListener("click",()=>{o.click()}),o.addEventListener("change",()=>{a.style.maxWidth=`${a.offsetWidth}px`,o.files.length>0?a.textContent=o.files[0].name:a.textContent=n}),e(this,s,o,a),this}bt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"textarea",{},t)),this}ft(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"div",{class:"bm-S"},t)),this}gt(t=Date.now(),e=500,n={},s=()=>{}){const o="bm--",a=n?.id||o+"-"+crypto.randomUUID().slice(0,8),r={class:o},l=i(this,g,w).call(this,"time",r,n);return l.id=a,l.dataset.endDate=t,setInterval(()=>{if(!l.isConnected)return;const t=Math.max(l.dataset.endDate-Date.now(),0),e=Math.floor(t/1e3),i=Math.floor(e/3600),n=Math.floor(e%60),s=Math.floor(e%3600/60);l.setAttribute("datetime",`PT${i}H${s}M${n}S`),l.textContent=String(i).padStart(2,"0")+":"+String(s).padStart(2,"0")+":"+String(n).padStart(2,"0")},e),s(this,l),this}ct(t,e,i=!1){const n=document.getElementById(t.replace(/^#/,""));n&&(n instanceof HTMLInputElement?n.value=e:i?n.textContent=e:n.innerHTML=e)}wt(t){if(t.disabled)return;t.disabled=!0,t.style.textDecoration="none";const e=t.closest(".bm-W"),i=t.closest(".bm-S"),n=e.querySelector("h1"),s=e.querySelector(".bm-m");if(e.parentElement.append(e),"expanded"==t.dataset.buttonStatus){s.style.height=s.scrollHeight+"px",e.style.width=e.scrollWidth+"px",s.style.height="0",s.addEventListener("transitionend",function e(){s.style.display="none",t.disabled=!1,t.style.textDecoration="",s.removeEventListener("transitionend",e)});const i=n.cloneNode(!0),o=i.textContent;t.nextElementSibling.appendChild(i),t.textContent="▶",t.dataset.buttonStatus="collapsed",t.ariaLabel=`Unminimize window "${o}"`}else{const n=i.querySelector("h1"),o=n.textContent;n.remove(),s.style.display="",s.style.height="0",e.style.width="",s.style.height=s.scrollHeight+"px",s.addEventListener("transitionend",function e(){s.style.height="",t.disabled=!1,t.style.textDecoration="",s.removeEventListener("transitionend",e)}),t.textContent="▼",t.dataset.buttonStatus="expanded",t.ariaLabel=`Minimize window "${o}"`}}xt(t,e){const i=document.querySelector(t),n=document.querySelector(e);if(!i||!n)return void this.yt(`Can not drag! ${i?"":"moveMe"} ${i||n?"":"and "}${n?"":"iMoveThings "}was not found!`);let s,o=!1,a=0,r=null,l=0,h=0,c=0,m=0,d=null;const u=()=>{if(o){const t=Math.abs(l-c),e=Math.abs(h-m);(t>.5||e>.5)&&(l=c,h=m,i.style.transform=`translate(${l}px, ${h}px)`,i.style.left="0px",i.style.top="0px",i.style.right=""),r=requestAnimationFrame(u)}},b=(t,e)=>{o=!0,d=i.getBoundingClientRect(),s=t-d.left,a=e-d.top;const b=window.getComputedStyle(i).transform;if(b&&"none"!==b){const t=new DOMMatrix(b);l=t.m41,h=t.m42}else l=d.left,h=d.top;c=l,m=h,document.body.style.userSelect="none",n.classList.add("bm-M"),document.addEventListener("mousemove",f),document.addEventListener("touchmove",g,{passive:!1}),document.addEventListener("mouseup",p),document.addEventListener("touchend",p),document.addEventListener("touchcancel",p),r&&cancelAnimationFrame(r),u()},p=()=>{o=!1,r&&(cancelAnimationFrame(r),r=null),document.body.style.userSelect="",n.classList.remove("bm-M"),document.removeEventListener("mousemove",f),document.removeEventListener("touchmove",g),document.removeEventListener("mouseup",p),document.removeEventListener("touchend",p),document.removeEventListener("touchcancel",p)},f=t=>{o&&d&&(c=t.clientX-s,m=t.clientY-a)},g=t=>{if(o&&d){const e=t.touches[0];if(!e)return;c=e.clientX-s,m=e.clientY-a,t.preventDefault()}};n.addEventListener("mousedown",function(t){t.preventDefault(),b(t.clientX,t.clientY)}),n.addEventListener("touchstart",function(t){const e=t?.touches?.[0];e&&(b(e.clientX,e.clientY),t.preventDefault())},{passive:!1})}vt(t){(0,console.info)(`${this.name}: ${t}`),this.ct(this.C,"Status: "+t,!0)}yt(t){(0,console.error)(`${this.name}: ${t}`),this.ct(this.C,"Error: "+t,!0)}};g=new WeakSet,w=function(t,e={},n={}){const s=document.createElement(t);this.M?(this.T?.appendChild(s),this.$.push(this.T),this.T=s):(this.M=s,this.T=s);for(const[t,n]of Object.entries(e))i(this,g,x).call(this,s,t,n);for(const[t,e]of Object.entries(n))i(this,g,x).call(this,s,t,e);return s},x=function(t,e,i){"class"==e?t.classList.add(...i.split(/\s+/)):"for"==e?t.htmlFor=i:"tabindex"==e?t.tabIndex=Number(i):"readonly"==e?t.readOnly="true"==i||"1"==i:"maxlength"==e?t.maxLength=Number(i):e.startsWith("data")?t.dataset[e.slice(5).split("-").map((t,e)=>0==e?t:t[0].toUpperCase()+t.slice(1)).join("")]=i:e.startsWith("aria")?t.setAttribute(e,i):t[e]=i};var T,$,S,k,D,L=class extends M{constructor(t,i){super(t,i),e(this,y),this.window=null,this.Ct="bm-l",this.Mt=document.body}Tt(){document.querySelector(`#${this.Ct}`)?document.querySelector(`#${this.Ct}`).remove():(this.window=this.H({id:this.Ct,class:"bm-W"}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().H({class:"bm-D"}).lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Settings"}).D().D().V().D().N({textContent:"Settings take 5 seconds to save."}).D().H({class:"bm-L bm-H"},(t,e)=>{this.$t(),this.St()}).D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}$t(){i(this,y,v).call(this,"Pixel Highlight")}St(){i(this,y,v).call(this,"Template")}};y=new WeakSet,v=function(t){this.window=this.H({class:"bm-L"}).W(2,{textContent:t}).D().V().D().N({innerHTML:`An error occured loading the ${t} category. SettingsManager failed to override the ${t} function inside WindowSettings.`}).D().D()},T=new WeakSet,$=function(t,e){t.disabled=!0;const i=t.dataset.status,n=this.kt?.highlight??[[1,0,1],[2,0,0],[1,-1,0],[1,1,0],[1,0,-1]];let s=[2,0,0];const o=n;switch(i){case"Disabled":t.dataset.status="Incorrect",t.ariaLabel="Sub-pixel incorrect",s=[1,...e];break;case"Incorrect":t.dataset.status="Template",t.ariaLabel="Sub-pixel template",s=[2,...e];break;case"Template":t.dataset.status="Disabled",t.ariaLabel="Sub-pixel disabled",s=[0,...e]}const a=n.findIndex(([,t,e])=>t==s[1]&&e==s[2]);0!=s[0]?-1!=a?o[a]=s:o.push(s):-1!=a&&o.splice(a,1),this.kt.highlight=o,t.disabled=!1},S=async function(t){const e=document.querySelectorAll(".bm-3 button");for(const t of e)t.disabled=!0;let i=[0,0,0,0,2,0,0,0,0];switch(t){case"Cross":i=[0,1,0,1,2,1,0,1,0];break;case"X":i=[1,0,1,0,2,0,1,0,1];break;case"Full":i=[2,2,2,2,2,2,2,2,2]}const s=document.querySelector(".bm-n")?.childNodes??[];for(let t=0;t{const[n,s,o,a]=e.split(",").map(Number);(s>>24==0?0:s.get(e)??-2;const a=o.get(n);o.set(n,a?a+1:1)}return o};var N=class{constructor(){this.Et=Math.ceil(80/1300*window.innerWidth),this.Yt=C.slice(1)}Xt(t){const e=document.createElement("div");for(let t=0;t{t.parentNode.childElementCount<=1?t.parentNode.remove():t.remove()},e.appendChild(t)}t.appendChild(e)}},O=class extends HTMLElement{};customElements.define("confetti-piece",O);var B,I,P,A,W,V,_,z,F,U=class extends M{constructor(t,e){super(t,e),this.window=null,this.Ct="bm-o",this.Mt=document.body}Tt(){document.querySelector(`#${this.Ct}`)?document.querySelector(`#${this.Ct}`).remove():(this.window=this.H({id:this.Ct,class:"bm-W"},(t,e)=>{}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Credits"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Credits"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Credits"}).D().D().V().D().H({class:"bm-L bm-H"}).B({role:"img","aria-label":this.name}).B({innerHTML:"\n██████╗ ██╗ ██╗ ██╗███████╗\n██╔══██╗██║ ██║ ██║██╔════╝\n██████╔╝██║ ██║ ██║█████╗ \n██╔══██╗██║ ██║ ██║██╔══╝ \n██████╔╝███████╗╚██████╔╝███████╗\n╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝\n\n███╗ ███╗ █████╗ ██████╗ ██████╗ ██╗ ███████╗\n████╗ ████║██╔══██╗██╔══██╗██╔══██╗██║ ██╔════╝\n██╔████╔██║███████║██████╔╝██████╔╝██║ █████╗ \n██║╚██╔╝██║██╔══██║██╔══██╗██╔══██╗██║ ██╔══╝ \n██║ ╚═╝ ██║██║ ██║██║ ██║██████╔╝███████╗███████╗\n╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚══════╝\n",class:"bm-_","aria-hidden":"true"}).D().D()._().D().V().D()._().D().B({textContent:'"Blue Marble" userscript is made by SwingTheVine.'}).D()._().D().B({innerHTML:'The Blue Marble Website is made by crqch.'}).D()._().D().B({textContent:`The Blue Marble Website used until ${a(new Date(175606932e4))} was made by Camille Daguin.`}).D()._().D().B({textContent:'The favicon "Blue Marble" is owned by NASA. (The image of the Earth is owned by NASA)'}).D()._().D().B({textContent:"Special Thanks:"}).D().J().Z({textContent:"Espresso, Meqa, and Robot for moderating SwingTheVine's community."}).D().Z({innerHTML:'nof, darkness for creating similar userscripts!'}).D().Z({innerHTML:'Wonda for the Blue Marble banner image!'}).D().Z({innerHTML:'BullStein, allanf181 for being early beta testers!'}).D().Z({innerHTML:'guidu_ and Nick-machado for the original "Minimize" Button code!'}).D().Z({innerHTML:'Nomad and Gustav for the tutorials!'}).D().Z({innerHTML:'cfp for creating the template overlay that Blue Marble was based on!'}).D().Z({innerHTML:'Force Network for hosting the telemetry server!'}).D().Z({innerHTML:'TheBlueCorner for getting me interested in online pixel canvases!'}).D().D()._().D().B({innerHTML:'Donators:'}).D().J().Z({textContent:"Soultree"}).D().Z({textContent:"Espresso"}).D().Z({textContent:"BEST FAN"}).D().Z({textContent:"FuchsDresden"}).D().Z({textContent:"Jack"}).D().Z({textContent:"raiken_au"}).D().Z({textContent:"Jacob"}).D().Z({textContent:"StupidOne"}).D().Z({textContent:"2 Anonymous Supporters"}).D().D().D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}},G=class extends M{constructor(t){super(t.name,t.version),e(this,B),this.window=null,this.Ct="bm-t",this.Jt="bm-E",this.Mt=document.body,this.qt=t.p?.qt,this.Zt='',this.Qt='';const{palette:i,jt:n}=this.qt.Kt;this.palette=i,this.te=0,this.ee=0,this.ie=new Map,this.ne=new Map,this.se=0,this.oe=0,this.timeRemaining=0,this.ae="",this.sortPrimary="id",this.sortSecondary="ascending",this.showUnused=!1}Tt(){if(document.querySelector(`#${this.Ct}`))return void document.querySelector(`#${this.Ct}`).remove();this.window=this.H({id:this.Ct,class:"bm-W"},(t,e)=>{}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().H({class:"bm-D"}).lt({class:"bm-s",textContent:"🗗","aria-label":'Switch to windowed mode for "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove(),this.re()},e.ontouchend=()=>{e.click()}}).D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Color Filter"}).D().D().V().D().H({class:"bm-L bm-x bm-h",style:"gap: 1.5ch;"}).lt({textContent:"Hide All Colors"},(t,e)=>{e.onclick=()=>i(this,B,A).call(this,!1)}).D().lt({textContent:"Refresh Data"},(t,e)=>{e.onclick=()=>{e.disabled=!0,this.le(),e.disabled=!1}}).D().lt({textContent:"Show All Colors"},(t,e)=>{e.onclick=()=>i(this,B,A).call(this,!0)}).D().D().H({class:"bm-L bm-H"}).H({class:"bm-L",style:"margin-left: 2.5ch; margin-right: 2.5ch;"}).H({class:"bm-L"}).B({id:"bm-i",innerHTML:"Tiles Loaded: 0 / ???"}).D()._().D().B({id:"bm-d",innerHTML:"Correct Pixels: ???"}).D()._().D().B({id:"bm-j",innerHTML:"Total Pixels: ???"}).D()._().D().B({id:"bm-7",innerHTML:"Complete: ??? (???)"}).D()._().D().B({id:"bm-8",innerHTML:"??? ???"}).D().D().H({class:"bm-L"}).N({innerHTML:`Press the 🗗 button to make this window smaller. Colors with the icon ${this.Zt.replace("