diff --git a/wled00/data/index.css b/wled00/data/index.css
index f4b4eaf15..20ba28d98 100644
--- a/wled00/data/index.css
+++ b/wled00/data/index.css
@@ -402,9 +402,8 @@ button {
}
#imgw {
- width: 200px;
- height: 55px;
display: inline-block;
+ margin: 8px;
}
#kv, #kn {
@@ -432,6 +431,12 @@ img {
max-height: 100%;
}
+.wi {
+ image-rendering: pixelated;
+ image-rendering: crisp-edges;
+ width: 210px;
+}
+
@keyframes fadein {
from {bottom: 0; opacity: 0;}
to {bottom: calc(var(--bh) + 22px); opacity: 1;}
@@ -519,12 +524,16 @@ input[type=range]:active + .sliderbubble {
position: relative;
}
+.sbs {
+ margin: 0px -20px 5px -6px;
+}
+
.sws {
- width: 212px;
+ width: 230px;
}
.sis {
- width: 192px !important;
+ width: 214px !important;
}
.hd {
@@ -561,12 +570,11 @@ input[type=range]:active + .sliderbubble {
}
.btn-s {
- padding: 9px;
width: 276px;
background-color: var(--c-2);
}
.btn-i {
- padding-bottom: 3px;
+ padding-bottom: 4px;
}
.btn-icon {
margin: 0px 8px 4px 0;
@@ -578,6 +586,19 @@ input[type=range]:active + .sliderbubble {
.btn-p {
width: 216px;
}
+.btn-xs {
+ width: 39px;
+}
+.btn-pl-add {
+ position: absolute;
+ bottom: -29px;
+ left: 94px;
+}
+.btn-pl-del {
+ position: absolute;
+ right: 2px;
+ bottom: -3px;
+}
#qcs-w {
margin-top: 10px;
@@ -601,6 +622,16 @@ input[type=range]:active + .sliderbubble {
width: 42px;
}
+.sel-pl {
+ width: 200px;
+ background-position: 176px 16px;
+}
+
+.sel-ple {
+ width: 216px;
+ background-position: 192px 16px;
+}
+
select {
-webkit-appearance: none;
-moz-appearance: none;
@@ -665,6 +696,11 @@ input[type=text] {
margin: 26px 0 6px 12px !important;
}
+.plentry {
+ margin-top: 16px;
+ position: relative;
+}
+
.stxt {
width: 50px !important;
}
@@ -741,9 +777,9 @@ input[type=number]::-webkit-outer-spin-button {
.cnf-s {
position: absolute;
- top: 66px;
- right: 28px;
- padding: 43px 6px;
+ top: 172px;
+ right: 23px;
+ padding: 7.5px 22px;
}
.pwr {
@@ -1016,6 +1052,16 @@ input[type="text"].search:not(:placeholder-shown) {
color: red;
}
+.hrz {
+ width: auto;
+ height: 2px;
+ background-color: var(--c-e);
+}
+.hrz-pl {
+ margin: 20px 0;
+ position: relative;
+}
+
::-webkit-scrollbar {
width: 6px;
}
diff --git a/wled00/data/index.htm b/wled00/data/index.htm
index 825832e4d..a998a9f3f 100644
--- a/wled00/data/index.htm
+++ b/wled00/data/index.htm
@@ -165,15 +165,7 @@
Loading...
-
- First preset:
- Last preset:
- Time per preset: s
- Transition: s
+ Transition s
@@ -189,7 +181,7 @@
-

+
Loading...
diff --git a/wled00/data/index.js b/wled00/data/index.js
index 2499c1fc8..0833cf147 100644
--- a/wled00/data/index.js
+++ b/wled00/data/index.js
@@ -14,11 +14,12 @@ var currentPreset = -1;
var lastUpdate = 0;
var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0;
var pcMode = false, pcModeA = false, lastw = 0;
+var tr = 7;
var d = document;
const ranges = RangeTouch.setup('input[type="range"]', {});
var palettesData;
var pJson = {};
-var pN = "", pI = 0;
+var pN = "", pI = 0, pNum = 0;
var pmt = 1, pmtLS = 0, pmtLast = 0;
var lastinfo = {};
var cfg = {
@@ -304,6 +305,10 @@ function pName(i) {
return n;
}
+function isPlaylist(i) {
+ return pJson[i].playlist && pJson[i].playlist.ps;
+}
+
function papiVal(i) {
if (!pJson[i]) return "";
var o = Object.assign({},pJson[i]);
@@ -418,9 +423,9 @@ function populatePresets(fromls)
var cn = "";
var arr = Object.entries(pJson);
arr.sort(cmpP);
- var added = false;
pQL = [];
var is = [];
+ pNum = 0;
for (var key of (arr||[]))
{
@@ -432,15 +437,15 @@ function populatePresets(fromls)
cn += `
`;
if (cfg.comp.pid) cn += `
${i}
`;
- cn += `
${pName(i)}
+ cn += `
${isPlaylist(i)?"":""}${pName(i)}
`;
- added = true;
+ pNum++;
}
d.getElementById('pcont').innerHTML = cn;
- if (added) {
+ if (pNum > 0) {
if (pmtLS != pmt && pmt != 0) {
localStorage.setItem("wledPmt", pmt);
pJson["0"] = {};
@@ -451,6 +456,7 @@ function populatePresets(fromls)
let i = is[a];
if (expanded[i+100]) expand(i+100, true);
}
+ makePlSel(arr);
} else { presetError(true); }
updatePA();
populateQL();
@@ -487,8 +493,8 @@ function populateInfo(i)
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
${inforow("Uptime",getRuntimeStr(i.uptime))}
${inforow("Free heap",heap," kB")}
- ${inforow("Estimated current",pwru)}
- ${inforow("Frames / second",i.leds.fps)}
+ ${inforow("Estimated current",pwru)}
+ ${inforow("Frames / second",i.leds.fps)}
${inforow("MAC address",i.mac)}
${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB (" +Math.round(i.fs.u*100/i.fs.t) + "%)")}
${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")}
@@ -522,34 +528,39 @@ function populateSegments(s)
-
-
-
-
-
-
-
+
+
+
+
+
`;
@@ -1184,37 +1192,148 @@ function resetUtil() {
d.getElementById('segutil').innerHTML = cn;
}
-function makeP(i) {
+var plJson = {"0":{
+ "ps": [0],
+ "dur": [100],
+ "transition": [-1], //to be inited to default transition dur
+ "repeat": 0,
+ "r": false,
+ "end": 0
+}};
+
+var plSelContent = "";
+function makePlSel(arr) {
+ plSelContent = "";
+ for (var i = 0; i < arr.length; i++) {
+ var n = arr[i][1].n ? arr[i][1].n : "Preset " + arr[i][0];
+ if (arr[i][1].playlist && arr[i][1].playlist.ps) continue; //remove playlists, sub-playlists not yet supported
+ plSelContent += ``
+ }
+}
+
+function refreshPlE(p) {
+ var plEDiv = d.getElementById(`ple${p}`);
+ if (!plEDiv) return;
+ var content = "";
+ for (var i = 0; i < plJson[p].ps.length; i++) {
+ content += makePlEntry(p,i);
+ }
+ plEDiv.innerHTML = content;
+ var dels = plEDiv.getElementsByClassName("btn-pl-del");
+ if (dels.length < 2 && p > 0) dels[0].style.display = "none";
+
+ var sels = d.getElementById(`seg${p+100}`).getElementsByClassName("sel");
+ for (var i of sels) {
+ if (i.dataset.val) {
+ if (parseInt(i.dataset.val) > 0) i.value = i.dataset.val;
+ else plJson[p].ps[i.dataset.index] = parseInt(i.value);
+ }
+ }
+}
+
+//p: preset ID, i: ps index
+function addPl(p,i) {
+ plJson[p].ps.splice(i+1,0,0);
+ plJson[p].dur.splice(i+1,0,plJson[p].dur[i]);
+ plJson[p].transition.splice(i+1,0,plJson[p].transition[i]);
+ refreshPlE(p);
+}
+
+function delPl(p,i) {
+ if (plJson[p].ps.length < 2) {if (p == 0) resetPUtil(); return;}
+ plJson[p].ps.splice(i,1);
+ plJson[p].dur.splice(i,1);
+ plJson[p].transition.splice(i,1);
+ refreshPlE(p);
+}
+
+function plePs(p,i,field) {
+ plJson[p].ps[i] = parseInt(field.value);
+}
+
+function pleDur(p,i,field) {
+ if (field.validity.valid)
+ plJson[p].dur[i] = Math.floor(field.value*10);
+}
+
+function pleTr(p,i,field) {
+ if (field.validity.valid)
+ plJson[p].transition[i] = Math.floor(field.value*10);
+}
+
+function plR(p) {
+ var pl = plJson[p];
+ pl.r = d.getElementById(`pl${p}rtgl`).checked;
+ if (d.getElementById(`pl${p}rptgl`).checked) { //infinite
+ pl.repeat = 0;
+ delete pl.end;
+ d.getElementById(`pl${p}o1`).style.display = "none";
+ } else {
+ pl.repeat = parseInt(d.getElementById(`pl${p}rp`).value);
+ pl.end = parseInt(d.getElementById(`pl${p}selEnd`).value);
+ d.getElementById(`pl${p}o1`).style.display = "block";
+ }
+}
+
+function makeP(i,pl) {
+ var content = "";
+ if (pl) {
+ var rep = plJson[i].repeat ? plJson[i].repeat : 0;
+ content = `
+
+
+
+
Repeat 0?rep:1}> times
+ End preset:
+
+
+ `;
+ }
+ else content = `
+ `;
+
return `
Quick load label:
(leave empty for no Quick load button)
+ `;
- updateTrail(d.getElementById('p0p'));
+}
+
+function makePlEntry(p,i) {
+ return `
+
+ ${i+1}:
+
+
+
+
+
+ `;
+}
+
+function makePlUtil() {
+ if (pNum < 2) {
+ showToast("You need at least 2 presets to make a playlist!"); return;
+ }
+ if (plJson[0].transition[0] < 0) plJson[0].transition[0] = tr;
+ d.getElementById('putil').innerHTML = `
+
+ New playlist
+
+ ${makeP(0,true)}
`;
+
+ refreshPlE(0);
}
function resetPUtil() {
- var cn = `
`;
+ var cn = `
+
`;
d.getElementById('putil').innerHTML = cn;
}
@@ -1266,8 +1422,10 @@ function setSeg(s){
{
var grp = parseInt(d.getElementById(`seg${s}grp`).value);
var spc = parseInt(d.getElementById(`seg${s}spc`).value);
+ var ofs = parseInt(d.getElementById(`seg${s}of` ).value);
obj.seg.grp = grp;
obj.seg.spc = spc;
+ obj.seg.of = ofs;
}
requestJson(obj);
}
@@ -1339,7 +1497,7 @@ function setPalette(paletteId = null)
function setBri() {
var obj = {"bri": parseInt(d.getElementById('sliderBri').value)};
- obj.transition = parseInt(d.getElementById('cyctt').value*10);
+ obj.transition = parseInt(d.getElementById('tt').value*10);
requestJson(obj);
}
@@ -1358,17 +1516,6 @@ function setLor(i) {
requestJson(obj);
}
-function toggleCY() {
- var obj = {"pl" : -1};
- if (d.getElementById('cyToggle').checked)
- {
- obj = {"pl": 0, "ccnf": {"min": parseInt(d.getElementById('cycs').value), "max": parseInt(d.getElementById('cyce').value), "time": parseInt(d.getElementById('cyct').value*10)}};
- obj.transition = parseInt(d.getElementById('cyctt').value*10);
- }
-
- requestJson(obj);
-}
-
function setPreset(i) {
var obj = {"ps": i};
@@ -1377,12 +1524,14 @@ function setPreset(i) {
requestJson(obj);
}
-function saveP(i) {
+function saveP(i,pl) {
pI = parseInt(d.getElementById(`p${i}id`).value);
if (!pI || pI < 1) pI = (i>0) ? i : getLowestUnusedP();
pN = d.getElementById(`p${i}txt`).value;
- if (pN == "") pN = "Preset " + pI;
+
+ if (pN == "") pN = (pl?"Playlist ":"Preset ") + pI;
var obj = {};
+
if (!d.getElementById(`p${i}cstgl`).checked) {
var raw = d.getElementById(`p${i}api`).value;
try {
@@ -1396,15 +1545,21 @@ function saveP(i) {
d.getElementById(`p${i}warn`).innerHTML = "⚠ Syntax error in custom JSON API command";
return;
} else if (raw.indexOf("Please") == 0) {
- d.getElementById(`p${i}warn`).innerHTML = "⚠ Please refresh the page before modifying this preset";
+ d.getElementById(`p${i}warn`).innerHTML = "⚠ Please refresh the page before modifying this preset";
return;
- }
+ }
}
obj.o = true;
} else {
- obj.ib = d.getElementById(`p${i}ibtgl`).checked;
- obj.sb = d.getElementById(`p${i}sbtgl`).checked;
+ if (pl) {
+ obj.playlist = plJson[i];
+ obj.o = true;
+ } else {
+ obj.ib = d.getElementById(`p${i}ibtgl`).checked;
+ obj.sb = d.getElementById(`p${i}sbtgl`).checked;
+ }
}
+
obj.psave = pI; obj.n = pN;
var pQN = d.getElementById(`p${i}ql`).value;
if (pQN.length > 0) obj.ql = pQN;
@@ -1426,11 +1581,36 @@ function saveP(i) {
resetPUtil();
}
-function delP(i) {
- var obj = {"pdel": i};
+function testPl(i,bt) {
+ if (bt.dataset.test == 1) {
+ bt.dataset.test = 0;
+ bt.innerHTML = "Test";
+ stopPl();
+ return;
+ }
+ bt.dataset.test = 1;
+ bt.innerHTML = "Stop";
+ var obj = {};
+ obj.playlist = plJson[i];
requestJson(obj);
- delete pJson[i];
- populatePresets();
+}
+
+function stopPl() {
+ requestJson({playlist:{}})
+}
+
+function delP(i) {
+ var bt = d.getElementById(`p${i}del`);
+ if (bt.dataset.cnf == 1) {
+ var obj = {"pdel": i};
+ requestJson(obj);
+ delete pJson[i];
+ populatePresets();
+ } else {
+ bt.style.color = "#f00";
+ bt.innerHTML = "Confirm delete";
+ bt.dataset.cnf = 1;
+ }
}
function selectSlot(b) {
@@ -1530,7 +1710,7 @@ function setColor(sr) {
}
updateHex();
updateRgb();
- obj.transition = parseInt(d.getElementById('cyctt').value*10);
+ obj.transition = parseInt(d.getElementById('tt').value*10);
requestJson(obj);
}
@@ -1658,22 +1838,61 @@ function cancelSearch(ic) {
searchField.focus();
}
+//make sure "dur" and "transition" are arrays with at least the length of "ps"
+function formatArr(pl) {
+ var l = pl.ps.length;
+ if (!Array.isArray(pl.dur)) {
+ var v = pl.dur;
+ if (isNaN(v)) v = 100;
+ pl.dur = [v];
+ }
+ var l2 = pl.dur.length;
+ if (l2 < l)
+ {
+ for (var i = 0; i < l - l2; i++)
+ pl.dur.push(pl.dur[l2-1]);
+ }
+
+ if (!Array.isArray(pl.transition)) {
+ var v = pl.transition;
+ if (isNaN(v)) v = tr;
+ pl.transition = [v];
+ }
+ var l2 = pl.transition.length;
+ if (l2 < l)
+ {
+ for (var i = 0; i < l - l2; i++)
+ pl.transition.push(pl.transition[l2-1]);
+ }
+}
+
function expand(i,a)
{
if (!a) expanded[i] = !expanded[i];
d.getElementById('seg' +i).style.display = (expanded[i]) ? "block":"none";
d.getElementById('sege' +i).style.transform = (expanded[i]) ? "rotate(180deg)":"rotate(0deg)";
- if (i > 100) { //presets
- var p = i-100;
- d.getElementById(`p${p}o`).style.background = (expanded[i] || p != currentPreset)?"var(--c-2)":"var(--c-6)";
- if (d.getElementById('seg' +i).innerHTML == "") {
- d.getElementById('seg' +i).innerHTML = makeP(p);
- var papi = papiVal(p);
- d.getElementById(`p${p}api`).value = papi;
- if (papi.indexOf("Please") == 0) d.getElementById(`p${p}cstgl`).checked = true;
- tglCs(p);
- }
+ if (i < 100) return; //no preset, we are done
+
+ var p = i-100;
+ d.getElementById(`p${p}o`).style.background = (expanded[i] || p != currentPreset)?"var(--c-2)":"var(--c-6)";
+ if (d.getElementById('seg' +i).innerHTML != "") return;
+ if (isPlaylist(p)) {
+ plJson[p] = pJson[p].playlist;
+ //make sure all keys are present in plJson[p]
+ formatArr(plJson[p]);
+ if (isNaN(plJson[p].repeat)) plJson[p].repeat = 0;
+ if (!plJson[p].r) plJson[p].r = false;
+ if (isNaN(plJson[p].end)) plJson[p].end = 0;
+
+ d.getElementById('seg' +i).innerHTML = makeP(p,true);
+ refreshPlE(p);
+ } else {
+ d.getElementById('seg' +i).innerHTML = makeP(p);
}
+ var papi = papiVal(p);
+ d.getElementById(`p${p}api`).value = papi;
+ if (papi.indexOf("Please") == 0) d.getElementById(`p${p}cstgl`).checked = true;
+ tglCs(p);
}
function unfocusSliders() {