Skip to content

Commit dd3b4d7

Browse files
fhirschmannclaude
andcommitted
feat(webui): Tools tab as left-nav sidebar + stop-instead-of-pause on tag removal
Tools tab now uses the same vertical UniFi-style sidebar as Settings: Command / File sync / RFID sync / NVS / EQ rules / Backup / Firmware are individual sub-panes (icon-only on mobile, pinned while scrolling) instead of one long stacked scroll. The jump-to-section helpers (sync runner, RFID sync, firmware update) now activate the matching sub-pane. New "Stop instead of pause on removal" option (General -> options, a sub-option of "Pause when RFID tag is removed"): pulling the tag fully stops playback and clears the playlist rather than just pausing, and re-applying the tag restarts it from the beginning. Persisted in NVS (stopRfidRem) and applied live without a reboot; covers both the PN5180 and MFRC522 readers. UI checkbox is only enabled while pause-on-removal is on. Demo mock + i18n (de/en/fr) updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 9e171d2 commit dd3b4d7

12 files changed

Lines changed: 201 additions & 47 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ neon logo that doubles as the SVG favicon ([`7be5254`](../../commit/7be5254)):
134134
| **Browser demo on GitHub Pages** ([live](https://fhirschmann.github.io/leoino/)): a device-free static build of the management page with a mock WebSocket/REST layer and copyright-free content, auto-deployed on every push so the UI can be tried without hardware. A CI check (`tools/check_demo_endpoints.py`) fails the build if a new firmware HTTP endpoint is added without a matching demo-mock handler (or an explicit allow-list entry), so the demo can't silently break | [`8bf93da`](../../commit/8bf93da) |
135135
| **Consolidated, auto-saving Settings tab**: WiFi/MQTT/FTP/Bluetooth merged with the general settings into one tab; the sub-tabs are a **vertical UniFi-style sidebar** (icon-only on mobile; stays pinned while scrolling) with flat, consistent sections, split into General / **Buttons** / **LEDs** / **Power** / **RFID** / … . General/Buttons/LEDs/Power/RFID auto-save (debounced); the credential tabs keep an explicit submit on purpose | [`8b01876`](../../commit/8b01876) · [`1944cf4`](../../commit/1944cf4) · [`c284d0e`](../../commit/c284d0e) · [`32aabfd`](../../commit/32aabfd) |
136136
| **Configurable branding**: re-brand the navbar header *and* footer from a single **Brand name** field (General → Branding) with a live preview as you type — empty keeps the "Leo Industries" default, so the fork is easy to re-brand | [`fd57fcb`](../../commit/fd57fcb) |
137+
| **Tools tab as a left-nav sidebar**: the Tools tab now uses the same **vertical UniFi-style sidebar** as Settings — Command / File sync / RFID sync / NVS / EQ rules / Backup / Firmware are individual sub-panes (icon-only on mobile, pinned while scrolling) instead of one long stacked scroll | [`PENDING`](../../commit/PENDING) |
137138
| **Password protection**: single password (no username), 90-day `HttpOnly` session cookie, brute-force lockout, logout entry; off in hotspot mode. Scripts/API clients authenticate with the password as an **API key** (`X-API-Key` header) | [`e74e712`](../../commit/e74e712) |
138139
| **One shared device password (Security tab)**: the password fields were pulled out of the WiFi/FTP/WebDAV tabs into a single **Security** sub-tab — one password now protects the web interface, FTP *and* WebDAV (set once, applies everywhere; applied live without a reboot). An **empty password is accepted** and disables protection for all three. The **hostname** moved to the top of the **General** tab (saved on its own; `/wificonfig` now does partial updates) and the navbar brand reads just **Leo Industries** | [`c6dfc67`](../../commit/c6dfc67) |
139140
| **FTP/WebDAV accept any username**: with one shared password the username is pointless, so both servers now authenticate by **password only — any username is accepted** (wrong password still rejected; empty device password = open). The username fields are gone from both tabs. FTP uses a **vendored, patched copy** of ESP-FTP-Server-Lib in [`lib/`](lib/ESP-FTP-Server-Lib) (password-only auth); WebDAV's self-contained server decodes the HTTP-Basic header and compares only the password | [`be96850`](../../commit/be96850) |
@@ -229,6 +230,7 @@ neon logo that doubles as the SVG favicon ([`7be5254`](../../commit/7be5254)):
229230
| Change | Commit |
230231
| --- | --- |
231232
| Tag removal detected via consecutive-miss counter instead of a wall-clock timeout: pause after ~0.5 s, immune to phantom dropouts and task starvation | [`1fad9cd`](../../commit/1fad9cd) |
233+
| **Stop instead of pause when the tag is removed** (General → options, sub-option of "Pause when RFID tag is removed"): pulling the card fully stops playback (clears the playlist) rather than just pausing; re-applying the card starts it again from the beginning. Persisted in NVS (`stopRfidRem`) and applied live without a reboot | [`PENDING`](../../commit/PENDING) |
232234
| Vendored PN5180 library with fast no-card detection: read attempts on an empty field take ~25 ms instead of ~230 ms (no more 200 ms timeout) | [`8762784`](../../commit/8762784) |
233235
| SLIX2 password support for protected ICODE-SLIX2 tags | [`d3cc69c`](../../commit/d3cc69c) |
234236
| Configurable idle LED and progress bar colors | [`bdc54e5`](../../commit/bdc54e5) |

‎html/locales/de.json‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,8 @@
696696
"restartFreshHrsExp": "Wird ein Hörbuch länger als die angegebene Stundenzahl nicht abgespielt, startet es beim nächsten Auflegen wieder von vorn statt an der gespeicherten Stelle. Praktisch für Kinder, die sich nach einem Tag Pause nicht mehr erinnern, wo sie waren. 0 = aus (immer fortsetzen). Standard: 24. Benötigt eine gültige Uhrzeit (NTP/RTC). Fork-Funktion.",
697697
"pauseIfRfidRemoved": "Pause wenn RFID-Tag entfernt",
698698
"pauseIfRfidRemovedExp": "Pausieren wenn RFID-Tag entfernt wird. Mehr Infos <a href='https://forum.espuino.de/t/welche-optionen-beim-kompilieren-gibt-es/120' target='_blank'>hier</a> unter <code>PAUSE_WHEN_RFID_REMOVED</code>.",
699+
"stopIfRfidRemoved": "Bei Entfernen stoppen statt pausieren",
700+
"stopIfRfidRemovedExp": "Wenn aktiv (nur in Kombination mit „Pause wenn RFID-Tag entfernt“): Beim Abziehen der Karte wird die Wiedergabe vollständig gestoppt statt nur pausiert. Erneutes Auflegen startet die Karte von vorn.",
699701
"dontAcceptRfidTwice": "Denselben RFID-Tag nicht erneut akzeptieren",
700702
"resumeOnSameRfidExp": "Wenn die Wiedergabe pausiert ist und derselbe RFID-Tag erneut aufgelegt wird, wird die Wiedergabe fortgesetzt. Funktioniert nur, wenn <em>Denselben RFID-Tag nicht erneut akzeptieren</em> aktiv ist.",
701703
"playLastRfidOnReboot": "Letzten RFID-Tag nach Neustart abspielen",
@@ -813,6 +815,15 @@
813815
"started": "Gerät installiert die Firmware und startet neu – die Seite lädt gleich automatisch neu…",
814816
"error": "Update fehlgeschlagen (siehe Konsole/Log)."
815817
}
818+
},
819+
"nav": {
820+
"command": "Befehl",
821+
"sync": "Datei-Sync",
822+
"rfidsync": "RFID-Sync",
823+
"nvs": "NVS",
824+
"eqrules": "EQ-Regeln",
825+
"backup": "Backup",
826+
"fwupdate": "Firmware"
816827
}
817828
},
818829
"help": {

‎html/locales/en.json‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,8 @@
696696
"restartFreshHrsExp": "If an audiobook has not been played for more than the given number of hours, it starts over from the beginning the next time the card is placed, instead of resuming the saved position. Handy for kids who, after a day's break, no longer remember where they were. 0 = off (always resume). Default: 24. Requires a valid clock (NTP/RTC). Fork feature.",
697697
"pauseIfRfidRemoved": "Pause when RFID tag is removed",
698698
"pauseIfRfidRemovedExp": "Playback is paused when the RFID tag is removed. More information <a href='https://forum.espuino.de/t/welche-optionen-beim-kompilieren-gibt-es/120' target='_blank'>here</a> under <code>PAUSE_IF_RFID_REMOVED</code>.",
699+
"stopIfRfidRemoved": "Stop instead of pause on removal",
700+
"stopIfRfidRemovedExp": "When enabled (only together with “Pause when RFID tag is removed”): removing the tag fully stops playback instead of just pausing. Re-applying the tag starts it again from the beginning.",
699701
"dontAcceptRfidTwice": "Don't accept the same RFID twice",
700702
"dontAcceptRfidTwiceExp": "RFID tags are not accepted twice in a row. Cannot be used together with <em>Pause when RFID tag is removed</em>.",
701703
"playLastRfidOnReboot": "Play last RFID tag on restart",
@@ -813,6 +815,15 @@
813815
"started": "Device is installing the firmware and will restart – the page will reload shortly…",
814816
"error": "Update failed (see console/log)."
815817
}
818+
},
819+
"nav": {
820+
"command": "Command",
821+
"sync": "File sync",
822+
"rfidsync": "RFID sync",
823+
"nvs": "NVS",
824+
"eqrules": "EQ rules",
825+
"backup": "Backup",
826+
"fwupdate": "Firmware"
816827
}
817828
},
818829
"help": {

‎html/locales/fr.json‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,8 @@
685685
"restartFreshHrsExp": "Si un livre audio n'a pas été lu depuis plus que le nombre d'heures indiqué, il recommence depuis le début lors de la prochaine pose de la carte, au lieu de reprendre à la position enregistrée. Pratique pour les enfants qui, après une journée de pause, ne se souviennent plus où ils en étaient. 0 = désactivé (toujours reprendre). Par défaut : 24. Nécessite une horloge valide (NTP/RTC). Fonction du fork.",
686686
"pauseIfRfidRemoved": "Mettre en pause si la balise RFID est retirée",
687687
"pauseIfRfidRemovedExp": "Mettre en pause la lecture si la balise RFID est retirée. Plus d'informations <a href='https://forum.espuino.de/t/welche-optionen-beim-kompilieren-gibt-es/120' target='_blank'>ici</a> sous <code>PAUSE_IF_RFID_REMOVED</code>.",
688+
"stopIfRfidRemoved": "Arrêter au lieu de mettre en pause",
689+
"stopIfRfidRemovedExp": "Si activé (uniquement avec « Mettre en pause si la balise RFID est retirée ») : retirer la balise arrête complètement la lecture au lieu de la mettre en pause. Reposer la balise redémarre depuis le début.",
688690
"dontAcceptRfidTwice": "Ne pas accepter la balise RFID deux fois",
689691
"dontAcceptRfidTwiceExp": "Ne pas accepter la même balise RFID deux fois. Ne peut pas être utilisé avec <em>Mettre en pause si la balise RFID est retirée</em>.",
690692
"playLastRfidOnReboot": "Lire la dernière balise RFID au redémarrage",
@@ -802,6 +804,15 @@
802804
"started": "L'appareil installe le micrologiciel et va redémarrer – la page se rechargera sous peu…",
803805
"error": "Échec de la mise à jour (voir la console/le journal)."
804806
}
807+
},
808+
"nav": {
809+
"command": "Commande",
810+
"sync": "Sync fichiers",
811+
"rfidsync": "Sync RFID",
812+
"nvs": "NVS",
813+
"eqrules": "Règles EQ",
814+
"backup": "Sauvegarde",
815+
"fwupdate": "Firmware"
805816
}
806817
},
807818
"help": {

0 commit comments

Comments
 (0)