Swapped color filter table for a flexbox

This commit is contained in:
SwingTheVine 2026-02-17 04:18:51 -05:00
parent 83edb1552b
commit 04949f9082
11 changed files with 170 additions and 228 deletions

View file

@ -89,6 +89,12 @@
.bm-dragbar div:has(h1) {
display: contents;
}
.bm-window h2 {
display: inline-block;
font-size: larger;
font-weight: 700;
vertical-align: middle;
}
.bm-container {
margin: 0.5em 0;
}
@ -205,79 +211,47 @@ input[type=file] {
align-items: center;
gap: 0.5ch;
}
#bm-window-filter .bm-container:has(table) {
max-height: 60vh;
#bm-window-filter .bm-container:has(> .bm-filter-grid) {
max-height: calc(80vh - 150px);
overflow: auto;
}
#bm-window-filter table {
table-layout: fixed;
width: 50ch;
border-collapse: separate;
border-spacing: 0 0.5em;
.bm-filter-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 1em 3ch;
}
#bm-window-filter tr {
padding: 0.5em 0;
.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-filter-tbl-clr {
.bm-filter-color:hover,
.bm-filter-color:focus-within {
background-color: rgba(17, 40, 85, 0.9);
}
.bm-filter-container-rgb {
display: block;
border: thick double lightgray;
border: thick double darkslategray;
width: fit-content;
height: fit-content;
padding: 1ch;
}
.bm-filter-tbl-clr button {
.bm-filter-container-rgb button {
padding: 0.75em 0.5ch;
}
.bm-filter-tbl-clr svg {
.bm-filter-container-rgb svg {
width: 4ch;
isolation: isolate;
}
.bm-filter-tbl-id {
position: relative;
top: 0.75em;
left: 0.5ch;
.bm-filter-color > .bm-flex-between {
flex-direction: column;
align-items: flex-start;
gap: 0;
}
.bm-filter-tbl-prmim {
position: relative;
top: 0.75em;
left: -3.5ch;
}
.bm-filter-tbl-name {
position: relative;
top: -0.75em;
left: -16ch;
text-wrap: nowrap;
}
.bm-filter-tbl-crct {
display: block;
width: 100%;
position: relative;
right: 18ch;
top: 0.75em;
text-align: right;
}
.bm-filter-tbl-totl {
display: block;
width: 100%;
position: relative;
left: -16ch;
top: 0.75em;
text-align: left;
}
.bm-filter-tbl-totl::after {
content: "/";
position: absolute;
left: -1.5ch;
top: 50%;
transform: translateY(-50%);
}
#bm-window-filter tfoot {
display: table-header-group;
}
#bm-window-filter tfoot th {
text-align: left;
}
#bm-window-filter tfoot td {
text-align: right;
padding-left: 1ch;
.bm-filter-color small {
font-size: 0.75em;
}

View file

@ -2,7 +2,7 @@
// @name Blue Marble
// @name:en Blue Marble
// @namespace https://github.com/SwingTheVine/
// @version 0.88.207
// @version 0.88.222
// @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.
// @description:en 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
@ -2276,18 +2276,17 @@ Version: ${version}`, "readOnly": true }).buildElement().buildElement().addDiv({
}
}
const colorList = new Overlay(name, version);
colorList.addDiv({ "class": "bm-container" }).addTable({ "class": "bm-container" }).addCaption().addHeader(2, { "textContent": "Pixels In Templates By Palette Color" }).buildElement().buildElement().addTfoot().addTr().addTh({ "textContent": "Total Correct", "scope": "row" }).buildElement().addTd({ "textContent": allPixelsCorrectTotal.toString() }).buildElement().buildElement().addTr().addTh({ "textContent": "Total Pixels", "scope": "row" }).buildElement().addTd({ "textContent": allPixelsTotal.toString() }).buildElement().buildElement().buildElement().addThead({ "class": "bm-screenreader" }).addTr().addTh({ "textContent": "Hide Color", "scope": "col" }).buildElement().addTh({ "textContent": "ID", "scope": "col" }).buildElement().addTh({ "textContent": "Is Premium", "scope": "col" }).buildElement().addTh({ "textContent": "Name", "scope": "col" }).buildElement().addTh({ "textContent": "Correct Pixels", "scope": "col" }).buildElement().addTh({ "textContent": "Total Pixels", "scope": "col" }).buildElement().buildElement().buildElement();
colorList.addDiv({ "class": "bm-container" }).addDiv({ "class": "bm-filter-grid" });
for (const color of palette) {
const lumin = calculateRelativeLuminance(color.rgb);
const textColorForPaletteColorBackground = 1.05 / (lumin + 0.05) > (lumin + 0.05) / 0.05 ? "white" : "black";
const bgEffectForButtons = textColorForPaletteColorBackground == "white" ? "bm-button-hover-white" : "bm-button-hover-black";
colorList.addTr().addTd().addDiv({ "class": "bm-filter-tbl-clr", "style": `background-color: rgb(${color.rgb?.map((channel) => Number(channel) || 0).join(",")});` }).addButton({ "class": "bm-button-trans " + bgEffectForButtons, "aria-label": `Hide the color ${color.name || "color"} on templates`, "innerHTML": `<svg viewBox="0 .5 6 3"><path d="M0,2Q3-1 6,2Q3,5 0,2H2A1,1 0 1 0 3,1Q3,2 2,2" fill="${textColorForPaletteColorBackground}"/></svg>` }).buildElement().buildElement().buildElement().addTd().addSpan({ "class": "bm-filter-tbl-id", "textContent": `#${color.id}` }).buildElement().buildElement().addTd().addSpan({ "class": "bm-filter-tbl-prmim", "textContent": color.premium ? "\u2605" : "" }).buildElement().buildElement().addTd().addSpan({ "class": "bm-filter-tbl-name", "textContent": color.name }).buildElement().buildElement().addTd().addSpan({ "class": "bm-filter-tbl-crct", "textContent": middleEllipsis(String(allPixelsCorrect.get(color.id) ?? "???"), 7) }).buildElement().buildElement().addTd().addSpan({ "class": "bm-filter-tbl-totl", "textContent": middleEllipsis(String(allPixelsColor.get(color.id) ?? "0"), 7) }).buildElement().buildElement().buildElement();
let colorCorrect = allPixelsCorrect.get(color.id) ?? "???";
colorCorrect = typeof colorCorrect == "string" ? colorCorrect : new Intl.NumberFormat().format(colorCorrect);
const colorTotal = new Intl.NumberFormat().format(allPixelsColor.get(color.id) ?? 0);
const colorPercent = isNaN(colorCorrect / colorTotal) ? "unknown." : new Intl.NumberFormat(void 0, { style: "percent", minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(colorCorrect / colorTotal);
colorList.addDiv({ "class": "bm-container bm-filter-color bm-flex-between" }).addDiv({ "class": "bm-filter-container-rgb", "style": `background-color: rgb(${color.rgb?.map((channel) => Number(channel) || 0).join(",")});` }).addButton({ "class": "bm-button-trans " + bgEffectForButtons, "aria-label": `Hide the color ${color.name || "color"} on templates`, "innerHTML": `<svg viewBox="0 .5 6 3"><path d="M0,2Q3-1 6,2Q3,5 0,2H2A1,1 0 1 0 3,1Q3,2 2,2" fill="${textColorForPaletteColorBackground}"/></svg>` }).buildElement().buildElement().addDiv({ "class": "bm-flex-between" }).addHeader(2, { "textContent": (color.premium ? "\u2605 " : "") + color.name }).buildElement().addDiv({ "class": "bm-flex-between", "style": "gap: 1.5ch;" }).addSmall({ "textContent": `#${color.id}` }).buildElement().addSmall({ "textContent": `${colorCorrect} / ${colorTotal}` }).buildElement().buildElement().addP({ "textContent": `${colorTotal - colorCorrect || "Unknown"} incorrect pixels. Color completion ${colorPercent}` }).buildElement().buildElement().buildElement();
}
colorList.buildOverlay(windowContent);
function middleEllipsis(text, maxChars) {
if (text.length <= maxChars) return text;
const half = Math.floor((maxChars - 3) / 2);
return text.slice(0, half) + "\u2026" + text.slice(text.length - half);
}
}
})();

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
.bm-13{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.bm-1h{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000;transition:all .3s ease,transform 0s;top:75px;left:60px;width:auto;max-height:calc(100vh - 150px);max-width:calc(100% - 135px);font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-15{max-width:300px}.bm-1f{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:.5ch;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue" /></svg>') repeat;cursor:grab;width:100%;height:fit-content}.bm-1f.bm-1a{cursor:grabbing}.bm-1h:has(.bm-1f.bm-1a){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.bm-1f.bm-1a{pointer-events:auto}.bm-1g{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle}.bm-1h h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}.bm-1f h1{font-size:1.2em;user-select:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;text-shadow:3px 0px rgba(21,48,99,.5),-3px 0px rgba(21,48,99,.5),0px 3px rgba(21,48,99,.5),0px -3px rgba(21,48,99,.5),3px 3px rgba(21,48,99,.5),-3px 3px rgba(21,48,99,.5),3px -3px rgba(21,48,99,.5),-3px -3px rgba(21,48,99,.5)}.bm-1f div:has(h1){display:contents}.bm-19{margin:.5em 0}.bm-1h button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}.bm-1h button:hover,.bm-1h button:focus-visible{background-color:#1061e5}.bm-1h button:active,.bm-1h button:disabled{background-color:#2e97ff}.bm-1h button:disabled{text-decoration:line-through}.bm--{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}.bm-17{vertical-align:middle}.bm-17 svg{width:50%;margin:0 auto;fill:#111}.bm-1h button.bm-14{background-color:unset}.bm-14.bm-M:hover,.bm-14.bm-M:focus{background-color:#ffffff2b}.bm-14.bm-M:active{background-color:#ffffff38}.bm-14.bm-N:hover,.bm-14.bm-N:focus{background-color:#0000002b}.bm-14.bm-N:active{background-color:#00000038}input[type=number].bm-11{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}input[type=number].bm-11::-webkit-outer-spin-button,input[type=number].bm-11::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}div:has(>.bm-18)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bm-18,input[type=file]{display:none!important;visibility:hidden!important;position:absolute!important;left:-9999px!important;top:-9999px!important;width:0!important;height:0!important;opacity:0!important;z-index:-9999!important;pointer-events:none!important}.bm-U{overflow:hidden;transition:height .3s cubic-bezier(.4,0,.2,1)}.bm-1h textarea{font-size:small;background-color:#0003;padding:0 .5ch;height:5.25em;width:100%}.bm-1h small{font-size:x-small;color:#d3d3d3}.bm-12{display:flex;align-content:center;justify-content:space-between;align-items:center;gap:.5ch}.bm-flex-center{display:flex;align-content:center;justify-content:center;align-items:center;gap:.5ch}#bm-_ .bm-19:has(table){max-height:60vh;overflow:auto}#bm-_ table{table-layout:fixed;width:50ch;border-collapse:separate;border-spacing:0 .5em}#bm-_ tr{padding:.5em 0}.bm-X{display:block;border:thick double lightgray;width:fit-content;height:fit-content;padding:1ch}.bm-X button{padding:.75em .5ch}.bm-X svg{width:4ch;isolation:isolate}.bm-10{position:relative;top:.75em;left:.5ch}.bm-Q{position:relative;top:.75em;left:-3.5ch}.bm-R{position:relative;top:-.75em;left:-16ch;text-wrap:nowrap}.bm-S{display:block;width:100%;position:relative;right:18ch;top:.75em;text-align:right}.bm-T{display:block;width:100%;position:relative;left:-16ch;top:.75em;text-align:left}.bm-T:after{content:"/";position:absolute;left:-1.5ch;top:50%;transform:translateY(-50%)}#bm-_ tfoot{display:table-header-group}#bm-_ tfoot th{text-align:left}#bm-_ tfoot td{text-align:right;padding-left:1ch}
.bm-screenreader{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.bm-1d{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000;transition:all .3s ease,transform 0s;top:75px;left:60px;width:auto;max-height:calc(100vh - 150px);max-width:calc(100% - 135px);font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-10{max-width:300px}.bm-1b{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:.5ch;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue" /></svg>') repeat;cursor:grab;width:100%;height:fit-content}.bm-1b.bm-16{cursor:grabbing}.bm-1d:has(.bm-1b.bm-16){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.bm-1b.bm-16{pointer-events:auto}.bm-1c{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle}.bm-1d h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}.bm-1b h1{font-size:1.2em;user-select:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;text-shadow:3px 0px rgba(21,48,99,.5),-3px 0px rgba(21,48,99,.5),0px 3px rgba(21,48,99,.5),0px -3px rgba(21,48,99,.5),3px 3px rgba(21,48,99,.5),-3px 3px rgba(21,48,99,.5),3px -3px rgba(21,48,99,.5),-3px -3px rgba(21,48,99,.5)}.bm-1b div:has(h1){display:contents}.bm-1d h2{display:inline-block;font-size:larger;font-weight:700;vertical-align:middle}.bm-15{margin:.5em 0}.bm-1d button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}.bm-1d button:hover,.bm-1d button:focus-visible{background-color:#1061e5}.bm-1d button:active,.bm-1d button:disabled{background-color:#2e97ff}.bm-1d button:disabled{text-decoration:line-through}.bm-W{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}.bm-13{vertical-align:middle}.bm-13 svg{width:50%;margin:0 auto;fill:#111}.bm-1d button.bm-_{background-color:unset}.bm-_.bm-N:hover,.bm-_.bm-N:focus{background-color:#ffffff2b}.bm-_.bm-N:active{background-color:#ffffff38}.bm-_.bm-O:hover,.bm-_.bm-O:focus{background-color:#0000002b}.bm-_.bm-O:active{background-color:#00000038}input[type=number].bm-Y{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}input[type=number].bm-Y::-webkit-outer-spin-button,input[type=number].bm-Y::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}div:has(>.bm-14)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bm-14,input[type=file]{display:none!important;visibility:hidden!important;position:absolute!important;left:-9999px!important;top:-9999px!important;width:0!important;height:0!important;opacity:0!important;z-index:-9999!important;pointer-events:none!important}.bm-R{overflow:hidden;transition:height .3s cubic-bezier(.4,0,.2,1)}.bm-1d textarea{font-size:small;background-color:#0003;padding:0 .5ch;height:5.25em;width:100%}.bm-1d small{font-size:x-small;color:#d3d3d3}.bm-Z{display:flex;align-content:center;justify-content:space-between;align-items:center;gap:.5ch}.bm-flex-center{display:flex;align-content:center;justify-content:center;align-items:center;gap:.5ch}#bm-X .bm-15:has(>.bm-11){max-height:calc(80vh - 150px);overflow:auto}.bm-11{display:flex;flex-direction:row;flex-wrap:wrap;gap:1em 3ch}.bm--{width:fit-content;max-width:35ch;background-color:#153063e6;border-radius:1em;padding:.5em;gap:1ch;transition:background-color .3s ease}.bm--:hover,.bm--:focus-within{background-color:#112855e6}.bm-M{display:block;border:thick double darkslategray;width:fit-content;height:fit-content;padding:1ch}.bm-M button{padding:.75em .5ch}.bm-M svg{width:4ch}.bm-->.bm-Z{flex-direction:column;align-items:flex-start;gap:0}.bm-- small{font-size:.75em}

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-111hrs_12mins-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-705-black?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-720-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-498-blue?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Comments" src="https://img.shields.io/badge/Lines_Of_Comments-498-blue?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Compression" src="https://img.shields.io/badge/Compression-70.19%25-blue"></a>

4
package-lock.json generated
View file

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

View file

@ -1,6 +1,6 @@
{
"name": "wplace-bluemarble",
"version": "0.88.207",
"version": "0.88.222",
"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.88.207
// @version 0.88.222
// @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.
// @description:en 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

View file

@ -536,90 +536,94 @@ function buildWindowFilter() {
}
}
buildColorList();
// Creates the color list container
const colorList = new Overlay(name, version);
colorList.addDiv({'class': 'bm-container'})
.addTable({'class': 'bm-container'})
.addCaption()
.addHeader(2, {'textContent': 'Pixels In Templates By Palette Color'}).buildElement()
.buildElement()
.addTfoot()
.addTr()
.addTh({'textContent': 'Total Correct', 'scope': 'row'}).buildElement()
.addTd({'textContent': allPixelsCorrectTotal.toString()}).buildElement()
.buildElement()
.addTr()
.addTh({'textContent': 'Total Pixels', 'scope': 'row'}).buildElement()
.addTd({'textContent': allPixelsTotal.toString()}).buildElement()
.buildElement()
.buildElement()
.addThead({'class': 'bm-screenreader'})
.addTr()
.addTh({'textContent': 'Hide Color', 'scope': 'col'}).buildElement()
.addTh({'textContent': 'ID', 'scope': 'col'}).buildElement()
.addTh({'textContent': 'Is Premium', 'scope': 'col'}).buildElement()
.addTh({'textContent': 'Name', 'scope': 'col'}).buildElement()
.addTh({'textContent': 'Correct Pixels', 'scope': 'col'}).buildElement()
.addTh({'textContent': 'Total Pixels', 'scope': 'col'}).buildElement()
.buildElement()
.buildElement()
// Notice that there is no buildElement() for the table here?
// We leave the table open so we can continue to add children.
function buildColorList() {
// For each color in the palette...
for (const color of palette) {
const colorList = new Overlay(name, version);
colorList.addDiv({'class': 'bm-container'})
.addDiv({'class': 'bm-filter-flex'})
// We leave it open so we can add children to the grid
// Relative Luminance
const lumin = calculateRelativeLuminance(color.rgb);
// For each color in the palette...
for (const color of palette) {
// Calculates if white or black text would contrast better with the palette color
const textColorForPaletteColorBackground =
(((1.05) / (lumin + 0.05)) > ((lumin + 0.05) / 0.05))
? 'white' : 'black';
// Relative Luminance
const lumin = calculateRelativeLuminance(color.rgb);
const bgEffectForButtons = (textColorForPaletteColorBackground == 'white') ? 'bm-button-hover-white' : 'bm-button-hover-black';
// Calculates if white or black text would contrast better with the palette color
const textColorForPaletteColorBackground =
(((1.05) / (lumin + 0.05)) > ((lumin + 0.05) / 0.05))
? 'white' : 'black';
// <svg viewBox="0 1 12 6"><mask id="a"><path d="M0,0H12V8L0,2" fill="#fff"/></mask><path d="M0,4Q6-2 12,4Q6,10 0,4H4A2,2 0 1 0 6,2Q6,4 4,4ZM1,2L10,6.5L9.5,7L.5,2.5" mask="url(#a)"/></svg>
const bgEffectForButtons = (textColorForPaletteColorBackground == 'white') ? 'bm-button-hover-white' : 'bm-button-hover-black';
// <svg viewBox="0 1 12 6"><mask id="a"><path d="M0,0H12V8L0,2" fill="#fff"/></mask><path d="M0,4Q6-2 12,4Q6,10 0,4H4A2,2 0 1 0 6,2Q6,4 4,4ZM1,2L10,6.5L9.5,7L.5,2.5" mask="url(#a)"/></svg>
// Construct the DOM tree
colorList.addTr()
.addTd()
.addDiv({'class': 'bm-filter-tbl-clr', 'style': `background-color: rgb(${color.rgb?.map(channel => Number(channel) || 0).join(',')});`})
// Turns "correct" color into a string of a number or "???" if unknown
let colorCorrect = allPixelsCorrect.get(color.id) ?? '???';
colorCorrect = (typeof colorCorrect == 'string') ? colorCorrect : new Intl.NumberFormat().format(colorCorrect);
// Turns "total" color into a string of a number; "0" if unknown
const colorTotal = new Intl.NumberFormat().format(allPixelsColor.get(color.id) ?? 0);
// Gets the percentage of completion for this color
const colorPercent = isNaN(colorCorrect / colorTotal) ? 'unknown.' : new Intl.NumberFormat(undefined, { style: 'percent', minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(colorCorrect / colorTotal);
// Construct the DOM tree
colorList.addDiv({'class': 'bm-container bm-filter-color bm-flex-between'})
.addDiv({'class': 'bm-filter-container-rgb', 'style': `background-color: rgb(${color.rgb?.map(channel => Number(channel) || 0).join(',')});`})
.addButton({'class': 'bm-button-trans ' + bgEffectForButtons, 'aria-label': `Hide the color ${color.name || 'color'} on templates`, 'innerHTML': `<svg viewBox="0 .5 6 3"><path d="M0,2Q3-1 6,2Q3,5 0,2H2A1,1 0 1 0 3,1Q3,2 2,2" fill="${textColorForPaletteColorBackground}"/></svg>`}).buildElement()
.buildElement()
.addDiv({'class': 'bm-flex-between'})
.addHeader(2, {'textContent': (color.premium ? '★ ' : '') + color.name}).buildElement()
.addDiv({'class': 'bm-flex-between', 'style': 'gap: 1.5ch;'})
.addSmall({'textContent': `#${color.id}`}).buildElement()
.addSmall({'textContent': `${colorCorrect} / ${colorTotal}`}).buildElement()
.buildElement()
.addP({'textContent': `${(colorTotal - colorCorrect) || 'Unknown'} incorrect pixels. Color completion ${colorPercent}`}).buildElement()
.buildElement()
.buildElement()
.addTd()
.addSpan({'class': 'bm-filter-tbl-id', 'textContent': `#${color.id}`}).buildElement()
.buildElement()
.addTd()
.addSpan({'class': 'bm-filter-tbl-prmim', 'textContent': color.premium ? '★' : ''}).buildElement()
.buildElement()
.addTd()
.addSpan({'class': 'bm-filter-tbl-name', 'textContent': color.name}).buildElement()
.buildElement()
.addTd()
.addSpan({'class': 'bm-filter-tbl-crct', 'textContent': middleEllipsis(String(allPixelsCorrect.get(color.id) ?? '0'), 7)}).buildElement()
.buildElement()
.addTd()
.addSpan({'class': 'bm-filter-tbl-totl', 'textContent': middleEllipsis(String(allPixelsColor.get(color.id) ?? '0'), 7)}).buildElement()
.buildElement()
.buildElement()
}
}
function sortColorList(order) {
// "order" can be either 'asc' or 'desc'
const colorList = overlayFilter.querySelector('.bm-filter-flex');
const colors = Array.from(colorList.children);
colors.sort((index, nextIndex) => {
const indexValue = index.getAttribute('data-value');
const nextIndexValue = nextIndex.getAttribute('data-value');
const indexValueNumber = parseFloat(indexValue);
const nextIndexValueNumber = parseFloat(nextIndexValue);
const indexValueNumberIsNumber = !isNaN(indexValueNumber);
const nextIndexValueNumberIsNumber = !isNaN(nextIndexValueNumber);
// If both index values are numbers...
if (indexValueNumberIsNumber && nextIndexValueNumberIsNumber) {
// Perform numeric comparison
return order === 'asc' ? indexValueNumber - nextIndexValueNumber : nextIndexValueNumber - indexValueNumber;
} else {
// Otherwise, perform string comparison
const indexValueString = indexValue.toLowerCase();
const nextIndexValueString = nextIndexValue.toLowerCase();
if (indexValueString < nextIndexValueString) return order === 'asc' ? -1 : 1;
if (indexValueString > nextIndexValueString) return order === 'asc' ? 1 : -1;
return 0;
}
});
colors.forEach(color => colorList.appendChild(color));
}
// Adds the colors to the color container in the filter window
colorList.buildOverlay(windowContent);
// Overflow ellipse but the ellipse is in the middle, not end of the string
function middleEllipsis(text, maxChars) {
if (text.length <= maxChars) return text;
const half = Math.floor((maxChars - 3) / 2);
return (
text.slice(0, half) +
"…" +
text.slice(text.length - half)
);
}
}
function buildOverlayTabTemplate() {

View file

@ -114,6 +114,14 @@
display: contents;
}
/* Header 2 */
.bm-window h2 {
display: inline-block;
font-size: larger;
font-weight: 700;
vertical-align: middle;
}
/* Containers for "sections" of elements */
.bm-container {
margin: 0.5em 0;
@ -272,107 +280,64 @@ input[type="file"] {
gap: 0.5ch;
}
/* Filter window table container */
#bm-window-filter .bm-container:has(table) {
max-height: 60vh;
/* Filter flex container */
#bm-window-filter .bm-container:has(> .bm-filter-grid) {
max-height: calc(80vh - 150px);
overflow: auto;
}
/* Filter window table */
#bm-window-filter table {
table-layout: fixed;
width: 50ch;
border-collapse: separate;
border-spacing: 0 0.5em;
/* Filter flex */
.bm-filter-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 1em 3ch;
}
/* Filter window table row */
#bm-window-filter tr {
padding: 0.5em 0;
/* Filter color */
.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;
}
/* Filter window table cell for RGB color display */
.bm-filter-tbl-clr {
/* Filter color on hover */
.bm-filter-color:hover,
.bm-filter-color:focus-within {
background-color: rgba(17, 40, 85, 0.9);
}
/* Filter window container for RGB color display */
.bm-filter-container-rgb {
display: block;
border: thick double lightgray;
border: thick double darkslategray;
width: fit-content;
height: fit-content;
padding: 1ch;
}
/* Filter window hide color button */
.bm-filter-tbl-clr button {
.bm-filter-container-rgb button {
padding: 0.75em 0.5ch;
}
/* Filter window hide color button SVG */
.bm-filter-tbl-clr svg {
.bm-filter-container-rgb svg {
width: 4ch;
isolation: isolate;
}
/* Filter window color ID */
.bm-filter-tbl-id {
position: relative;
top: 0.75em;
left: 0.5ch;
/* Filter window container for color information */
.bm-filter-color > .bm-flex-between {
flex-direction: column;
align-items: flex-start;
gap: 0;
}
/* Filter window color premium */
.bm-filter-tbl-prmim {
position: relative;
top: 0.75em;
left: -3.5ch;
}
/* Filter window color name */
.bm-filter-tbl-name {
position: relative;
top: -0.75em;
left: -16ch;
text-wrap: nowrap;
}
/* Filter window color correct pixels */
.bm-filter-tbl-crct {
display: block;
width: 100%;
position: relative;
right: 18ch;
top: 0.75em;
text-align: right;
}
/* Filter window color total pixels */
.bm-filter-tbl-totl {
display: block;
width: 100%;
position: relative;
left: -16ch;
top: 0.75em;
text-align: left;
}
.bm-filter-tbl-totl::after {
content: "/";
position: absolute;
left: -1.5ch;
top: 50%;
transform: translateY(-50%);
}
/* Filter window table footer */
#bm-window-filter tfoot {
display: table-header-group;
}
/* Filter window table footer header */
#bm-window-filter tfoot th {
text-align: left;
}
/* Filter window table footer data */
#bm-window-filter tfoot td {
text-align: right;
padding-left: 1ch;
/* Filter window color flavor text */
.bm-filter-color small {
font-size: 0.75em;
}