scripts/codex-review.ps1 lets the Codex CLI review a state read-only in a clean git worktree without .env or var/, as in Domira: by default everything since the last release tag, or -Seit, -Commit, -Offen, -Alles. The final answer is also saved under reports/. Review rules for KantSites live in scripts/codex-review-prompt.md; README and AGENTS.md describe the workflow. video.js: when the browser refuses to start a loop (NotAllowedError), the first touch or key press starts it; a click on the pause button only counts as that button's own decision. ADR on videos updated. Luximmo seed: Instagram and Facebook profiles in the footer, icons instead of numbers in the process steps, services as subgrid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
54 lines
2.5 KiB
JavaScript
54 lines
2.5 KiB
JavaScript
/*
|
|
* Schleifenvideos: im Hintergrund einer Sektion oder im Video-Baustein.
|
|
*
|
|
* Abgespielt wird erst von hier aus, nicht mit autoplay im HTML. So lädt die Seite zuerst ihren
|
|
* Inhalt, das Video folgt danach (preload="none"), und es läuft nur, solange es zu sehen ist.
|
|
* Wer „Bewegung reduzieren“ eingestellt hat oder Daten sparen will, bekommt das Standbild und kann
|
|
* das Video selbst starten. Der Knopf gehört dazu: Bewegung, die länger als fünf Sekunden läuft,
|
|
* muss sich anhalten lassen (WCAG 2.2.2). Ohne Skript bleibt das Standbild.
|
|
*/
|
|
const reduced = matchMedia('(prefers-reduced-motion: reduce)');
|
|
const saving = navigator.connection?.saveData === true;
|
|
|
|
for (const video of document.querySelectorAll('[data-video-loop]')) {
|
|
const toggle = video.parentElement.querySelector(':scope > .video-toggle');
|
|
// Ein data-video-loop aus „Eigenes HTML“ hat keinen Knopf; ohne ihn bleibt es beim Standbild.
|
|
if (!toggle) continue;
|
|
let wanted = !reduced.matches && !saving;
|
|
let visible = false;
|
|
// Verweigert der Browser den Start, holt die erste Berührung oder Taste ihn nach. Der Knopf
|
|
// entscheidet selbst: Sein Klick würde das nachgeholte Abspielen sonst gleich wieder anhalten.
|
|
const retry = event => {
|
|
if (toggle.contains(event.target)) return;
|
|
stopWaiting();
|
|
wanted = true;
|
|
paint();
|
|
sync();
|
|
};
|
|
const waitForGesture = () => {
|
|
addEventListener('pointerup', retry);
|
|
addEventListener('keydown', retry);
|
|
};
|
|
const stopWaiting = () => {
|
|
removeEventListener('pointerup', retry);
|
|
removeEventListener('keydown', retry);
|
|
};
|
|
const paint = () => {
|
|
toggle.classList.toggle('is-paused', !wanted);
|
|
toggle.setAttribute('aria-label', wanted ? toggle.dataset.pause : toggle.dataset.play);
|
|
};
|
|
const sync = () => {
|
|
if (!wanted || !visible) { video.pause(); return; }
|
|
video.play().catch(error => {
|
|
// Ein Anhalten mitten im Starten ist kein Fehler. Verweigert der Browser das Abspielen,
|
|
// etwa im Stromsparmodus, zeigt der Knopf die Wahrheit, bis eine Eingabe den Start erlaubt.
|
|
if (error.name === 'NotAllowedError') { wanted = false; paint(); waitForGesture(); }
|
|
});
|
|
};
|
|
toggle.hidden = false;
|
|
toggle.addEventListener('click', () => { stopWaiting(); wanted = !wanted; paint(); sync(); });
|
|
reduced.addEventListener('change', () => { if (reduced.matches) { stopWaiting(); wanted = false; paint(); sync(); } });
|
|
new IntersectionObserver(([entry]) => { visible = entry.isIntersecting; sync(); }).observe(video);
|
|
paint();
|
|
}
|