Smooth graphs, sun path card, skeleton page switches and editor drag & drop
Firmware 0.2.15: trend graphs use a Catmull-Rom curve with a soft fill drawn by the card's draw event; sun.sun gets a wide sun-path card with horizon, arc and the sun at its current position; page switches show the new page's card frames immediately and fill them from a one-shot timer; the navigation is one full-width bar without a divider; the CYD adopts the Guition light palette in styles and overlays. App 0.2.13: pointer-based drag & drop (mouse and touch) to reorder tiles in the mockup and to drop entities from the list, sticky save bar, a new header with explained actions and a three-step guide, clearer firmware dialog. New sun, weather and clock tiles start wide in their best view. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,13 @@
|
||||
## 0.2.13 (firmware 0.2.15)
|
||||
|
||||
- Editor: sleep tegels in de schermmockup om te ordenen en sleep entiteiten rechtstreeks uit de lijst naar een plek in de mockup — met muis én touch (even vasthouden). De opslaan-balk blijft altijd in beeld.
|
||||
- Nieuwe kop met duidelijke acties (**Nieuw scherm**, **Firmware & USB**, **Uitleg**) en een korte uitleg in drie stappen; de firmwaredialoog legt profiel, doel en knoppen uit.
|
||||
- **Zonnebaan**: `sun.sun` toont dubbelbreed een horizon met de zon op zijn huidige positie tussen opkomst en ondergang (’s nachts onder de horizon), met beide tijden. Nieuwe zon-, weer- en kloktegels starten meteen dubbelbreed in hun mooiste weergave.
|
||||
- Grafiek: vloeiende curve door dezelfde 24 punten met een zachte vulling eronder, zoals de HA-trendkaart.
|
||||
- Scherm: de paginering is één geïntegreerde onderbalk — links tikken is vorige, rechts is volgende, het paginanummer staat in het midden.
|
||||
- Scherm: bij een paginawissel verschijnen direct de kaartkaders van de nieuwe pagina (skeleton) en vult de inhoud een fractie later; geen oude waarden meer in nieuwe kaders.
|
||||
- CYD: hetzelfde lichte kleurenschema als de Guition — lichtgrijze achtergrond, witte kaarten met een fijne rand, donkere tekst en lichtblauwe accenten, ook in de bedieningskaarten.
|
||||
|
||||
## 0.2.12 (firmware 0.2.14)
|
||||
|
||||
- Nieuwe kaarten in de kiezer: **Klok** (digitaal of analoog, ingebouwd), **weersvoorspelling** met vijf dagen op een dubbelbrede weerkaart, **grafiek** van de sensorgeschiedenis in de tegel, **zon** (`sun.sun`, opkomst en ondergang in je eigen tijdzone), **kookwekker** (`timer.*`, live aftellen; tikken start of pauzeert, lang indrukken annuleert) en **aanwezigheid** (`person.*`).
|
||||
|
||||
@@ -30,7 +30,9 @@ TILE_BACKGROUNDS = {
|
||||
}
|
||||
|
||||
# Display modes per domain; everything else offers standard and watch (large value).
|
||||
DISPLAYS = {'weather': ('standard', 'watch', 'forecast'), 'sensor': ('standard', 'watch', 'graph'), 'screen': ('digital', 'analog')}
|
||||
DISPLAYS = {'weather': ('standard', 'watch', 'forecast'), 'sensor': ('standard', 'watch', 'graph'), 'screen': ('digital', 'analog'), 'sun': ('standard', 'watch', 'sunpath')}
|
||||
# Displays that only work on a double-width card.
|
||||
WIDE_ONLY = ('forecast', 'sunpath')
|
||||
|
||||
# Additive schema 1 extension. An absent object retains old firmware/YAML defaults.
|
||||
SETTING_RULES = {
|
||||
@@ -114,8 +116,8 @@ def validate_layout(data):
|
||||
for key, allowed in choices.items():
|
||||
if key in options and options[key] not in allowed:
|
||||
raise ValueError('Ongeldige tegelinstelling: ' + key)
|
||||
# The five-day strip only fits a double-width card.
|
||||
if options.get('display') == 'forecast':
|
||||
# The five-day strip and the sun path only fit a double-width card.
|
||||
if options.get('display') in WIDE_ONLY:
|
||||
options = {**options, 'size': 'wide'}
|
||||
if options.get('tap') == 'toggle' and domain not in {'light','switch','input_boolean','fan','media_player'}:
|
||||
raise ValueError('Deze entiteit ondersteunt geen aan/uit-actie.')
|
||||
|
||||
@@ -5,7 +5,6 @@ let inventory = { screens: [], entities: [] },
|
||||
layout = null,
|
||||
dirty = false,
|
||||
filter = "",
|
||||
dragIndex = -1,
|
||||
busy = false,
|
||||
selectedTile = null;
|
||||
const node = (tag, text, cls) => {
|
||||
@@ -225,7 +224,15 @@ const domains = {
|
||||
timer: ["Kookwekker", "⏱", "#008577", "#def3ed"],
|
||||
person: ["Persoon", "☺", "#2f7d32", "#e1f2e2"],
|
||||
};
|
||||
const displayNames = { standard: "standaard", watch: "grote waarde", forecast: "weersvoorspelling", graph: "grafiek", digital: "digitale klok", analog: "analoge klok" };
|
||||
const displayNames = { standard: "standaard", watch: "grote waarde", forecast: "weersvoorspelling", graph: "grafiek", digital: "digitale klok", analog: "analoge klok", sunpath: "zonnebaan" };
|
||||
// New tiles start with the card that shows the entity best.
|
||||
function defaultOptions(id) {
|
||||
const domain = id.split(".")[0];
|
||||
if (domain === "sun") return { options: { display: "sunpath", size: "wide" } };
|
||||
if (domain === "weather") return { options: { display: "forecast", size: "wide" } };
|
||||
if (domain === "screen") return { options: { display: "digital", size: "wide" } };
|
||||
return {};
|
||||
}
|
||||
// Same packing as the firmware: wide tiles start in the left column and take a whole row.
|
||||
function packTiles(tiles) {
|
||||
let position = 0;
|
||||
@@ -283,11 +290,8 @@ function renderPreview() {
|
||||
if(background)card.style.backgroundColor=background;
|
||||
if (place.wide) card.classList.add("wide");
|
||||
card.append(domainBadge(tile.entity), node("strong", name));
|
||||
card.draggable = true;
|
||||
card.ondragstart = (e) => { dragIndex = index; e.dataTransfer.setData("text/plain", String(index)); };
|
||||
card.ondragend = () => { dragIndex = -1; };
|
||||
card.ondragover = (e) => { if (dragIndex >= 0) e.preventDefault(); };
|
||||
card.ondrop = (e) => { e.preventDefault(); if (dragIndex >= 0) move(dragIndex, index); };
|
||||
card.dataset.index = index;
|
||||
enableDrag(card, { kind: "tile", index });
|
||||
card.classList.toggle("chosen", selectedTile === tile.entity);
|
||||
card.setAttribute("aria-label", `Tegel ${index + 1}: ${name}, instellen`);
|
||||
card.onclick = () => {
|
||||
@@ -322,27 +326,6 @@ function renderTiles() {
|
||||
if (i > 0 && placement[i].page !== placement[i - 1].page) $("#tiles").append(node("li", `Pagina ${placement[i].page + 1}`, "page-break"));
|
||||
const li = node("li", undefined, "tile");
|
||||
li.classList.toggle("selected-tile", selectedTile === tile.entity);
|
||||
li.draggable = true;
|
||||
li.ondragstart = (e) => {
|
||||
if (e.target.closest("input, select, button")) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
dragIndex = i;
|
||||
e.dataTransfer.setData("text/plain", String(i));
|
||||
li.classList.add("dragging");
|
||||
};
|
||||
li.ondragend = () => {
|
||||
dragIndex = -1;
|
||||
li.classList.remove("dragging");
|
||||
};
|
||||
li.ondragover = (e) => {
|
||||
if (dragIndex >= 0) e.preventDefault();
|
||||
};
|
||||
li.ondrop = (e) => {
|
||||
e.preventDefault();
|
||||
if (dragIndex >= 0) move(dragIndex, i);
|
||||
};
|
||||
const content = node("div"),
|
||||
input = node("input");
|
||||
input.value = tile.name;
|
||||
@@ -423,6 +406,7 @@ function renderTiles() {
|
||||
: [["standard", "Naam en status"], ["watch", "Grote waarde"]];
|
||||
if (domain === "weather") displays.push(["forecast", "Weersvoorspelling (dubbelbreed)"]);
|
||||
if (domain === "sensor") displays.push(["graph", "Grafiek van de geschiedenis"]);
|
||||
if (domain === "sun") displays.push(["sunpath", "Zonnebaan (dubbelbreed)"]);
|
||||
const fields = [
|
||||
[
|
||||
"tap",
|
||||
@@ -498,7 +482,7 @@ function renderTiles() {
|
||||
};
|
||||
if (key === "display" && input.value === "watch")
|
||||
tile.options.inline = "none";
|
||||
if (key === "display" && input.value === "forecast")
|
||||
if (key === "display" && ["forecast", "sunpath"].includes(input.value))
|
||||
tile.options.size = "wide";
|
||||
if (key === "inline" && input.value === "slider")
|
||||
tile.options.display = "standard";
|
||||
@@ -522,6 +506,97 @@ function renderTiles() {
|
||||
$("#tiles").append(li);
|
||||
});
|
||||
}
|
||||
function addTile(id, at) {
|
||||
if (layout.tiles.some((t) => t.entity === id) || layout.tiles.length >= tileLimit()) return;
|
||||
layout.tiles.splice(Math.min(at, layout.tiles.length), 0, { entity: id, name: "", ...defaultOptions(id) });
|
||||
selectedTile = id;
|
||||
markDirty();
|
||||
renderTiles();
|
||||
renderResults();
|
||||
}
|
||||
// Pointer-based drag & drop: works with mouse and touch, from the picker into
|
||||
// the mockup and between tiles. Touch starts after a short hold so the page
|
||||
// still scrolls; a finished drag never doubles as a click.
|
||||
const drag = { active: false, source: null, element: null, ghost: null, target: null, timer: 0, start: null, offset: null, pointerId: null, suppressUntil: 0, last: null, scroller: 0 };
|
||||
function enableDrag(element, source) {
|
||||
element.addEventListener("pointerdown", (e) => {
|
||||
if (e.button !== 0 || element.disabled) return;
|
||||
Object.assign(drag, { source, element, start: { x: e.clientX, y: e.clientY }, pointerId: e.pointerId, active: false });
|
||||
clearTimeout(drag.timer);
|
||||
if (e.pointerType === "touch") drag.timer = setTimeout(() => beginDrag(e), 260);
|
||||
});
|
||||
element.addEventListener("pointermove", (e) => {
|
||||
if (!drag.start || drag.element !== element) return;
|
||||
const distance = Math.hypot(e.clientX - drag.start.x, e.clientY - drag.start.y);
|
||||
if (!drag.active) {
|
||||
if (e.pointerType === "touch") { if (distance > 10) { clearTimeout(drag.timer); drag.start = null; } return; }
|
||||
if (distance < 6) return;
|
||||
beginDrag(e);
|
||||
}
|
||||
moveDrag(e);
|
||||
});
|
||||
const finish = (e) => {
|
||||
if (drag.element !== element) return;
|
||||
clearTimeout(drag.timer);
|
||||
if (drag.active) endDrag(e.type === "pointerup");
|
||||
drag.start = null;
|
||||
};
|
||||
element.addEventListener("pointerup", finish);
|
||||
element.addEventListener("pointercancel", finish);
|
||||
}
|
||||
function beginDrag(e) {
|
||||
if (drag.active || !drag.start) return;
|
||||
drag.active = true;
|
||||
try { drag.element.setPointerCapture(drag.pointerId); } catch {}
|
||||
const rect = drag.element.getBoundingClientRect();
|
||||
const ghost = drag.element.cloneNode(true);
|
||||
ghost.classList.add("drag-ghost");
|
||||
ghost.style.width = `${rect.width}px`;
|
||||
drag.offset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
|
||||
document.body.append(ghost);
|
||||
drag.ghost = ghost;
|
||||
document.body.classList.add("dragging");
|
||||
document.addEventListener("touchmove", blockScroll, { passive: false });
|
||||
// Near the viewport edges the page scrolls along, so the mockup can be reached on small screens.
|
||||
drag.scroller = setInterval(() => {
|
||||
if (!drag.last) return;
|
||||
const step = drag.last.y < 70 ? -12 : drag.last.y > innerHeight - 70 ? 12 : 0;
|
||||
if (step) { window.scrollBy(0, step); setTarget(document.elementFromPoint(drag.last.x, drag.last.y)?.closest(".preview-tile, .preview-gap, .preview-grid") || null); }
|
||||
}, 16);
|
||||
moveDrag(e);
|
||||
}
|
||||
function blockScroll(e) { if (drag.active) e.preventDefault(); }
|
||||
function moveDrag(e) {
|
||||
if (!drag.ghost) return;
|
||||
drag.last = { x: e.clientX, y: e.clientY };
|
||||
drag.ghost.style.transform = `translate(${e.clientX - drag.offset.x}px, ${e.clientY - drag.offset.y}px)`;
|
||||
const under = document.elementFromPoint(e.clientX, e.clientY);
|
||||
setTarget(under?.closest(".preview-tile, .preview-gap, .preview-grid") || null);
|
||||
}
|
||||
function setTarget(target) {
|
||||
if (drag.target === target) return;
|
||||
drag.target?.classList.remove("drop-target");
|
||||
drag.target = target;
|
||||
drag.target?.classList.add("drop-target");
|
||||
}
|
||||
function endDrag(drop) {
|
||||
const { target, source } = drag;
|
||||
setTarget(null);
|
||||
drag.ghost?.remove();
|
||||
drag.ghost = null;
|
||||
drag.active = false;
|
||||
drag.suppressUntil = Date.now() + 400;
|
||||
clearInterval(drag.scroller);
|
||||
drag.last = null;
|
||||
document.body.classList.remove("dragging");
|
||||
document.removeEventListener("touchmove", blockScroll);
|
||||
try { drag.element.releasePointerCapture(drag.pointerId); } catch {}
|
||||
if (!drop || !target) return;
|
||||
const to = target.dataset.index !== undefined ? Number(target.dataset.index) : layout.tiles.length;
|
||||
if (source.kind === "tile") { if (to !== source.index) move(source.index, to); }
|
||||
else addTile(source.id, to);
|
||||
}
|
||||
window.addEventListener("click", (e) => { if (Date.now() < drag.suppressUntil) { e.stopPropagation(); e.preventDefault(); } }, true);
|
||||
function renderResults() {
|
||||
if (!layout) return;
|
||||
const query = $("#search").value.toLocaleLowerCase();
|
||||
@@ -550,13 +625,8 @@ function renderResults() {
|
||||
node("span", chosen.has(entity.id) ? "✓" : "+", "plus"),
|
||||
);
|
||||
b.disabled = chosen.has(entity.id) || layout.tiles.length >= tileLimit();
|
||||
b.onclick = () => {
|
||||
layout.tiles.push({ entity: entity.id, name: "" });
|
||||
selectedTile = entity.id;
|
||||
markDirty();
|
||||
renderTiles();
|
||||
renderResults();
|
||||
};
|
||||
b.onclick = () => addTile(entity.id, layout.tiles.length);
|
||||
if (!b.disabled) enableDrag(b, { kind: "entity", id: entity.id });
|
||||
$("#results").append(b);
|
||||
}
|
||||
if (!matches.length)
|
||||
@@ -845,6 +915,7 @@ function openSection(id) {
|
||||
section.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
$("#nav-tiles").onclick = () => openSection("#tile-section");
|
||||
$("#open-help").onclick = () => openSection("#help");
|
||||
$("#nav-settings").onclick = () => openSection("#general-settings");
|
||||
$("#nav-inspector").onclick = () => inspect();
|
||||
async function inspect(entity) {
|
||||
|
||||
@@ -12,11 +12,29 @@
|
||||
<div class="brand">
|
||||
<span class="mark">▦</span>
|
||||
<div>
|
||||
<strong>ESP Screens</strong><small>Je huis. Jouw bediening.</small>
|
||||
<strong>ESP Screens</strong
|
||||
><small>Tegels voor je CYD en Guition, zonder opnieuw flashen</small>
|
||||
</div>
|
||||
</div>
|
||||
<button id="open-firmware" class="quiet">Firmware & USB</button>
|
||||
<button id="new-screen">+ Nieuw scherm</button>
|
||||
<nav class="toolbar" aria-label="Acties">
|
||||
<button id="new-screen" class="tool tool-primary">
|
||||
<span class="tool-icon" aria-hidden="true">+</span>
|
||||
<span class="tool-text"
|
||||
><b>Nieuw scherm</b><small>Koppel een CYD of Guition</small></span
|
||||
>
|
||||
</button>
|
||||
<button id="open-firmware" class="tool">
|
||||
<span class="tool-icon" aria-hidden="true">⇪</span>
|
||||
<span class="tool-text"
|
||||
><b>Firmware & USB</b
|
||||
><small>Bouwen en installeren via USB of wifi</small></span
|
||||
>
|
||||
</button>
|
||||
<button id="open-help" class="tool">
|
||||
<span class="tool-icon" aria-hidden="true">?</span>
|
||||
<span class="tool-text"><b>Uitleg</b><small>Hoe werkt het</small></span>
|
||||
</button>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<div class="intro">
|
||||
@@ -27,6 +45,26 @@
|
||||
</div>
|
||||
<span id="connection" role="status" class="badge">Verbinden…</span>
|
||||
</div>
|
||||
<ol class="howto" aria-label="Zo werkt het">
|
||||
<li>
|
||||
<b>1 · Koppelen</b><span
|
||||
>Kies <b>Nieuw scherm</b> voor de installatie-YAML met eigen
|
||||
sleutels. Eén keer via USB flashen, daarna gaat alles draadloos.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<b>2 · Tegels kiezen</b><span
|
||||
>Sleep entiteiten uit de lijst rechts in de schermmockup, of tik
|
||||
op +. Klik op een tegel voor weergave, breedte en kleur.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<b>3 · Opslaan</b><span
|
||||
>Met <b>Opslaan & naar scherm</b> staat het direct op je scherm.
|
||||
Nieuwe schermfuncties installeer je via <b>Firmware & USB</b>.</span
|
||||
>
|
||||
</li>
|
||||
</ol>
|
||||
<div class="workspace">
|
||||
<aside>
|
||||
<h2>Mijn schermen</h2>
|
||||
@@ -136,7 +174,7 @@
|
||||
<b>Firmware & USB</b> bovenaan. Je bestaande YAML en secrets blijven
|
||||
bewaard.
|
||||
</p>
|
||||
<details class="help">
|
||||
<details class="help" id="help">
|
||||
<summary>Hoe werkt het dagelijks?</summary>
|
||||
<p>
|
||||
Zoek een entiteit, voeg deze toe en klik op
|
||||
@@ -147,9 +185,18 @@
|
||||
waarde, mini-schuif of klikgedrag.
|
||||
</p>
|
||||
<p>
|
||||
Nieuwe firmware bouw en installeer je hier met de ingebouwde ESPHome
|
||||
CLI. Je tegels blijven in deze app bewaard. Een offline scherm krijgt
|
||||
de wijzigingen zodra het terug is.
|
||||
Speciale kaarten: de <b>Klok</b> staat bovenaan de kiezer; een
|
||||
weerentiteit toont de vijfdaagse voorspelling, <code>sun.sun</code>
|
||||
de zonnebaan, een sensor een trendgrafiek, een <code>timer</code>
|
||||
telt live af en een <code>person</code> toont thuis/weg. Zet een
|
||||
tegel op <b>Dubbelbreed</b> voor meer ruimte.
|
||||
</p>
|
||||
<p>
|
||||
Nieuwe firmware bouw en installeer je via <b>Firmware & USB</b> met
|
||||
de ingebouwde ESPHome CLI: kies je eigen profiel, dan een USB-poort of
|
||||
wifi, en klik op <b>Bouwen & installeren</b>. Je tegels blijven in
|
||||
deze app bewaard; een offline scherm krijgt de wijzigingen zodra het
|
||||
terug is.
|
||||
</p>
|
||||
</details>
|
||||
</main>
|
||||
@@ -271,13 +318,19 @@
|
||||
</dialog>
|
||||
<dialog id="firmware-dialog">
|
||||
<div class="dialog-head">
|
||||
<h2>Firmware & USB</h2>
|
||||
<button id="close-firmware" class="quiet">Sluiten</button>
|
||||
<div>
|
||||
<p class="eyebrow">FIRMWARE & USB</p>
|
||||
<h2>Schermfirmware bouwen en installeren</h2>
|
||||
</div>
|
||||
<button id="close-firmware" class="quiet" aria-label="Sluiten">✕</button>
|
||||
</div>
|
||||
<p>
|
||||
De ingebouwde ESPHome CLI gebruikt je eigen YAML en secrets. Tegels
|
||||
blijven in ESP Screens staan. Op de Raspberry kan de eerste build enkele
|
||||
minuten duren.
|
||||
De ingebouwde ESPHome CLI gebruikt je eigen profiel (YAML met sleutels)
|
||||
en secrets. Kies het profiel van het scherm, dan een USB-poort (scherm
|
||||
aan de Home Assistant-machine) of <b>Wifi / OTA</b> met de hostnaam.
|
||||
<b>Controleren</b> valideert alleen, <b>Alleen bouwen</b> compileert,
|
||||
<b>Bouwen & installeren</b> zet de firmware op het scherm. Je tegels
|
||||
blijven in ESP Screens staan. De eerste build duurt enkele minuten.
|
||||
</p>
|
||||
<label>ESPHome-profiel<select id="firmware-file"></select></label>
|
||||
<label
|
||||
|
||||
@@ -184,6 +184,98 @@ aside h2 {
|
||||
color: var(--muted);
|
||||
padding: 7px;
|
||||
}
|
||||
.toolbar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.tool {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 14px 8px 9px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 14px;
|
||||
background: white;
|
||||
color: var(--ink);
|
||||
text-align: left;
|
||||
}
|
||||
.tool:hover {
|
||||
border-color: #b9cbec;
|
||||
box-shadow: 0 3px 10px #202c3c0c;
|
||||
}
|
||||
.tool-icon {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 10px;
|
||||
background: var(--soft);
|
||||
color: var(--accent);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 19px;
|
||||
font-weight: 700;
|
||||
flex: none;
|
||||
}
|
||||
.tool-primary .tool-icon {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
}
|
||||
.tool-text b {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
}
|
||||
.tool-text small {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
.howto {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0 0 26px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.howto li {
|
||||
background: white;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 14px;
|
||||
padding: 12px 14px;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
line-height: 1.45;
|
||||
}
|
||||
.howto li > b {
|
||||
display: block;
|
||||
color: var(--ink);
|
||||
font-size: 13px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.preview-tile,
|
||||
.result {
|
||||
touch-action: pan-y;
|
||||
cursor: grab;
|
||||
}
|
||||
.drag-ghost {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
opacity: 0.92;
|
||||
box-shadow: 0 14px 32px #202c3c33;
|
||||
background: white;
|
||||
margin: 0;
|
||||
}
|
||||
.drop-target {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
background: var(--soft) !important;
|
||||
}
|
||||
body.dragging {
|
||||
cursor: grabbing;
|
||||
user-select: none;
|
||||
}
|
||||
#editor,
|
||||
.empty {
|
||||
background: white;
|
||||
@@ -331,9 +423,15 @@ aside h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
.savebar {
|
||||
margin-top: 25px;
|
||||
padding-top: 20px;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
z-index: 6;
|
||||
margin: 25px -27px -27px;
|
||||
padding: 16px 27px;
|
||||
background: #ffffffee;
|
||||
backdrop-filter: blur(6px);
|
||||
border-top: 1px solid var(--line);
|
||||
border-radius: 0 0 18px 18px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -475,10 +573,22 @@ dialog label {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 1000px) {
|
||||
.howto {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
@media (max-width: 620px) {
|
||||
main {
|
||||
padding: 25px 16px;
|
||||
}
|
||||
.tool-text small {
|
||||
display: none;
|
||||
}
|
||||
.savebar {
|
||||
margin: 25px -18px -18px;
|
||||
padding: 14px 18px;
|
||||
}
|
||||
.workspace {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: ESP Screen Manager
|
||||
version: "0.2.12"
|
||||
version: "0.2.13"
|
||||
slug: esp_screen_manager
|
||||
description: Kies tegels voor je CYD en Guition touchscreen, zonder opnieuw flashen.
|
||||
url: https://github.com/MaxGramser/homeassistant_espscreen
|
||||
|
||||
Reference in New Issue
Block a user