From 99a163e7a95e1f4efee84e467b96f82da1cbdc1a Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 8 Oct 2025 05:52:14 +0200 Subject: [PATCH 01/17] initial version, need to manually add iro.js --- wled00/data/cpal/cpal.htm | 989 +++++++++++++++----------------------- 1 file changed, 386 insertions(+), 603 deletions(-) diff --git a/wled00/data/cpal/cpal.htm b/wled00/data/cpal/cpal.htm index b8e0e08be1..a8a7c5597f 100644 --- a/wled00/data/cpal/cpal.htm +++ b/wled00/data/cpal/cpal.htm @@ -1,646 +1,429 @@ - - - - - - WLED Custom Palette Editor - - - - - + + + WLED Palette Editor + + + + + -
-
-

- - - - - - - WLED Palette Editor -

-
+
+

WLED Palette Editor

+ +
-
-
-
-
- -
Custom palettes
+
+ + +
-
-
-
Click gradient to add. Box = color. Red = delete. Arrow = upload. Pencil = edit.
-
-
-
-
Static palettes
+
+
-
- - + - - + \ No newline at end of file From 492715136d9c0875cab619c8455bf63d08acccca Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 8 Oct 2025 08:03:59 +0200 Subject: [PATCH 02/17] serve iro.js as a seperate file, UX improvements in cpal.h separating iro.js adds about 400bytes to the bin file, this is due to reduced gzip compression when using it as an external file (no dictionary reuse in index.x) --- .gitignore | 1 + tools/cdata.js | 45 +++++- wled00/data/cpal/cpal.htm | 316 ++++++++++++++++++++++++++++++++------ wled00/wled_server.cpp | 6 + 4 files changed, 315 insertions(+), 53 deletions(-) diff --git a/.gitignore b/.gitignore index ec9d4efcc3..a3c1bfbdcc 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ wled-update.sh /wled00/Release /wled00/wled00.ino.cpp /wled00/html_*.h +/wled00/js_*.h diff --git a/tools/cdata.js b/tools/cdata.js index c05b28e522..a6be98d617 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -126,16 +126,38 @@ async function minify(str, type = "plain") { throw new Error("Unknown filter: " + type); } -async function writeHtmlGzipped(sourceFile, resultFile, page) { +// protect specific JS ', options); return js.replace(/<[\/]*script>/g, ''); @@ -147,6 +148,14 @@ async function writeHtmlGzipped(sourceFile, resultFile, page, opts = {}) { const originalHtml = fs.readFileSync(sourceFile, "utf8"); const preprocessedHtml = protectScriptExclusions(originalHtml, excludeScripts); + // Apply JavaScript preprocessing if advanced minify is requested + if (opts.advancedMinify) { + const preserveFunctions = options.preserveFunctions || []; + const reservedVars = options.reservedVars || []; // Add reserved variable names + console.info("Applying advanced JavaScript minification to " + sourceFile); + html = await preprocessJavaScript(html, preserveFunctions, reservedVars); + } + inline.html({ fileContent: preprocessedHtml, relativeTo: path.dirname(sourceFile), @@ -180,8 +189,18 @@ async function specToChunk(srcDir, s) { let str = buf.toString("utf-8"); str = adoptVersionAndRepo(str); const originalLength = str.length; + + // Apply JavaScript preprocessing if advanced minify is requested + if (s.advancedMinify) { + console.info("Applying advanced JavaScript minification to " + s.file); + str = await preprocessJavaScript( + str, + s.preserveFunctions || [], + s.reservedVars || [] // Add reserved variable names + ); + } + if (s.method == "gzip") { - if (s.mangle) str = s.mangle(str); const zip = zlib.gzipSync(await minify(str, s.filter), { level: zlib.constants.Z_BEST_COMPRESSION }); console.info("Minified and compressed " + s.file + " from " + originalLength + " to " + zip.length + " bytes"); const result = hexdump(zip); @@ -204,6 +223,57 @@ async function specToChunk(srcDir, s) { throw new Error("Unknown method: " + s.method); } +/** + * Preprocesses JavaScript in HTML content using Terser + * @param {string} html - HTML content with embedded JavaScript + * @param {string[]} preserveFunctions - Array of function names to preserve + * @returns {Promise} - Processed HTML with minified JavaScript + */ +/** + * Preprocesses JavaScript in HTML content using Terser + * @param {string} html - HTML content with embedded JavaScript + * @param {string[]} preserveFunctions - Array of function names to preserve + * @param {string[]} reservedVars - Array of variable names to avoid using + * @returns {Promise} - Processed HTML with minified JavaScript + */ +async function preprocessJavaScript(html, preserveFunctions = [], reservedVars = []) { + if (typeof html !== 'string') { + console.warn("Warning: Input to preprocessJavaScript is not a string"); + return html; + } + + const scriptRegex = /`); + } + } catch (err) { + console.warn(`Warning: JavaScript preprocessing error: ${err.message.substring(0, 100)}`); + } + } + + return result; +} + async function writeChunks(srcDir, specs, resultFile) { let src = multiHeader; for (const s of specs) { @@ -278,7 +348,10 @@ writeChunks( file: "cpal.htm", name: "PAGE_cpal", method: "gzip", - filter: "html-minify" + filter: "html-minify", + advancedMinify: false, // set to true to enable advanced JS minification, saves about 100 bytes only. + preserveFunctions: ['onclick'], // add functions here that should not be minified + reservedVars: ['d', 'n'] // d and n are used in common.js } ], "wled00/html_cpal.h" diff --git a/wled00/data/cpal/cpal.htm b/wled00/data/cpal/cpal.htm index ac57a91bc1..d150776856 100644 --- a/wled00/data/cpal/cpal.htm +++ b/wled00/data/cpal/cpal.htm @@ -14,12 +14,11 @@ @import url("style.css"); :root { --bg:#111; --fg:#ddd; --bd:#333; - --pad:16px; /* side padding aligning editor and previews */ - --maxw:820px; /* max working width */ - --namew:16ch; /* 2/3 of 24ch to reduce name width by ~30% */ + --pad:16px; + --maxw:820px; + --namew:16ch; } button.sml { text-overflow: clip; } - * { box-sizing: border-box; } body { margin:0; background:var(--bg); color:var(--fg); font:14px/1.35 system-ui, Segoe UI, Arial, sans-serif; } @@ -32,13 +31,10 @@ header { text-align:center; margin-bottom:10px; width:100%; } header h1 { margin:0; font-size:20px; font-weight:600; } - - #pickerWrap { display:flex; justify-content:center; margin:8px 0 12px; width:100%; } - + #pickerWrap { display:flex; flex-direction:column; align-items:center; margin:8px 0 12px; width:100%; } .bar { display:flex; flex-wrap:wrap; gap:10px; align-items:center; justify-content:center; margin:8px 0 12px; width:100%; max-width:var(--maxw); padding:0 var(--pad); } .small { font-size:12px; opacity:0.85; } - /* Editor gradient row (frozen top) */ #editor { width:100%; display:flex; justify-content:center; position: sticky; top: 0; z-index: 5; background: var(--bg); padding: 6px 0; @@ -49,94 +45,87 @@ } #grad { height:100%; border-radius:6px; } - /* Markers (50% wider -> 15px) */ .mk { position:absolute; top:50%; transform:translate(-50%,-50%); width:15px; height:34px; border:4px solid #0008; border-radius:6px; background:#999; touch-action:none; } .mk.sel { outline:4px solid #0bf; border-color:#000; } .mk[data-lock="1"] { background:#666; } section { margin-top:18px; width:100%; } - - /* List rows with flex: - Name (fixed 16ch) | Preview (flex) | Save button (right) - Names hide on narrow screens, preview grows to fill space. - */ .table { max-width:var(--maxw); width:100%; margin:0 auto; padding:0 var(--pad); } .list { display:flex; flex-direction:column; gap:8px; width:100%; } .row { display:flex; align-items:center; gap:10px; width:100%; } - .row .sml:last-child { margin-left: auto; } /* right-align the last button in row when present */ + .row .sml:last-child { margin-left: auto; } .name { flex:0 0 var(--namew); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; opacity:.95; } .name .by { display:block; font-size:12px; opacity:.8; } .preview { flex:1 1 50%; min-width:100px; height:26px; border-radius:6px; border:1px solid var(--bd); } .preview.clickable { cursor: pointer; } - /* Sticky "new slot" row (if present) */ - .row.sticky-new { position: sticky; top: 48px; z-index: 3; background: var(--bg); padding: 4px 0; } - - /* Responsive: hide names before touching preview */ @media (max-width: 760px) { .name { display:none; } .preview { flex-basis: auto; } } - /* Categories (external palettes) - restored collapsed styles with chevron */ .categories { max-width:var(--maxw); width:100%; padding:0 var(--pad); } details.category { border:1px solid var(--bd); border-radius:8px; margin:10px 0; background: #101010; } details.category > summary { cursor:pointer; padding:8px 10px; display:flex; align-items:center; gap:8px; color: var(--fg); - list-style: none; + list-style: none; font-size: 18px; font-weight: 500; } details.category > summary::-webkit-details-marker { display:none; } - details.category > summary::before { - display:inline-block; transform: translateY(-1px); transition: transform .15s ease; - width: 1em; text-align: center; opacity: .9; - } - details.category[open] > summary::before { - transform: translateY(-1px); - } .cat-body { padding:8px 10px 12px; display:flex; flex-direction:column; gap:8px; } + .chk-label { + display: flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; + justify-content: center; padding: 4px 0; + } + .chk-label input[type="checkbox"] { + width: 16px; height: 16px; cursor: pointer; + accent-color: var(--c-6); + background-color: var(--c-2); + border: 1px solid var(--c-4); + } + + .rgb-inputs { display: flex; gap: 6px; margin-top: 8px; align-items: center; } + .rgb-inputs input { width: 60px; padding: 4px; text-align: center; } + .rgb-inputs label { font-size: 12px; opacity: 0.8; } + + #memWarn { + display: none; + color: #ff9900; + font-size: 16px; + margin-bottom: 8px; + text-align: center; + } + #memWarn a { color: #ffcc00; text-decoration: underline; }

WLED Palette Editor

- -
- +
+
+
+ + + +
+
-
- +
-
- Custom slots - -
-
-
-
+ Warning: Adding many custom palettes might cause stability issues, create backups before proceeding. +
- -
- WLED palettes -
-
-
-
- - -
- External palettes -
-
- - +
@@ -144,502 +133,452 @@ - \ No newline at end of file From ec26c81ee949e6931d84771c6ab4a4d89cd252c5 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 9 Oct 2025 19:01:35 +0200 Subject: [PATCH 04/17] revert changes to cdata.js, change @import to link for css link properly minifies, @import is skipped completely. --- tools/cdata.js | 81 +----------------- wled00/data/cpal/cpal.htm | 174 ++++++++++++++++++-------------------- 2 files changed, 85 insertions(+), 170 deletions(-) diff --git a/tools/cdata.js b/tools/cdata.js index 5fe1a0d711..a6be98d617 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -22,7 +22,6 @@ const zlib = require("node:zlib"); const CleanCSS = require("clean-css"); const minifyHtml = require("html-minifier-terser").minify; const packageJson = require("../package.json"); -const { minify: terserMinify } = require("terser"); // Export functions for testing module.exports = { isFileNewerThan, isAnyFileInFolderNewerThan }; @@ -105,7 +104,7 @@ async function minify(str, type = "plain") { conservativeCollapse: true, // preserve spaces in text collapseBooleanAttributes: true, collapseInlineTagWhitespace: true, - minifyCSS: (css) => new CleanCSS({ level: 2 }).minify(css).styles, + minifyCSS: true, minifyJS: true, removeAttributeQuotes: true, removeComments: true, @@ -116,7 +115,7 @@ async function minify(str, type = "plain") { if (type == "plain") { return str; } else if (type == "css-minify") { - return new CleanCSS({ level: 2 }).minify(str).styles; // Updated to level 2 + return new CleanCSS({}).minify(str).styles; } else if (type == "js-minify") { let js = await minifyHtml('', options); return js.replace(/<[\/]*script>/g, ''); @@ -148,14 +147,6 @@ async function writeHtmlGzipped(sourceFile, resultFile, page, opts = {}) { const originalHtml = fs.readFileSync(sourceFile, "utf8"); const preprocessedHtml = protectScriptExclusions(originalHtml, excludeScripts); - // Apply JavaScript preprocessing if advanced minify is requested - if (opts.advancedMinify) { - const preserveFunctions = options.preserveFunctions || []; - const reservedVars = options.reservedVars || []; // Add reserved variable names - console.info("Applying advanced JavaScript minification to " + sourceFile); - html = await preprocessJavaScript(html, preserveFunctions, reservedVars); - } - inline.html({ fileContent: preprocessedHtml, relativeTo: path.dirname(sourceFile), @@ -189,18 +180,8 @@ async function specToChunk(srcDir, s) { let str = buf.toString("utf-8"); str = adoptVersionAndRepo(str); const originalLength = str.length; - - // Apply JavaScript preprocessing if advanced minify is requested - if (s.advancedMinify) { - console.info("Applying advanced JavaScript minification to " + s.file); - str = await preprocessJavaScript( - str, - s.preserveFunctions || [], - s.reservedVars || [] // Add reserved variable names - ); - } - if (s.method == "gzip") { + if (s.mangle) str = s.mangle(str); const zip = zlib.gzipSync(await minify(str, s.filter), { level: zlib.constants.Z_BEST_COMPRESSION }); console.info("Minified and compressed " + s.file + " from " + originalLength + " to " + zip.length + " bytes"); const result = hexdump(zip); @@ -223,57 +204,6 @@ async function specToChunk(srcDir, s) { throw new Error("Unknown method: " + s.method); } -/** - * Preprocesses JavaScript in HTML content using Terser - * @param {string} html - HTML content with embedded JavaScript - * @param {string[]} preserveFunctions - Array of function names to preserve - * @returns {Promise} - Processed HTML with minified JavaScript - */ -/** - * Preprocesses JavaScript in HTML content using Terser - * @param {string} html - HTML content with embedded JavaScript - * @param {string[]} preserveFunctions - Array of function names to preserve - * @param {string[]} reservedVars - Array of variable names to avoid using - * @returns {Promise} - Processed HTML with minified JavaScript - */ -async function preprocessJavaScript(html, preserveFunctions = [], reservedVars = []) { - if (typeof html !== 'string') { - console.warn("Warning: Input to preprocessJavaScript is not a string"); - return html; - } - - const scriptRegex = /`); - } - } catch (err) { - console.warn(`Warning: JavaScript preprocessing error: ${err.message.substring(0, 100)}`); - } - } - - return result; -} - async function writeChunks(srcDir, specs, resultFile) { let src = multiHeader; for (const s of specs) { @@ -348,10 +278,7 @@ writeChunks( file: "cpal.htm", name: "PAGE_cpal", method: "gzip", - filter: "html-minify", - advancedMinify: false, // set to true to enable advanced JS minification, saves about 100 bytes only. - preserveFunctions: ['onclick'], // add functions here that should not be minified - reservedVars: ['d', 'n'] // d and n are used in common.js + filter: "html-minify" } ], "wled00/html_cpal.h" diff --git a/wled00/data/cpal/cpal.htm b/wled00/data/cpal/cpal.htm index d150776856..5c9652e18e 100644 --- a/wled00/data/cpal/cpal.htm +++ b/wled00/data/cpal/cpal.htm @@ -3,26 +3,17 @@ WLED Palette Editor - + -
+

WLED Palette Editor

-
+
@@ -117,15 +102,15 @@
-