Simplified UI and general UI polishing (CSS, HTML & JS).

Boot transition fix.
Local storage invalidation when uploading presets.json.
This commit is contained in:
Blaz Kristan 2021-08-10 17:11:17 +02:00
parent 5c6d755750
commit dcfbf2b154
22 changed files with 6565 additions and 2467 deletions

View File

@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.13.0-bl2",
"version": "0.13.0-bl3",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {

View File

@ -196,7 +196,7 @@ lib_deps =
${env.lib_deps}
# ESPAsyncTCP @ 1.2.0
ESPAsyncUDP
makuna/NeoPixelBus @ 2.6.4 # 2.6.5 and newer do not compile on ESP core < 3.0.0
makuna/NeoPixelBus @ 2.6.7 # 2.6.5/2.6.6 do not compile on ESP core < 3.0.0
[esp32]
build_flags = -g
@ -205,7 +205,7 @@ build_flags = -g
lib_deps =
${env.lib_deps}
https://github.com/Makuna/NeoPixelBus.git # until next upstream release
makuna/NeoPixelBus @ 2.6.7
AsyncTCP @ 1.0.3
[esp32s2]
@ -217,7 +217,7 @@ build_flags = -g
lib_deps =
${env.lib_deps}
https://github.com/Makuna/NeoPixelBus.git # until next upstream release
makuna/NeoPixelBus @ 2.6.7
AsyncTCP @ 1.0.3
# ------------------------------------------------------------------------------

View File

@ -65,7 +65,7 @@ function adoptVersionAndRepo(html) {
return html;
}
function writeHtmlGzipped(sourceFile, resultFile) {
function writeHtmlGzipped(sourceFile, resultFile, page) {
console.info("Reading " + sourceFile);
new inliner(sourceFile, function (error, html) {
console.info("Inlined " + html.length + " characters");
@ -95,8 +95,8 @@ function writeHtmlGzipped(sourceFile, resultFile) {
*/
// Autogenerated from ${sourceFile}, do not edit!!
const uint16_t PAGE_index_L = ${result.length};
const uint8_t PAGE_index[] PROGMEM = {
const uint16_t PAGE_${page}_L = ${result.length};
const uint8_t PAGE_${page}[] PROGMEM = {
${array}
};
`;
@ -194,7 +194,8 @@ function writeChunks(srcDir, specs, resultFile) {
fs.writeFileSync(resultFile, src);
}
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h");
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple');
writeChunks(
"wled00/data",

View File

@ -23,6 +23,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
getStringFromJson(cmDNS, id[F("mdns")], 33);
getStringFromJson(serverDescription, id[F("name")], 33);
getStringFromJson(alexaInvocationName, id[F("inv")], 33);
#ifndef WLED_DISABLE_SIMPLE_UI
CJSON(simplifiedUI, id[F("sui")]);
#endif
JsonObject nw_ins_0 = doc["nw"]["ins"][0];
getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33);
@ -480,6 +483,7 @@ void serializeConfig() {
id[F("mdns")] = cmDNS;
id[F("name")] = serverDescription;
id[F("inv")] = alexaInvocationName;
id[F("sui")] = simplifiedUI;
JsonObject nw = doc.createNestedObject("nw");

View File

@ -116,6 +116,9 @@ button {
font-size: 24px;
line-height: 1;
display: inline-block;
}
.top .icons, .bot .icons {
margin: -2px 0 4px 0;
}
@ -128,7 +131,7 @@ button {
width: 100%;
}
.segt {
.segt, .plentry TABLE {
table-layout: fixed;
width: 100%;
}
@ -136,11 +139,13 @@ button {
.segt TD {
text-align: center;
/*text-transform: uppercase;*/
}
.segt TD, .plentry TD {
font-size: 14px;
padding: 0;
vertical-align: middle;
}
.segt TD.h {
.segt TD.h, .plentry TD.h {
font-size: 13px;
padding: 2px 0 0;
}
@ -178,12 +183,12 @@ button {
}
.flr {
float: right;
cursor: pointer;
margin: 0;
color: var(--c-f);
transform: rotate(0deg);
transition: transform 0.3s;
position: absolute;
top: 8px;
right: 8px;
}
.exp {
@ -222,6 +227,7 @@ button {
transition: color 0.3s, background-color 0.3s;
font-size: 17px;
color: var(--c-c);
min-width: 44px;
}
.top button {
@ -276,6 +282,19 @@ button {
-webkit-overflow-scrolling: touch;
}
#segutil, #segutil2, #segcont, #putil, #pcont, #pql {
width: 280px;
margin: 0 auto;
}
#segutil .seg {
margin: 10px 0;
}
#segcont, #segutil, #putil {
padding: 10px 0 0;
}
.smooth { transition: transform calc(var(--f, 1)*.5s) ease-out }
.tab-label {
@ -300,7 +319,7 @@ button {
pointer-events: none;
}
.staytop {
.staytop, .staybot {
display: block;
position: -webkit-sticky;
position: sticky;
@ -309,6 +328,10 @@ button {
margin: 0 auto auto;
}
.staybot {
bottom: 0;
}
#staytop, #staytop1 {
background: var(--c-2);
width: 310px;
@ -324,11 +347,6 @@ button {
top: 58px;
}
#fxb0 {
margin-bottom: 2px;
filter: drop-shadow(0 0 1px #000);
}
.first {
margin-top: 10px;
}
@ -336,6 +354,7 @@ button {
#toast {
opacity: 0;
background-color: var(--c-5);
border: 1px solid var(--c-2);
max-width: 90%;
color: var(--c-f);
text-align: center;
@ -387,10 +406,6 @@ button {
margin: 12px 0;
}
.valtd i {
font-size: 14px;
}
#roverstar {
position: fixed;
top: calc(var(--th) + 5px);
@ -534,18 +549,6 @@ input[type=range]::-moz-range-thumb {
position: relative;
}
.sbs {
margin: 0px -20px 5px -6px;
}
.sws {
width: 220px;
}
.sis {
width: 210px !important;
}
.hd {
display: var(--bhd);
}
@ -569,35 +572,43 @@ input[type=range]::-moz-range-thumb {
margin: 10px 4px;
width: 230px;
font-size: 19px;
background-color: var(--c-3);
color: var(--c-d);
cursor: pointer;
border: 1px solid var(--c-3);
border-radius: 25px;
transition-duration: 0.5s;
transition-duration: 0.3s;
-webkit-backface-visibility: hidden;
-webkit-transform:translate3d(0,0,0);
overflow: clip;
text-overflow: clip;
}
.btn:hover {
border: 1px solid var(--c-4);
background-color: var(--c-4);
}
.btn {
border: 1px solid var(--c-3);
background-color: var(--c-3);
}
.btn-s {
width: 276px;
background-color: var(--c-2);
}
.btn-i {
padding-bottom: 4px;
margin: 0 0 10px;
}
.btn-i {}
.btn-icon {
margin: 0px 8px 4px 0;
margin: -4px 8px 0 0;
vertical-align: middle;
display: inline-block;
}
.btna-icon {
margin: 0px;
}
.btn-n {
width: 230px;
margin-right: 8px;
}
.btn-p {
width: 165px;
margin: 5px;
width: 120px;
margin: 5px 0;
}
.btn-xs, .btn-pl-del, .btn-pl-add {
width: 42px;
@ -650,7 +661,7 @@ input[type=range]::-moz-range-thumb {
width: 42px;
}
.sel-pl {
width: 165px;
width: 100%;
background-position: 141px 16px;
}
.sel-ple {
@ -677,10 +688,14 @@ input[type=number], input[type=text] {
appearance: textfield;
}
input[type=number] {
text-align: right;
}
textarea {
background: var(--c-2);
color: var(--c-f);
width: 236px;
width: 95%;
height: 90px;
border-radius: 5px;
border: 2px solid #555;
@ -699,8 +714,8 @@ input[type=text] {
}
.ptxt {
width: 216px !important;
margin: 6px !important;
width: 240px !important;
margin: 0 4px 4px !important;
}
.stxt {
@ -716,61 +731,25 @@ input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
}
.pln {
border-radius: 25px !important;
width: 67px !important;
margin: 0 2px 8px 0 !important;
text-align: center;
}
.plnl {
width: 86px;
margin: 0 2px 0 0;
display: inline-block;
}
.pli {
width: 38px;
margin: 0 0 0 29px;
display: inline-block;
}
.segn {
margin: 3px 0 6px 0 !important;
text-align: right;
}
.segname, .pname {
position: absolute;
top: 0px;
left: 50%;
padding: 10px 0;
transform: translateX(-50%);
padding: 4px 0;
white-space: nowrap;
cursor: pointer;
text-align: center;
}
/*
.segname {
padding: 7px 0;
font-size: 11px;
}
*/
.pname {
width: 208px;
overflow: hidden;
text-overflow: clip;
}
.segpwr {
margin: 8px 0 0;
padding: 4px 0 4px 8px;
}
.pid {
position: absolute;
top: 0px;
left: 0px;
padding: 11px 0px 0px 11px;
padding: 12px 0px 0px 12px;
font-size: 16px;
width: 20px;
text-align: center;
color: var(--c-b);
}
@ -796,25 +775,6 @@ input[type=number]::-webkit-outer-spin-button {
height: 40px;
}
.cnf {
color: var(--c-f);
cursor: pointer;
background: var(--c-3);
border-radius: 25px;
padding: 8px;
margin: -8px 0 0;
}
.cnf-s {
/*
padding: 8px;
position: absolute;
top: 173px;
right: 23px;
padding: 7px 22px;
*/
}
.pwr {
color: var(--c-6);
transform: translate(2px, 3px);
@ -848,17 +808,11 @@ input[type=number]::-webkit-outer-spin-button {
}
.revchkl {
padding: 2px 0px 0px 35px;
padding: 4px 0px 0px 35px;
margin-bottom: 0px;
margin-top: 8px;
}
.fxchkl {
position: absolute;
top: 0px;
left: 8px;
}
.check input, .radio input {
position: absolute;
opacity: 0;
@ -869,39 +823,25 @@ input[type=number]::-webkit-outer-spin-button {
.checkmark, .radiomark {
position: absolute;
top: 0;
bottom: 0;
left: 0;
background-color: var(--c-3);
border: 1px solid var(--c-2);
}
.radiomark {
height: 24px;
width: 24px;
border-radius: 50%;
top: -2px;
}
.checkmark {
height: 25px;
width: 25px;
border-radius: 10px;
top: 0;
}
.psv {
left: initial;
bottom: initial;
top: 0;
right: 0;
}
.psvl {
padding: 2px 35px 10px 0px;
margin-top: 10px;
margin-bottom: 0px;
}
.check:hover input ~ .checkmark {
background-color: var(--c-4);
}
@ -952,13 +892,11 @@ input[type=number]::-webkit-outer-spin-button {
margin-bottom: 5px;
}
.seg {
.seg, .pres {
position: relative;
display: inline-block;
padding: 8px;
margin: 3px 0;
width: 260px;
font-size: 19px;
display: block;
padding: 8px 0;
margin: 0 0 10px;
background-color: var(--c-2);
color: var(--c-f);
border: 0px solid var(--c-f);
@ -966,17 +904,37 @@ input[type=number]::-webkit-outer-spin-button {
text-align: left;
transition: background-color 0.5s;
filter: brightness(1);
font-size: 19px;
}
.seg:last-child {
margin: 0;
}
.seg .schkl {
position: absolute;
top: 8px;
left: 8px;
}
.pres {
padding-bottom: 4px;
}
#pcont .pres:hover {
background-color: var(--c-3);
}
.list {
position: relative;
width: 260px;
width: 280px;
transition: background-color 0.5s;
margin: auto auto 20px;
font-size: 19px;
line-height: 24px;
}
.lstI {
border-bottom: 1px solid var(--c-3);
display: flex;
align-items: center;
padding: 8px 10px;
@ -984,39 +942,36 @@ input[type=number]::-webkit-outer-spin-button {
background-color: var(--c-2);
overflow: hidden;
position: sticky;
border: 1px solid var(--c-2);
border-radius: 25px;
margin: 10px auto 0;
min-height: 24px;
}
.lstI:hover {
background: var(--c-4);
}
.lstI:last-child {
border: none;
border-radius: 0 0 20px 20px;
padding-bottom: 10px;
}
.lstI.selected {
background: var(--c-5);
top: 135px;
top: 142px;
bottom: 0;
}
.lstI.sticky {
top: 100px;
}
.lstI.sticky, .lstI.selected {
z-index: 1;
}
#pallist .lstI.selected {
top: 80px;
bottom: 0;
top: 84px;
}
#pallist .lstI.sticky {
top: 40px;
}
.lstI.sticky {
top: 98px;
top: 42px;
}
.lstIcontent {
@ -1024,20 +979,20 @@ input[type=number]::-webkit-outer-spin-button {
vertical-align: middle;
padding: 0 20px 0 5px;
text-align: left;
display: inline-block;
position: relative;
}
.lstIname {
margin: 3px 0;
white-space: nowrap;
cursor: pointer;
}
.lstIprev {
border: 1px solid var(--c-4);
border-radius: 4px;
width: 100%;
height: 8px;
margin: auto;
position: absolute;
bottom: 0;
left: 0;
}
.fndIcn { /* needed for magnifier SVG, can be removed when magnifier is in Wicons font */
@ -1049,6 +1004,11 @@ input[type=number]::-webkit-outer-spin-button {
margin-top: 1px;
}
.fnd {
width: 280px;
margin: 0 auto;
}
div.fnd div {
position: absolute;
top: 10px;
@ -1065,30 +1025,27 @@ div.fnd span {
input[type="text"].fnd {
display: block;
width: 260px;
width: 100%;
box-sizing: border-box;
padding: 8px 48px 8px 60px;
margin: 5px auto 0;
text-align: left;
border-radius: 20px 20px 0 0;
border-radius: 25px;
background: var(--c-2);
border-bottom: 1px solid var(--c-3);
border: 1px solid var(--c-3);
}
input[type="text"].fnd:focus {
background-color: var(--c-5);
}
input[type="text"].fnd:not(:placeholder-shown) {
background-color: var(--c-4);
}
.pres {
margin-bottom: 6px;
input[type="text"].fnd:not(:placeholder-shown),
input[type="text"].fnd:hover {
background-color: var(--c-3);
}
.segin {
padding: 4px 8px 4px 8px;
padding: 0 8px 8px;
display: none;
}

View File

@ -24,7 +24,7 @@
<button id="buttonSync" onclick="toggleSync()"><i class="icons">&#xe116;</i><p class="tab-label">Sync</p></button>
<button id="buttonSr" onclick="toggleLiveview()"><i class="icons">&#xe410;</i><p class="tab-label">Peek</p></button>
<button id="buttonI" onclick="toggleInfo()"><i class="icons">&#xe066;</i><p class="tab-label">Info</p></button>
<button id="buttonNodes" onclick="toggleNodes()"><i class="icons">&#xe22d;</i><p class="tab-label">Nodes</p></button></div>
<button id="buttonNodes" onclick="toggleNodes()"><i class="icons">&#xe22d;</i><p class="tab-label">Nodes</p></button>
<button onclick="window.location.href='/settings';"><i class="icons">&#xe0a2;</i><p class="tab-label">Config</p></button>
<button id="buttonPcm" onclick="togglePcMode(true)"><i class="icons">&#xe23d;</i><p class="tab-label">PC Mode</p></button>
</div>
@ -164,18 +164,18 @@
</div>
<div id="Segments" class="tabcontent">
<div id="segutil" class="staytop">
</div>
<div id="segcont">
Loading...
</div>
<div id="segutil" class="staybot">
</div>
<div id="segutil2">
<button class="btn btn-s" id="rsbtn" onclick="rSegs()">Reset segments</button>
</div>
<p>Transition: <input id="tt" class="noslide" type="number" min="0" max="65.5" step="0.1" value="0.7">s</p>
</div>
<div id="Favorites" class="tabcontent">
<div id="Presets" class="tabcontent">
<div id="putil" class="staytop">
</div>
<div id="pql">
@ -190,7 +190,7 @@
<button class="tablinks" onclick="openTab(0)"><i class="icons">&#xe2b3;</i><p class="tab-label">Colors</p></button>
<button class="tablinks" onclick="openTab(1)"><i class="icons">&#xe23d;</i><p class="tab-label">Effects</p></button>
<button class="tablinks" onclick="openTab(2)"><i class="icons">&#xe34b;</i><p class="tab-label">Segments</p></button>
<button class="tablinks" onclick="openTab(3)"><i class="icons">&#xe04c;</i><p class="tab-label">Favorites</p></button>
<button class="tablinks" onclick="openTab(3)"><i class="icons">&#xe04c;</i><p class="tab-label">Presets</p></button>
<!--button class="tablinks" onclick="toggleNodes()"><i class="icons">&#xe22d;</i><p class="tab-label">Nodes</p></button-->
</div>

View File

@ -512,19 +512,12 @@ function populateQL()
{
var cn = "";
if (pQL.length > 0) {
pQL.sort((a,b) => (a[0]>b[0]));
cn += `<p class="labels">Quick load</p>`;
var it = 0;
for (var key of (pQL||[])) {
cn += `<button class="xxs btn psts" id="p${key[0]}qlb" onclick="setPreset(${key[0]});">${key[1]}</button>`;
it++;
if (it > 4) {
it = 0;
cn += '<br>';
cn += `<button class="xxs btn psts" id="p${key[0]}qlb" title="${key[2]?key[2]:''}" onclick="setPreset(${key[0]});">${key[1]}</button>`;
}
}
if (it != 0) cn+= '<br>';
}
gId('pql').innerHTML = cn;
}
@ -544,15 +537,15 @@ function populatePresets(fromls)
if (!isObject(key[1])) continue;
let i = parseInt(key[0]);
var qll = key[1].ql;
if (qll) pQL.push([i, qll]);
if (qll) pQL.push([i, qll, pName(i)]);
is.push(i);
cn += `<div class="seg pres" id="p${i}o">`;
cn += `<div class="pres" id="p${i}o">`;
if (cfg.comp.pid) cn += `<div class="pid">${i}</div>`;
cn += `<div class="pname" onclick="setPreset(${i})">${isPlaylist(i)?"<i class='icons btn-icon'>&#xe139;</i>":""}${pName(i)}</div>
<i class="icons e-icon flr ${expanded[i+100] ? "exp":""}" id="sege${i+100}" onclick="expand(${i+100})">&#xe395;</i>
<div class="segin" id="seg${i+100}"></div>
</div><br>`;
</div>`;
pNum++;
}
@ -667,7 +660,7 @@ function populateSegments(s)
if (i > lSeg) lSeg = i;
cn += `<div class="seg">
<label class="check schkl" style="position:absolute">
<label class="check schkl">
&nbsp;
<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>
<span class="checkmark schk"></span>
@ -675,16 +668,16 @@ function populateSegments(s)
<div class="segname" onclick="selSegEx(${i})">
${inst.n ? inst.n : "Segment "+i}
</div>
<i class="icons e-icon flr ${expanded[i] ? "exp":""}" id="sege${i}" onclick="expand(${i})">&#xe395;</i><br>
<i class="icons e-icon flr ${expanded[i] ? "exp":""}" id="sege${i}" onclick="expand(${i})">&#xe395;</i>
<div class="segpwr">
<i class="icons e-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})">&#xe08f;</i>
<div class="sliderwrap il sws">
<input id="seg${i}bri" class="noslide sis" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />
<div class="sliderwrap il">
<input id="seg${i}bri" class="noslide" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />
<div class="sliderdisplay"></div>
</div>
</div>
<div class="segin ${expanded[i] ? "expanded":""}" id="seg${i}">
<input type="text" class="ptxt noslide" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/><br>
<input type="text" class="ptxt noslide" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/>
<table class="infot segt">
<tr>
<td>Start LED</td>
@ -696,8 +689,6 @@ function populateSegments(s)
<td><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?inst.start:0)}" value="${inst.stop-(cfg.comp.seglen?inst.start:0)}" oninput="updateLen(${i})"></td>
<td><input class="noslide segn" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
</tr>
</table>
<table class="infot segt">
<tr>
<td>Grouping</td>
<td>Spacing</td>
@ -706,7 +697,7 @@ function populateSegments(s)
<tr>
<td><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})"></td>
<td><input class="noslide segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})"></td>
<td><i class="icons e-icon cnf cnf-s" id="segc${i}" onclick="setSeg(${i})">&#xe390;</i></td>
<td><button class="btn btn-i btn-xs" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}">&#xe390;</i></button></td>
</tr>
</table>
<div class="h bp" id="seg${i}len"></div>
@ -722,7 +713,7 @@ function populateSegments(s)
<span class="checkmark schk"></span>
</label>
</div>
</div><br>`;
</div>`;
}
gId('segcont').innerHTML = cn;
@ -942,8 +933,8 @@ function generateListItemHtml(listName, id, name, clickAction, extraHtml = '')
<span class="lstIname">
${name}
</span>
${extraHtml}
</div>
${extraHtml}
</div>`;
}
@ -1000,13 +991,13 @@ function updateLen(s)
function updatePA(scrollto=false)
{
var ps = gEBCN("seg");
var ps = gEBCN("pres");
for (let i = 0; i < ps.length; i++) {
ps[i].style.backgroundColor = "var(--c-2)";
ps[i].style.backgroundColor = "";
}
ps = gEBCN("psts");
for (let i = 0; i < ps.length; i++) {
ps[i].style.backgroundColor = "var(--c-2)";
ps[i].style.backgroundColor = "";
}
if (currentPreset > 0) {
var acv = gId(`p${currentPreset}o`);
@ -1083,7 +1074,9 @@ function displayRover(i,s)
function cmpP(a, b)
{
if (!a[1].n) return (a[0] > b[0]);
return a[1].n.localeCompare(b[1].n,undefined, {numeric: true});
// playlists follow presets
var name = (a[1].playlist ? '~' : ' ') + a[1].n;
return name.localeCompare((b[1].playlist ? '~' : ' ') + b[1].n, undefined, {numeric: true});
}
function makeWS() {
@ -1145,6 +1138,7 @@ function readState(s,command=false)
if (s.pl<0) currentPreset = s.ps;
else currentPreset = s.pl;
gId('tt').value = s.transition/10;
if (s.tdd >= 0) tr = s.tdd/10;
var selc=0; var ind=0;
populateSegments(s);
@ -1229,7 +1223,7 @@ function requestJson(command=null)
command.v = true; // force complete /json/si API response
command.time = Math.floor(Date.now() / 1000);
var t = d.getElementById('tt');
if (t.validity.valid) {
if (command.transition===null && t.validity.valid) {
var tn = parseInt(t.value*10);
if (tn != tr) command.transition = tn;
}
@ -1336,9 +1330,13 @@ function makeSeg()
var pend = parseInt(gId(`seg${lowestUnused -1}e`).value,10) + (cfg.comp.seglen?parseInt(gId(`seg${lowestUnused -1}s`).value,10):0);
if (pend < ledCount) ns = pend;
}
gId('segutil').scrollIntoView({
behavior: 'smooth',
block: 'start',
});
var cn = `<div class="seg">
<div class="segin expanded">
<input type="text" class="ptxt noslide" id="seg${lowestUnused}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lowestUnused}"/><br>
<input type="text" class="ptxt noslide" id="seg${lowestUnused}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lowestUnused}"/>
<table class="segt">
<tr>
<td width="38%">Start LED</td>
@ -1347,7 +1345,7 @@ function makeSeg()
<tr>
<td><input class="noslide segn" id="seg${lowestUnused}s" type="number" min="0" max="${ledCount-1}" value="${ns}" oninput="updateLen(${lowestUnused})"></td>
<td><input class="noslide segn" id="seg${lowestUnused}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?ns:0)}" value="${ledCount-(cfg.comp.seglen?ns:0)}" oninput="updateLen(${lowestUnused})"></td>
<td><i class="icons e-icon cnf cnf-s" id="segc${lowestUnused}" onclick="setSeg(${lowestUnused});resetUtil();">&#xe390;</i></td>
<td><button class="btn btn-i btn-xs" onclick="setSeg(${lowestUnused});resetUtil();"><i class="icons bth-icon" id="segc${lowestUnused}">&#xe390;</i></button></td>
</tr>
</table>
<div class="h" id="seg${lowestUnused}len">${ledCount - ns} LEDs</div>
@ -1359,7 +1357,7 @@ function makeSeg()
function resetUtil()
{
gId('segutil').innerHTML = '<button class="btn btn-s btn-i" onclick="makeSeg()"><i class="icons btn-icon">&#xe18a;</i>Add segment</button><br>';
gId('segutil').innerHTML = '<button class="btn btn-s btn-i" onclick="makeSeg()"><i class="icons btn-icon">&#xe18a;</i>Add segment</button>';
for (var i=0; i<expanded.length; i++) if (expanded[i]) expand(i); // collapse all expanded elements
}
@ -1392,7 +1390,8 @@ function refreshPlE(p) {
content += `<div class="hrz"></div>`;
plEDiv.innerHTML = content;
var dels = plEDiv.getElementsByClassName("btn-pl-del");
if (dels.length < 2 && p > 0) dels[0].style.display = "none";
// if (dels.length < 2 && p > 0) dels[0].style.display = "none";
if (dels.length < 2) dels[0].style.display = "none";
var sels = gId(`seg${p+100}`).getElementsByClassName("sel");
for (var i of sels) {
@ -1412,7 +1411,8 @@ function addPl(p,i) {
}
function delPl(p,i) {
if (plJson[p].ps.length < 2) {if (p == 0) resetPUtil(); return;}
// if (plJson[p].ps.length < 2) {if (p == 0) resetPUtil(); return;}
if (plJson[p].ps.length < 2) return;
plJson[p].ps.splice(i,1);
plJson[p].dur.splice(i,1);
plJson[p].transition.splice(i,1);
@ -1482,7 +1482,7 @@ ${plSelContent}
<span class="checkmark schk"></span>
</label>`;
return `<input type="text" class="ptxt noslide" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/><br>
return `<input type="text" class="ptxt noslide" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/>
<div class="c">Quick load label: <input type="text" class="stxt noslide" maxlength=2 value="${qlName(i)}" id="p${i}ql" autocomplete="off"/></div>
<div class="h">(leave empty for no Quick load button)</div>
<div ${pl&&i==0?"style='display:none'":""}>
@ -1490,7 +1490,7 @@ ${plSelContent}
${pl?"Show playlist editor":(i>0)?"Overwrite with state":"Use current state"}
<input type="checkbox" id="p${i}cstgl" onchange="tglCs(${i})" ${(i==0||pl)?"checked":""}>
<span class="checkmark schk"></span>
</label><br>
</label>
</div>
<div class="po2" id="p${i}o2">
API command<br>
@ -1502,7 +1502,7 @@ ${plSelContent}
<div class="c">Save to ID <input class="noslide" id="p${i}id" type="number" oninput="checkUsed(${i})" max=250 min=1 value=${(i>0)?i:getLowestUnusedP()}></div>
<div class="c">
<button class="btn btn-i btn-p" onclick="saveP(${i},${pl})"><i class="icons btn-icon">&#xe390;</i>Save</button>
${(i>0)?'<button class="btn btn-i btn-pl-del" id="p'+i+'del" onclick="delP('+i+')"><i class="icons btn-icon">&#xe037;</i>Delete':'<button class="btn btn-p" onclick="resetPUtil()">Cancel'}</button>
${(i>0)?'<button class="btn btn-i btn-p" id="p'+i+'del" onclick="delP('+i+')"><i class="icons btn-icon">&#xe037;</i>Delete':'<button class="btn btn-p" onclick="resetPUtil()">Cancel'}</button>
</div>
<div class="pwarn ${(i>0)?"bp":""} c" id="p${i}warn"></div>
${(i>0)? ('<div class="h">ID ' +i+ '</div>'):""}`;
@ -1510,21 +1510,26 @@ ${(i>0)? ('<div class="h">ID ' +i+ '</div>'):""}`;
function makePUtil()
{
gId('putil').innerHTML = `<div class="seg pres"><div class="segin expanded">${makeP(0)}</div></div>`;
gId('putil').classList.remove("staytop");
gId('putil').scrollIntoView({
behavior: 'smooth',
block: 'start',
});
gId('putil').innerHTML = `<div class="pres"><div class="segin expanded">${makeP(0)}</div></div>`;
for (var i=0; i<expanded.length; i++) if (expanded[i]) expand(i); // collapse all expanded elements
}
function makePlEntry(p,i) {
return `<div class="plentry">
<div class="hrz"></div>
<table class="segt">
<table>
<tr>
<td width="80%" colspan=2>
<select class="sel sel-pl" onchange="plePs(${p},${i},this)" data-val="${plJson[p].ps[i]}" data-index="${i}">
${plSelContent}
</select>
</td>
<td><button class="btn btn-i btn-pl-add" onclick="addPl(${p},${i})"><i class="icons btn-icon">&#xe18a;</i></button></td>
<td class="c"><button class="btn btn-i btn-pl-add" onclick="addPl(${p},${i})"><i class="icons btn-icon">&#xe18a;</i></button></td>
</tr>
<tr>
<td class="h">Duration</td>
@ -1532,9 +1537,9 @@ function makePlEntry(p,i) {
<td class="h">#${i+1}</td>
</tr>
<tr>
<td width="40%"><input class="noslide segn" type="number" placeholder="Duration" max=6553.0 min=0.2 step=0.1 oninput="pleDur(${p},${i},this)" value="${plJson[p].dur[i]/10.0}">s</td>
<td width="40%"><input class="noslide segn" type="number" placeholder="Transition" max=65.0 min=0.0 step=0.1 oninput="pleTr(${p},${i},this)" value="${plJson[p].transition[i]/10.0}">s</td>
<td><button class="btn btn-i btn-pl-del" onclick="delPl(${p},${i})"><i class="icons btn-icon">&#xe037;</i></button></div></td>
<td class="c" width="40%"><input class="noslide segn" type="number" placeholder="Duration" max=6553.0 min=0.2 step=0.1 oninput="pleDur(${p},${i},this)" value="${plJson[p].dur[i]/10.0}">s</td>
<td class="c" width="40%"><input class="noslide segn" type="number" placeholder="Transition" max=65.0 min=0.0 step=0.1 oninput="pleTr(${p},${i},this)" value="${plJson[p].transition[i]/10.0}">s</td>
<td class="c"><button class="btn btn-i btn-pl-del" onclick="delPl(${p},${i})"><i class="icons btn-icon">&#xe037;</i></button></div></td>
</tr>
</table>
</div>`;
@ -1546,14 +1551,20 @@ function makePlUtil()
showToast("You need at least 2 presets to make a playlist!"); //return;
}
if (plJson[0].transition[0] < 0) plJson[0].transition[0] = tr;
gId('putil').innerHTML = `<div class="seg pres"><div class="segin expanded" id="seg100">${makeP(0,true)}</div></div>`;
gId('putil').classList.remove("staytop");
gId('putil').scrollIntoView({
behavior: 'smooth',
block: 'start',
});
gId('putil').innerHTML = `<div class="pres"><div class="segin expanded" id="seg100">${makeP(0,true)}</div></div>`;
refreshPlE(0);
}
function resetPUtil()
{
var cn = `<button class="btn btn-s btn-i" style="width:230px;margin-left:0;" onclick="makePUtil()"><i class="icons btn-icon">&#xe18a;</i>New&nbsp;preset</button>`+
`<button class="btn btn-i btn-xs" onclick="makePlUtil()"><i class="icons btn-icon">&#xe139;</i></button><br>`;
gId('putil').classList.add("staytop");
var cn = `<button class="btn btn-s btn-n" onclick="makePUtil()"><i class="icons btn-icon">&#xe18a;</i>New&nbsp;preset</button>`+
`<button class="btn btn-i btn-xs" onclick="makePlUtil()"><i class="icons btn-icon">&#xe139;</i></button>`;
gId('putil').innerHTML = cn;
}
@ -1753,6 +1764,7 @@ function saveP(i,pl)
}
populatePresets();
resetPUtil();
if (i>0) expand(pI+100); // collapse edited preset or expand created preset.
}
function testPl(i,bt) {
@ -1782,7 +1794,7 @@ function delP(i) {
populatePresets();
} else {
bt.style.color = "#f00";
bt.innerHTML = "<i class='icons btn-icon'>&#xe037;</i>Confirm delete";
bt.innerHTML = "<i class='icons btn-icon'>&#xe037;</i>Delete!";
bt.dataset.cnf = 1;
}
}
@ -2033,8 +2045,8 @@ function expand(i,a)
seg.style.display = (expanded[i]) ? "block":"none";
gId('sege' +i).style.transform = (expanded[i]) ? "rotate(180deg)":"rotate(0deg)";
if (expanded[i]) gId(i<100?'segutil':'putil').classList.remove("staytop");
else gId(i<100?'segutil':'putil').classList.add("staytop");
if (expanded[i]) gId(i<100?'segutil':'putil').classList.remove(i<100?"staybot":"staytop");
else gId(i<100?'segutil':'putil').classList.add(i<100?"staybot":"staytop");
if (i >= 100) {
var p = i-100;

View File

@ -6,7 +6,7 @@
<title>UI Settings</title>
<script>
var d = document;
var initial_ds, initial_st;
var initial_ds, initial_st, initial_su;
var sett = null;
var l = {
"comp":{
@ -148,7 +148,7 @@
function Save() {
SetLS();
if (d.Sf.DS.value != initial_ds || d.Sf.ST.checked != initial_st) d.Sf.submit();
if (d.Sf.DS.value != initial_ds || d.Sf.ST.checked != initial_st || d.Sf.SU.checked != initial_su) d.Sf.submit();
}
function S()
@ -156,6 +156,7 @@
GetV();
initial_ds = d.Sf.DS.value;
initial_st = d.Sf.ST.checked;
initial_su = d.Sf.SU.checked;
GetLS();
}
function H()
@ -202,7 +203,7 @@
}
function GetV(){var d=document;}
</script>
<style>@import url("/style.css");</style>
<style>@import url("style.css");</style>
<link href="/skin.css" rel="stylesheet">
</head>
<body onload="S()">
@ -216,6 +217,7 @@
<h2>Web Setup</h2>
Server description: <input name="DS" maxlength="32"><br>
Sync button toggles both send and receive: <input type="checkbox" name="ST"><br>
Enable simplified UI: <input type="checkbox" name="SU"><br>
<i>The following UI customization settings are unique both to the WLED device and this browser.<br>
You will need to set them again if using a different browser, device or WLED IP address.<br>
Refresh the main UI to apply changes.</i><br>

758
wled00/data/simple.css Normal file

File diff suppressed because one or more lines are too long

172
wled00/data/simple.htm Normal file
View File

@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<meta charset="utf-8">
<meta name="theme-color" content="#222222">
<meta content="yes" name="apple-mobile-web-app-capable">
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAGACGAAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAE1JREFUOI1j/P//PwOxgNGeAUMxE9G6cQCKDWAhpADZ2f8PMjBS3QW08QK20KaZC2gfC9hCnqouoNgARgY7zMxAyNlUdQHlXiAlO2MDAD63EVqNHAe0AAAAAElFTkSuQmCC"/>
<title>WLED</title>
<script>function feedback(){}</script>
<link rel="stylesheet" href="simple.css">
</head>
<body onload="onLoad()">
<div id="cv" class="overlay">Loading WLED UI...</div>
<noscript><div class="overlay" style="opacity:1;">Sorry, WLED UI needs JavaScript!</div></noscript>
<div id="bg"></div>
<div class="wrapper" id="top">
<div class="tab top">
<div class="btnwrap">
<button id="buttonPower" onclick="togglePower()"><i class="icons">&#xe08f;</i><p class="tab-label">Power</p></button>
<button id="buttonI" onclick="toggleInfo()"><i class="icons">&#xe066;</i><p class="tab-label">Info</p></button>
<button id="buttonNodes" onclick="toggleNodes()"><i class="icons">&#xe22d;</i><p class="tab-label">Nodes</p></button></div>
<button onclick="window.location.href='/settings';"><i class="icons">&#xe0a2;</i><p class="tab-label">Config</p></button>
<button id="buttonCP" onclick="tglCP()"><i class="icons">&#xe2b3;</i><p class="tab-label">Expand</p></button>
<button id="buttonBri" onclick="tglBri()"><i class="icons">&#xe2a6;</i><p class="tab-label">Global</p></button>
</div>
<div id="briwrap">
<p class="labels hd">Global brightness</p>
<div class="il">
<i class="icons slider-icon" onclick="tglTheme()">&#xe2a6;</i>
<div class="sliderwrap il">
<input id="sliderBri" onchange="setBri()" oninput="updateTrail(this)" max="255" min="1" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
</div>
</div>
</div>
<div class ="container">
<div class="tabcontent">
<div id="picker" class="center"></div>
<div id="wwrap" class="center">
<p class="labels h">White channel</p>
<i class="icons slider-icon" id="wht" title="White channel">&#xe333;</i>
<div class="sliderwrap il">
<input id="sliderW" onchange="setColor(0)" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
<div id="qcs-w" class="center">
<div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div>
<div class="qcs" onclick="pC('#ffa000');" title="Orange" style="background-color:#ffa000;"></div>
<div class="qcs" onclick="pC('#ffc800');" title="Yellow" style="background-color:#ffc800;"></div>
<div class="qcs" onclick="pC('#ffe0a0');" title="Warm White" style="background-color:#ffe0a0;"></div>
<div class="qcs" onclick="pC('#ffffff');" title="White" style="background-color:#ffffff;"></div>
<div class="qcs qcsb" onclick="pC('#000000');" title="Black" style="background-color:#000000;"></div><br>
<div class="qcs" onclick="pC('#ff00ff');" title="Pink" style="background-color:#ff00ff;"></div>
<div class="qcs" onclick="pC('#0000ff');" title="Blue" style="background-color:#0000ff;"></div>
<div class="qcs" onclick="pC('#00ffc8');" title="Cyan" style="background-color:#00ffc8;"></div>
<div class="qcs" onclick="pC('#08ff00');" title="Green" style="background-color:#08ff00;"></div>
<div class="qcs" onclick="pC('rnd');" title="Random" style="background-color:var(--c-3); padding: 4px 8px; transform: translateY(-10px);">R</div>
</div>
<div id="csl" class="center" style="display: none;">
<button class="xxs btn" onclick="selectSlot(0);">1</button>
<button class="xxs btn" onclick="selectSlot(1);">2</button>
<button class="xxs btn" onclick="selectSlot(2);">3</button>
</div>
<div id="Segments" class="center">
<div id="segcont"></div>
</div>
<div id="QuickLoad" class="center">
<p class="labels h">Quick Load</p>
<div id="pql"></div>
</div>
<div id="Presets" class="center" style="display:none;">
<p class="labels h">Presets</p>
<div class="fnd">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'pcont')" onfocus="search(this)" />
<span onclick="clean(this)" class="icons">&#xe38f;</span>
<div class="icons"><svg xmlns='http://www.w3.org/2000/svg' class='fndIcn'><circle cx='8' cy='8' r='6' /><line x1='12' y1='12' x2='24' y2='12' transform='rotate(45,12,12)' /></svg></div>
</div>
<div id="pcont" class="list"></div>
</div>
<div id="Effects" class="center">
<p class="labels h">Effect</p>
<!--p class="labels h">Effect speed</p-->
<div>
<i class="icons slider-icon">&#xe325;</i>
<div class="sliderwrap il">
<input id="sliderSpeed" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
<!--p class="labels h">Effect intensity</p-->
<div>
<i class="icons slider-icon" onclick="tglLabels()">&#xe409;</i>
<div class="sliderwrap il">
<input id="sliderIntensity" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
<div style="padding-bottom:20px;">
<div onclick="tglFxDropdown()" class="c btn" id="fxBtn"><i class="icons">&#xe0e8;</i> Solid</div>
<div onclick="tglPalDropdown()" class="c btn" id="palBtn"><i class="icons">&#xe2b3;</i>Default</div>
<div id="fxDropdown" class="dd-content">
<div class="fnd">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'fxlist')" onfocus="search(this)" />
<span onclick="clean(this)" class="icons">&#xe38f;</span>
<div class="icons"><svg xmlns='http://www.w3.org/2000/svg' class='fndIcn'><circle cx='8' cy='8' r='6' /><line x1='12' y1='12' x2='24' y2='12' transform='rotate(45,12,12)' /></svg></div>
</div>
<div id="fxlist" class="list">
<div class="lstI" data-id="0" onClick="setX(0)"><a href="#0" onClick="setX(0)">Solid</a></div>
</div>
</div>
<div id="palDropdown" class="dd-content">
<div class="fnd">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'pallist')" onfocus="search(this)" />
<span onclick="clean(this)" class="icons">&#xe38f;</span>
<div class="icons"><svg xmlns='http://www.w3.org/2000/svg' class='fndIcn'><circle cx='8' cy='8' r='6' /><line x1='12' y1='12' x2='24' y2='12' transform='rotate(45,12,12)' /></svg></div>
</div>
<div id="pallist" class="list">
<div class="lstI" data-id="0" onClick="setPalette(0)"><a href="#0" onClick="setPalette(0)">Default</a><div class="lstIprev"></div></div>
</div>
</div>
<br>
</div>
</div>
</div>
</div>
<div id="connind"></div>
<div id="toast"></div>
<div id="namelabel" onclick="toggleNodes()"></div>
<div id="info" class="modal">
<div id="imgw">
<img class="wi" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAFCAYAAAC5Fuf5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABbSURBVChTlY9bDoAwDMNW7n9nwCipytQN4Z8tbrTHmDmF4oPzyldwRqp1SSdnV/NuZuzqerAByxXznBw3igkeFEfXyUuhK/yFM0CxJfyqXZEOc6/Sr9/bf7uIC5Nwd7orMvAPAAAAAElFTkSuQmCC" />
</div><br>
<div id="kv">Loading...</div><br>
<button class="btn infobtn" onclick="loadInfo()">Refresh</button>
<button class="btn infobtn" onclick="toggleInfo()">Close Info</button><br>
<button class="btn infobtn" onclick="toggleNodes()">Instance List</button>
<button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button><br>
<span class="h">Made with <span id="heart">&#10084;&#xFE0E;</span> by Aircoookie and the WLED community</span>
</div>
<div id="nodes" class="modal">
<div id="ndlt">WLED instances</div>
<div id="kn">Loading...</div><br>
<button class="btn infobtn" onclick="loadNodes()">Refresh</button>
<button class="btn infobtn" onclick="toggleNodes()">Close list</button><br>
</div>
<i id="roverstar" class="icons huge" onclick="setLor(0)">&#xe410;</i><br>
<script src="iro.js"></script>
<script src="rangetouch.js"></script>
<script src="simple.js"></script>
</body>
</html>

1383
wled00/data/simple.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -380,10 +380,10 @@ String getContentType(AsyncWebServerRequest* request, String filename){
else if(filename.endsWith(".htm")) return "text/html";
else if(filename.endsWith(".html")) return "text/html";
else if(filename.endsWith(".css")) return "text/css";
// else if(filename.endsWith(".js")) return "application/javascript";
else if(filename.endsWith(".js")) return "application/javascript";
else if(filename.endsWith(".json")) return "application/json";
else if(filename.endsWith(".png")) return "image/png";
// else if(filename.endsWith(".gif")) return "image/gif";
else if(filename.endsWith(".gif")) return "image/gif";
else if(filename.endsWith(".jpg")) return "image/jpeg";
else if(filename.endsWith(".ico")) return "image/x-icon";
// else if(filename.endsWith(".xml")) return "text/xml";

View File

@ -42,7 +42,7 @@ function B(){window.history.back()}function U(){document.getElementById("uf").st
.bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%}#msg{display:none}
</style></head><body><h2>WLED Software Update</h2><form method="POST"
action="/update" id="uf" enctype="multipart/form-data" onsubmit="U()">
Installed version: 0.13.0-bl2<br>Download the latest binary: <a
Installed version: 0.13.0-bl3<br>Download the latest binary: <a
href="https://github.com/Aircoookie/WLED/releases" target="_blank"><img
src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square">
</a><br><input type="file" class="bt" name="update" required><br><input

View File

@ -192,7 +192,7 @@ const char PAGE_settings_dmx[] PROGMEM = R"=====()=====";
// Autogenerated from wled00/data/settings_ui.htm, do not edit!!
const char PAGE_settings_ui[] PROGMEM = R"=====(<!DOCTYPE html><html><head lang="en"><meta charset="utf-8"><meta
name="viewport" content="width=500"><title>UI Settings</title><script>
var initial_ds,initial_st,timeout,d=document,sett=null,l={comp:{labels:"Show button labels",colors:{LABEL:"Color selection methods",picker:"Color Wheel",rgb:"RGB sliders",quick:"Quick color selectors",hex:"HEX color input"},pcmbot:"Show bottom tab bar in PC mode",pid:"Show preset IDs",seglen:"Use LED Count instead of Stop LED on segments"},theme:{alpha:{bg:"Background opacity",tab:"Button opacity"},bg:{url:"BG image URL",random:"Random BG image"},color:{bg:"BG HEX color"}}};function gId(e){return d.getElementById(e)}function isObject(e){return e&&"object"==typeof e&&!Array.isArray(e)}function set(e,t,i){for(var n=t,o=e.split("_"),s=o.length,a=0;a<s-1;a++){var l=o[a];n[l]||(n[l]={}),n=n[l]}n[o[s-1]]=i}function showToast(e,t=!1){var i=gId("toast");i.innerHTML=e,i.className=t?"error":"show",clearTimeout(timeout),i.style.animation="none",timeout=setTimeout((function(){i.className=i.className.replace("show","")}),2900)}function addRec(e,t="",n=null){var o="";for(i in e){var s=t+(t?"_":"")+i;if(isObject(e[i]))n&&n[i]&&n[i].LABEL&&(o+=`<h3>${n[i].LABEL}</h3>`),o+=addRec(e[i],s,n?n[i]:null);else{var a=s;if(n&&n[i]?a=n[i]:e[i+"LABEL"]&&(a=e[i+"LABEL"]),i.indexOf("LABEL")>0)continue;var l=typeof e[i];gId(s)?("boolean"===l?gId(s).checked=e[i]:gId(s).value=e[i],gId(s).previousElementSibling.matches(".l")&&(gId(s).previousElementSibling.innerHTML=a)):"boolean"===l?o+=`${a}: <input class="agi cb" type="checkbox" id=${s} ${e[i]?"checked":""}><br>`:"number"===l?o+=`${a}: <input class="agi" type="number" id=${s} value=${e[i]}><br>`:"string"===l&&(o+=`${a}:<br><input class="agi" id=${s} value=${e[i]}><br>`)}}return o}function genForm(e){var t;t=addRec(e,"",l),gId("gen").innerHTML=t}function GetLS(){(sett=localStorage.getItem("wledUiCfg"))||(gId("lserr").style.display="inline");try{sett=JSON.parse(sett)}catch(e){sett={},gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON parsing failed. ("+e+")"}genForm(sett),gId("dm").checked="light"===gId("theme_base").value}function SetLS(){for(var e=d.querySelectorAll(".agi"),t=0;t<e.length;t++){var i=e[t],n=i.classList.contains("cb")?i.checked:i.value;set(i.id,sett,n),console.log(`${i.id} set to ${n}`)}try{localStorage.setItem("wledUiCfg",JSON.stringify(sett)),gId("lssuc").style.display="inline"}catch(i){gId("lssuc").style.display="none",gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON saving failed. ("+i+")"}}function Save(){SetLS(),d.Sf.DS.value==initial_ds&&d.Sf.ST.checked==initial_st||d.Sf.submit()}function S(){GetV(),initial_ds=d.Sf.DS.value,initial_st=d.Sf.ST.checked,GetLS()}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings")}function B(){window.open("/settings","_self")}function UI(){gId("idonthateyou").style.display=gId("dm").checked?"inline":"none";var e=gId("theme_base");e&&(e.value=gId("dm").checked?"light":"dark")}function setRandomBg(){gId("theme_bg_random").checked?gId("theme_bg_url").value="https://picsum.photos/1920/1080":gId("theme_bg_url").value=""}function checkRandomBg(){"https://picsum.photos/1920/1080"===gId("theme_bg_url").value?gId("theme_bg_random").checked=!0:gId("theme_bg_random").checked=!1}function uploadFile(e,t){var i=new XMLHttpRequest;i.addEventListener("load",(function(){showToast(this.responseText)})),i.addEventListener("error",(function(e){showToast(e.stack,!0)})),i.open("POST","/upload");var n=new FormData;return n.append("data",e.files[0],t),i.send(n),e.value="",!1}function GetV() {var d=document;
var initial_ds,initial_st,initial_su,timeout,d=document,sett=null,l={comp:{labels:"Show button labels",colors:{LABEL:"Color selection methods",picker:"Color Wheel",rgb:"RGB sliders",quick:"Quick color selectors",hex:"HEX color input"},pcmbot:"Show bottom tab bar in PC mode",pid:"Show preset IDs",seglen:"Use LED Count instead of Stop LED on segments"},theme:{alpha:{bg:"Background opacity",tab:"Button opacity"},bg:{url:"BG image URL",random:"Random BG image"},color:{bg:"BG HEX color"}}};function gId(e){return d.getElementById(e)}function isObject(e){return e&&"object"==typeof e&&!Array.isArray(e)}function set(e,t,i){for(var n=t,o=e.split("_"),s=o.length,a=0;a<s-1;a++){var l=o[a];n[l]||(n[l]={}),n=n[l]}n[o[s-1]]=i}function showToast(e,t=!1){var i=gId("toast");i.innerHTML=e,i.className=t?"error":"show",clearTimeout(timeout),i.style.animation="none",timeout=setTimeout((function(){i.className=i.className.replace("show","")}),2900)}function addRec(e,t="",n=null){var o="";for(i in e){var s=t+(t?"_":"")+i;if(isObject(e[i]))n&&n[i]&&n[i].LABEL&&(o+=`<h3>${n[i].LABEL}</h3>`),o+=addRec(e[i],s,n?n[i]:null);else{var a=s;if(n&&n[i]?a=n[i]:e[i+"LABEL"]&&(a=e[i+"LABEL"]),i.indexOf("LABEL")>0)continue;var l=typeof e[i];gId(s)?("boolean"===l?gId(s).checked=e[i]:gId(s).value=e[i],gId(s).previousElementSibling.matches(".l")&&(gId(s).previousElementSibling.innerHTML=a)):"boolean"===l?o+=`${a}: <input class="agi cb" type="checkbox" id=${s} ${e[i]?"checked":""}><br>`:"number"===l?o+=`${a}: <input class="agi" type="number" id=${s} value=${e[i]}><br>`:"string"===l&&(o+=`${a}:<br><input class="agi" id=${s} value=${e[i]}><br>`)}}return o}function genForm(e){var t;t=addRec(e,"",l),gId("gen").innerHTML=t}function GetLS(){(sett=localStorage.getItem("wledUiCfg"))||(gId("lserr").style.display="inline");try{sett=JSON.parse(sett)}catch(e){sett={},gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON parsing failed. ("+e+")"}genForm(sett),gId("dm").checked="light"===gId("theme_base").value}function SetLS(){for(var e=d.querySelectorAll(".agi"),t=0;t<e.length;t++){var i=e[t],n=i.classList.contains("cb")?i.checked:i.value;set(i.id,sett,n),console.log(`${i.id} set to ${n}`)}try{localStorage.setItem("wledUiCfg",JSON.stringify(sett)),gId("lssuc").style.display="inline"}catch(i){gId("lssuc").style.display="none",gId("lserr").style.display="inline",gId("lserr").innerHTML="&#9888; Settings JSON saving failed. ("+i+")"}}function Save(){SetLS(),d.Sf.DS.value==initial_ds&&d.Sf.ST.checked==initial_st&&d.Sf.SU.checked==initial_su||d.Sf.submit()}function S(){GetV(),initial_ds=d.Sf.DS.value,initial_st=d.Sf.ST.checked,initial_su=d.Sf.SU.checked,GetLS()}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings")}function B(){window.open("/settings","_self")}function UI(){gId("idonthateyou").style.display=gId("dm").checked?"inline":"none";var e=gId("theme_base");e&&(e.value=gId("dm").checked?"light":"dark")}function setRandomBg(){gId("theme_bg_random").checked?gId("theme_bg_url").value="https://picsum.photos/1920/1080":gId("theme_bg_url").value=""}function checkRandomBg(){"https://picsum.photos/1920/1080"===gId("theme_bg_url").value?gId("theme_bg_random").checked=!0:gId("theme_bg_random").checked=!1}function uploadFile(e,t){var i=new XMLHttpRequest;i.addEventListener("load",(function(){showToast(this.responseText)})),i.addEventListener("error",(function(e){showToast(e.stack,!0)})),i.open("POST","/upload");var n=new FormData;return n.append("data",e.files[0],t),i.send(n),e.value="",!1}function GetV() {var d=document;
%CSS%%SCSS%<link href="/skin.css"
rel="stylesheet"></head><body onload="S()"><form id="form_s" name="Sf"
method="post"><div class="toprow"><div class="helpB"><button type="button"
@ -203,7 +203,8 @@ id="lserr" style="color:red;display:none">
&#9888; Could not access local storage. Make sure it is enabled in your browser.
</span><hr></div><h2>Web Setup</h2>Server description: <input name="DS"
maxlength="32"><br>Sync button toggles both send and receive: <input
type="checkbox" name="ST"><br><i>
type="checkbox" name="ST"><br>Enable simplified UI: <input type="checkbox"
name="SU"><br><i>
The following UI customization settings are unique both to the WLED device and this browser.
<br>
You will need to set them again if using a different browser, device or WLED IP address.
@ -408,7 +409,7 @@ onclick='uploadFile(d.Sf.data2,"/cfg.json")'><br></div><div style="color:#fa0">
</h3><button type="button" onclick="U()">Manual OTA Update</button><br>
Enable ArduinoOTA: <input type="checkbox" name="AO"><br><hr><h3>About</h3><a
href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a>
version 0.13.0-bl2<br><br><a
version 0.13.0-bl3<br><br><a
href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits"
target="_blank">Contributors, dependencies and special thanks</a><br>
A huge thank you to everyone who helped me create WLED!<br><br>

1791
wled00/html_simple.h Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -413,6 +413,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
root["on"] = (bri > 0);
root["bri"] = briLast;
root[F("transition")] = transitionDelay/100; //in 100ms
root[F("tdd")] = transitionDelayDefault/100; //in 100ms
}
if (!forPreset) {

View File

@ -46,10 +46,6 @@ byte scaledBri(byte in)
void setAllLeds() {
if (!realtimeMode || !arlsForceMaxBri)
{
strip.setBrightness(scaledBri(briT));
}
if (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY)
{
colorRGBtoRGBW(col);
@ -61,6 +57,10 @@ void setAllLeds() {
{
col[3] = 0; colSec[3] = 0;
}
if (!realtimeMode || !arlsForceMaxBri)
{
strip.setBrightness(scaledBri(briT));
}
}

View File

@ -192,6 +192,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
{
strlcpy(serverDescription, request->arg(F("DS")).c_str(), 33);
syncToggleReceive = request->hasArg(F("ST"));
#ifndef WLED_DISABLE_SIMPLE_UI
simplifiedUI = request->hasArg(F("SU"));
#endif
}
//SYNC

View File

@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2107311
#define VERSION 2108091
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -136,6 +136,9 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#include "fcn_declare.h"
#include "html_ui.h"
#ifndef WLED_DISABLE_SIMPLE_UI
#include "html_simple.h"
#endif
#include "html_settings.h"
#include "html_other.h"
#include "FX.h"
@ -278,6 +281,7 @@ WLED_GLOBAL byte briMultiplier _INIT(100); // % of brightness to set (t
// User Interface CONFIG
WLED_GLOBAL char serverDescription[33] _INIT("WLED"); // Name of module
WLED_GLOBAL bool syncToggleReceive _INIT(false); // UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise
WLED_GLOBAL bool simplifiedUI _INIT(false); // enable simplified UI
// Sync CONFIG
WLED_GLOBAL NodesMap Nodes;

View File

@ -16,13 +16,14 @@ bool isIp(String str) {
}
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
if(!index){
if (!index) {
request->_tempFile = WLED_FS.open(filename, "w");
if (filename == "/presets.json") presetsModifiedTime = toki.second();
}
if (len) {
request->_tempFile.write(data,len);
}
if(final){
if (final) {
request->_tempFile.close();
request->send(200, "text/plain", F("File Uploaded!"));
}
@ -298,7 +299,13 @@ void serveIndex(AsyncWebServerRequest* request)
if (handleIfNoneMatchCacheHeader(request)) return;
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_index, PAGE_index_L);
AsyncWebServerResponse *response;
#ifndef WLED_DISABLE_SIMPLE_UI
if (simplifiedUI)
response = request->beginResponse_P(200, "text/html", PAGE_simple, PAGE_simple_L);
else
#endif
response = request->beginResponse_P(200, "text/html", PAGE_index, PAGE_index_L);
response->addHeader(F("Content-Encoding"),"gzip");
setStaticContentCacheHeaders(response);

View File

@ -422,6 +422,7 @@ void getSettingsJS(byte subPage, char* dest)
{
sappends('s',SET_F("DS"),serverDescription);
sappend('c',SET_F("ST"),syncToggleReceive);
sappend('c',SET_F("SU"),simplifiedUI);
}
if (subPage == 4)