Finished highlight UI

This commit is contained in:
SwingTheVine 2026-03-04 20:58:33 -05:00
parent 1d258d857c
commit e858aeac9c
13 changed files with 238 additions and 15 deletions

View file

@ -390,6 +390,31 @@ input[type=file] {
}
/* src/WindowSettings.css */
#bm-window-settings .bm-highlight-preset-container {
display: flex;
flex-direction: column;
}
#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: 1ch;
max-width: 10ch;
}
#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;
@ -399,6 +424,7 @@ input[type=file] {
}
#bm-window-settings .bm-highlight-grid > button {
width: 100%;
padding: 0;
aspect-ratio: 1 / 1;
background-color: white;
border: #333 1px solid;

View file

@ -2,7 +2,7 @@
// @name Blue Marble
// @name:en Blue Marble
// @namespace https://github.com/SwingTheVine/
// @version 0.91.42
// @version 0.91.53
// @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
@ -3435,7 +3435,7 @@ Version: ${this.version}`, "readOnly": true }).buildElement().buildElement().add
button.ontouchend = () => {
button.click();
};
}).buildElement().buildElement().buildElement().addDiv({ "class": "bm-window-content" }).addDiv({ "class": "bm-container bm-center-vertically" }).addHeader(1, { "textContent": "Settings" }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container bm-scrollable" }, (instance, div) => {
}).buildElement().buildElement().buildElement().addDiv({ "class": "bm-window-content" }).addDiv({ "class": "bm-container bm-center-vertically" }).addHeader(1, { "textContent": "Settings" }).buildElement().buildElement().addHr().buildElement().addP({ "textContent": "Settings take 5 seconds to save." }).buildElement().addDiv({ "class": "bm-container bm-scrollable" }, (instance, div) => {
this.buildHighlight();
}).buildElement().buildElement().buildElement().buildOverlay(this.windowParent);
this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`);
@ -3457,7 +3457,7 @@ Version: ${this.version}`, "readOnly": true }).buildElement().buildElement().add
};
// src/settingsManager.js
var _SettingsManager_instances, updateHighlightSettings_fn;
var _SettingsManager_instances, updateHighlightSettings_fn, updateHighlightToPreset_fn;
var SettingsManager = class extends WindowSettings {
/** Constructor for the SettingsManager class
* @param {string} name - The name of the userscript
@ -3494,8 +3494,18 @@ Version: ${this.version}`, "readOnly": true }).buildElement().buildElement().add
* @see WindowSettings#buildHighlight
*/
buildHighlight() {
const highlightPresetOff = '<svg viewBox="0 0 3 3"><path d="M0,0H3V3H0ZM0,1H3M0,2H3M1,0V3M2,0V3" fill="#fff"/><path d="M1,1H2V2H1Z" fill="#2f4f4f"/></svg>';
const highlightPresetCross = '<svg viewBox="0 0 3 3"><path d="M0,0H3V3H0Z" fill="#fff"/><path d="M1,0H2V1H3V2H2V3H1V2H0V1H1Z" fill="brown"/><path d="M1,1H2V2H1Z" fill="#2f4f4f"/></svg>';
const storedHighlight = this.userSettings?.highlight ?? [[1, 0, 1], [2, 0, 0], [1, -1, 0], [1, 1, 0], [1, 0, -1]];
this.window = this.addDiv({ "class": "bm-container" }).addHeader(2, { "textContent": "Pixel Highlight" }).buildElement().addHr().buildElement().addDiv({ "style": "margin-left: 1.5ch;" }).addP({ "id": "bm-highlight-grid-label", "textContent": "Create a custom pattern:" }).buildElement().addDiv({ "class": "bm-highlight-grid", "role": "group", "aria-labelledby": "bm-highlight-grid-label" });
this.window = this.addDiv({ "class": "bm-container" }).addHeader(2, { "textContent": "Pixel Highlight" }).buildElement().addHr().buildElement().addDiv({ "style": "margin-left: 1.5ch;" }).addP({ "id": "bm-highlight-preset-label", "textContent": "Choose a preset:" }).buildElement().addDiv({ "class": "bm-container bm-flex-center", "style": "width: 50%;", "role": "group", "aria-labelledby": "bm-highlight-preset-label" }).addDiv({ "class": "bm-highlight-preset-container" }).addSpan({ "textContent": "None" }).buildElement().addButton({ "innerHTML": highlightPresetOff, "aria-label": 'Preset "None"' }, (instance, button) => {
button.onclick = () => __privateMethod(this, _SettingsManager_instances, updateHighlightToPreset_fn).call(this, "None");
}).buildElement().buildElement().addDiv({ "class": "bm-highlight-preset-container" }).addSpan({ "textContent": "Cross" }).buildElement().addButton({ "innerHTML": highlightPresetCross, "aria-label": 'Preset "Cross Shape"' }, (instance, button) => {
button.onclick = () => __privateMethod(this, _SettingsManager_instances, updateHighlightToPreset_fn).call(this, "Cross");
}).buildElement().buildElement().addDiv({ "class": "bm-highlight-preset-container" }).addSpan({ "textContent": "X" }).buildElement().addButton({ "innerHTML": highlightPresetCross.replace('d="M1,0H2V1H3V2H2V3H1V2H0V1H1Z"', 'd="M0,0V1H3V0H2V3H3V2H0V3H1V0Z"'), "aria-label": 'Preset "X Shape"' }, (instance, button) => {
button.onclick = () => __privateMethod(this, _SettingsManager_instances, updateHighlightToPreset_fn).call(this, "X");
}).buildElement().buildElement().addDiv({ "class": "bm-highlight-preset-container" }).addSpan({ "textContent": "Full" }).buildElement().addButton({ "innerHTML": highlightPresetOff.replace("#fff", "#2f4f4f"), "aria-label": 'Preset "Full Template"' }, (instance, button) => {
button.onclick = () => __privateMethod(this, _SettingsManager_instances, updateHighlightToPreset_fn).call(this, "Full");
}).buildElement().buildElement().buildElement().addP({ "id": "bm-highlight-grid-label", "textContent": "Create a custom pattern:" }).buildElement().addDiv({ "class": "bm-highlight-grid", "role": "group", "aria-labelledby": "bm-highlight-grid-label" });
for (let buttonY = -1; buttonY <= 1; buttonY++) {
for (let buttonX = -1; buttonX <= 1; buttonX++) {
const buttonState = storedHighlight[storedHighlight.findIndex(([, x, y]) => x == buttonX && y == buttonY)]?.[0] ?? 0;
@ -3521,6 +3531,7 @@ Version: ${this.version}`, "readOnly": true }).buildElement().buildElement().add
* Additionally, it will update user settings with the new selection.
* @param {HTMLButtonElement} button - The button that was pressed
* @param {Array<number, number>} coords - The relative coordinates of the button
* @since 0.91.46
*/
updateHighlightSettings_fn = function(button, coords2) {
console.log(coords2);
@ -3566,6 +3577,48 @@ Version: ${this.version}`, "readOnly": true }).buildElement().buildElement().add
this.userSettings["highlight"] = userStorageNew;
button.disabled = false;
};
updateHighlightToPreset_fn = async function(preset) {
const presetButtons = document.querySelectorAll(".bm-highlight-preset-container button");
for (const button of presetButtons) {
button.disabled = true;
}
let presetArray = [0, 0, 0, 0, 2, 0, 0, 0, 0];
switch (preset) {
case "Cross":
presetArray = [0, 1, 0, 1, 2, 1, 0, 1, 0];
break;
case "X":
presetArray = [1, 0, 1, 0, 2, 0, 1, 0, 1];
break;
case "Full":
presetArray = [2, 2, 2, 2, 2, 2, 2, 2, 2];
break;
}
const buttons = document.querySelector(".bm-highlight-grid")?.childNodes ?? [];
for (let buttonIndex = 0; buttonIndex < buttons.length; buttonIndex++) {
const button = buttons[buttonIndex];
let buttonState = button.dataset["status"];
buttonState = buttonState != "Disabled" ? buttonState != "Incorrect" ? 2 : 1 : 0;
let buttonStateDelta = presetArray[buttonIndex] - buttonState;
if (buttonStateDelta == 0) {
continue;
}
buttonStateDelta += buttonStateDelta < 0 ? 3 : 0;
button.click();
if (buttonStateDelta == 2) {
for (let timeWaited = 0; timeWaited < 200; timeWaited += 10) {
if (!button.disabled) {
break;
}
await sleep(10);
}
button.click();
}
}
for (const button of presetButtons) {
button.disabled = false;
}
};
// src/main.js
var name = GM_info.script.name.toString();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -51,7 +51,7 @@
<a href="https://discord.gg/tpeBPy46hf" target="_blank" rel="noopener noreferrer"><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="https://bluemarble.lol/" target="_blank" rel="noopener noreferrer"><img alt="Blue Marble Website" src="https://img.shields.io/badge/Blue_Marble_Website-crqch-blue?style=flat&logo=globe&logoColor=white"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="WakaTime" src="https://img.shields.io/badge/Coding_Time-212hrs_17mins-blue?style=flat&logo=wakatime&logoColor=black&logoSize=auto&labelColor=white"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-1157-black?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-1168-black?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Lines of Code" src="https://img.shields.io/badge/Lines_Of_Code-6620-blue?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Comments" src="https://img.shields.io/badge/Lines_Of_Comments-5414-blue?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Compression" src="https://img.shields.io/badge/Compression-71.85%25-blue"></a>

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "wplace-bluemarble",
"version": "0.91.42",
"version": "0.91.53",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wplace-bluemarble",
"version": "0.91.42",
"version": "0.91.53",
"devDependencies": {
"esbuild": "^0.25.0",
"jsdoc": "^4.0.5",

View file

@ -1,6 +1,6 @@
{
"name": "wplace-bluemarble",
"version": "0.91.42",
"version": "0.91.53",
"type": "module",
"homepage": "https://bluemarble.lol/",
"repository": {

View file

@ -2,7 +2,7 @@
// @name Blue Marble
// @name:en Blue Marble
// @namespace https://github.com/SwingTheVine/
// @version 0.91.42
// @version 0.91.53
// @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

View file

@ -1,5 +1,40 @@
/* @since 0.91.22 */
/* Highlight preset container */
#bm-window-settings .bm-highlight-preset-container {
display: flex;
flex-direction: column;
}
/* Highlight preset title */
#bm-window-settings .bm-highlight-preset-container span {
width: fit-content;
margin: auto;
font-size: 0.7em;
}
/* Highlight preset button */
#bm-window-settings .bm-highlight-preset-container button {
width: fit-content;
padding: 0;
border-radius: 0;
}
/* Highlight preset SVG */
#bm-window-settings .bm-highlight-preset-container svg {
stroke: #333;
stroke-width: 0.02px;
width: 100%;
min-width: 1ch;
max-width: 10ch;
}
/* Highlight preset SVG on hover/focus */
#bm-window-settings .bm-highlight-preset-container button:hover svg,
#bm-window-settings .bm-highlight-preset-container button:focus svg {
opacity: 0.9;
}
/* Highlight pattern container */
#bm-window-settings .bm-highlight-grid {
display: grid;

View file

@ -54,6 +54,7 @@ export default class WindowSettings extends Overlay {
.addHeader(1, {'textContent': 'Settings'}).buildElement()
.buildElement()
.addHr().buildElement()
.addP({'textContent': 'Settings take 5 seconds to save.'}).buildElement()
.addDiv({'class': 'bm-container bm-scrollable'}, (instance, div) => {
// Each category in the settings window
this.buildHighlight();

View file

@ -1,7 +1,9 @@
import { sleep } from "./utils";
import WindowSettings from "./WindowSettings";
/** SettingsManager class for handling user settings and making them persist between sessions.
* Logic for {@link WindowSettings} is managed here.
* "Flags" should follow the same styling as `.classList()` and should not contain spaces.
* @class SettingsManager
* @since 0.91.11
* @examples
@ -60,6 +62,9 @@ export default class SettingsManager extends WindowSettings {
*/
buildHighlight() {
const highlightPresetOff = '<svg viewBox="0 0 3 3"><path d="M0,0H3V3H0ZM0,1H3M0,2H3M1,0V3M2,0V3" fill="#fff"/><path d="M1,1H2V2H1Z" fill="#2f4f4f"/></svg>';
const highlightPresetCross = '<svg viewBox="0 0 3 3"><path d="M0,0H3V3H0Z" fill="#fff"/><path d="M1,0H2V1H3V2H2V3H1V2H0V1H1Z" fill="brown"/><path d="M1,1H2V2H1Z" fill="#2f4f4f"/></svg>';
// Obtains user settings for highlight from storage, or the default array if nothing was found
const storedHighlight = this.userSettings?.highlight ?? [[1, 0, 1], [2, 0, 0], [1, -1, 0], [1, 1, 0], [1, 0, -1]];
@ -68,6 +73,25 @@ export default class SettingsManager extends WindowSettings {
.addHeader(2, {'textContent': 'Pixel Highlight'}).buildElement()
.addHr().buildElement()
.addDiv({'style': 'margin-left: 1.5ch;'})
.addP({'id': 'bm-highlight-preset-label', 'textContent': 'Choose a preset:'}).buildElement()
.addDiv({'class': 'bm-container bm-flex-center', 'style': 'width: 50%;', 'role': 'group', 'aria-labelledby': 'bm-highlight-preset-label'})
.addDiv({'class': 'bm-highlight-preset-container'})
.addSpan({'textContent': 'None'}).buildElement()
.addButton({'innerHTML': highlightPresetOff, 'aria-label': 'Preset "None"'}, (instance, button) => {button.onclick = () => this.#updateHighlightToPreset('None')}).buildElement()
.buildElement()
.addDiv({'class': 'bm-highlight-preset-container'})
.addSpan({'textContent': 'Cross'}).buildElement()
.addButton({'innerHTML': highlightPresetCross, 'aria-label': 'Preset "Cross Shape"'}, (instance, button) => {button.onclick = () => this.#updateHighlightToPreset('Cross')}).buildElement()
.buildElement()
.addDiv({'class': 'bm-highlight-preset-container'})
.addSpan({'textContent': 'X'}).buildElement()
.addButton({'innerHTML': highlightPresetCross.replace('d="M1,0H2V1H3V2H2V3H1V2H0V1H1Z"', 'd="M0,0V1H3V0H2V3H3V2H0V3H1V0Z"'), 'aria-label': 'Preset "X Shape"'}, (instance, button) => {button.onclick = () => this.#updateHighlightToPreset('X')}).buildElement()
.buildElement()
.addDiv({'class': 'bm-highlight-preset-container'})
.addSpan({'textContent': 'Full'}).buildElement()
.addButton({'innerHTML': highlightPresetOff.replace('#fff', '#2f4f4f'), 'aria-label': 'Preset "Full Template"'}, (instance, button) => {button.onclick = () => this.#updateHighlightToPreset('Full')}).buildElement()
.buildElement()
.buildElement()
.addP({'id': 'bm-highlight-grid-label', 'textContent': 'Create a custom pattern:'}).buildElement()
.addDiv({'class': 'bm-highlight-grid', 'role': 'group', 'aria-labelledby': 'bm-highlight-grid-label'});
// We leave this open so we can add buttons
@ -101,6 +125,7 @@ export default class SettingsManager extends WindowSettings {
* Additionally, it will update user settings with the new selection.
* @param {HTMLButtonElement} button - The button that was pressed
* @param {Array<number, number>} coords - The relative coordinates of the button
* @since 0.91.46
*/
#updateHighlightSettings(button, coords) {
@ -178,4 +203,87 @@ export default class SettingsManager extends WindowSettings {
button.disabled = false; // Reenables the button since we are done
}
/** Changes the highlight buttons to the clicked preset.
* @param {string} preset - The name of the preset
* @since 0.91.49
*/
async #updateHighlightToPreset(preset) {
// Obtains all preset buttons as a NodeList
const presetButtons = document.querySelectorAll('.bm-highlight-preset-container button');
// For each preset...
for (const button of presetButtons) {
button.disabled = true; // Disables the button
}
let presetArray = [0,0,0,0,2,0,0,0,0]; // The preset "None"
// Selects the preset passed in
switch (preset) {
case 'Cross':
presetArray = [0,1,0,1,2,1,0,1,0]; // The preset "Cross"
break;
case 'X':
presetArray = [1,0,1,0,2,0,1,0,1]; // The preset "X"
break;
case 'Full':
presetArray = [2,2,2,2,2,2,2,2,2]; // The preset "Full"
break;
}
// Obtains the buttons to click as a NodeList
const buttons = document.querySelector('.bm-highlight-grid')?.childNodes ?? [];
// For each button...
for (let buttonIndex = 0; buttonIndex < buttons.length; buttonIndex++) {
const button = buttons[buttonIndex]; // Gets the current button to check
// Gets the state of the button as a number
let buttonState = button.dataset['status'];
buttonState = (buttonState != 'Disabled') ? ((buttonState != 'Incorrect') ? 2 : 1) : 0;
// Finds the difference between the preset and the button
let buttonStateDelta = presetArray[buttonIndex] - buttonState;
// Since there is no difference, the button matches, so we skip it
if (buttonStateDelta == 0) {continue;}
// Makes the difference positive
buttonStateDelta += (buttonStateDelta < 0) ? 3 : 0;
/** At this point, these are the possible options:
* 1. The preset is zero and the button is two (-2) so we need to click once
* 2. The preset is one and the button is two (-1) so we need to click twice
* 3. The preset is one ahead of the button (1) so we need to click once
* 4. The preset is two ahead of the button (2) so we need to click twice
* Due to the addition of three in the line above, options 1 & 3 combine, and options 2 & 4 combine.
* Now the only options we have are:
* 1. If (1) then click once
* 2. If (2) then click twice
* Also due to the addition of three in the line above, our two options are POSITIVE numbers
*/
button.click(); // Clicks once
// Clicks a second time if needed
if (buttonStateDelta == 2) {
// For 0.2 seconds, or when the button is NOT disabled, wait for 10 milliseconds before attempting to continue
for (let timeWaited = 0; timeWaited < 200; timeWaited += 10) {
if (!button.disabled) {break;} // Breaks early once the button is enabled
await sleep(10);
}
button.click(); // Clicks again
}
}
// For each preset...
for (const button of presetButtons) {
button.disabled = false; // Re-enables the button
}
}
}

View file

@ -15,7 +15,7 @@ export function getWplaceVersion() {
/** Halts execution of this specific userscript, for the specified time.
* This will not block the thread.
* @param {number} - Time to wait in milliseconds
* @param {number} time - Time to wait in milliseconds
* @since 0.88.483
* @returns {Promise} Promise of a setTimeout()
*/