feat(control): SUIVANT queued-load button
CI build oscope-of / build-check (push) Has been cancelled

Add a SUIVANT button to the matrix persist bar that arms the selected
preset to load at the next loop wrap (/matrix/loadnext), with an armed
pulse + queued-name label driven by the /matrix/queued feedback.

No jsdom unit test: the project's web tests cover pure logic modules, not
main.js DOM wiring (jsdom is not a dependency); the button is verified by
the live smoke. node --check + the existing 29-test suite pass.
This commit is contained in:
L'électron rare
2026-06-30 09:15:53 +02:00
parent 68fa3c4e3b
commit e0c4405044
3 changed files with 17 additions and 0 deletions
+3
View File
@@ -127,6 +127,9 @@ button.queued { animation: queued-blink 600ms ease-in-out infinite alternate; }
border: 1px solid #335; border-radius: 6px; padding: 7px 6px; font-size: 14px;
cursor: pointer; -webkit-appearance: none; appearance: none; }
.mat-action-btn { width: auto; padding: 8px 14px; font-size: 14px; flex-shrink: 0; }
.mat-action-btn.armed { background: #2a4d2a; color: #9f9; border-color: #4a4;
animation: matArmedPulse 1s ease-in-out infinite; }
@keyframes matArmedPulse { 0%,100% { opacity: 1; } 50% { opacity: .55; } }
.mat-refresh-btn { width: auto; padding: 8px 12px; font-size: 16px; flex-shrink: 0;
background: #1a2a1a; border-color: #2a4a2a; color: #4d9; }
.mvoice-label { position: sticky; left: 0; z-index: 2; background: #111; }
+1
View File
@@ -169,6 +169,7 @@
<button id="matrix-save" class="mat-action-btn">SAUVER</button>
<select id="matrix-list" class="matrix-list-select"></select>
<button id="matrix-load" class="mat-action-btn">CHARGER</button>
<button id="matrix-loadnext" class="mat-action-btn" title="Charge a la fin de la boucle">SUIVANT</button>
<button id="matrix-refresh" class="mat-refresh-btn" title="Rafraichir la liste">&#8635;</button>
</div>
<button id="matrix-loop-full" class="mat-action-btn mat-loop-full-btn">BOUCLE COMPLETE</button>
+13
View File
@@ -116,6 +116,19 @@ document.addEventListener("DOMContentLoaded", () => {
const n = document.getElementById("matrix-list").value;
if (n) send("/matrix/load", n);
});
// Queued load: arm the selected preset to swap at the next loop wrap.
const matLoadNext = document.getElementById("matrix-loadnext");
if (matLoadNext) matLoadNext.addEventListener("click", () => {
const n = document.getElementById("matrix-list").value;
if (n) send("/matrix/loadnext", n);
});
on("/matrix/queued", (args) => {
const name = (args && args[0] != null) ? String(args[0]) : "";
if (matLoadNext) {
matLoadNext.classList.toggle("armed", name.length > 0);
matLoadNext.textContent = name.length > 0 ? "SUIVANT → " + name : "SUIVANT";
}
});
const matRefresh = document.getElementById("matrix-refresh");
if (matRefresh) matRefresh.addEventListener("click", () => send("/matrix/list"));
});