Ajuste le mode Time et met en valeur les chronos

This commit is contained in:
2026-04-12 19:51:41 +02:00
parent a6e007762d
commit 7649ba2fb9
6 changed files with 199 additions and 56 deletions

142
app.js
View File

@@ -13,17 +13,17 @@ const PRESETS = {
fast: { fast: {
label: "FAST", label: "FAST",
quota: 6, quota: 6,
description: "6 coups par joueur et par partie.", description: "6 coups par joueur.",
}, },
freeze: { freeze: {
label: "FREEZE", label: "FREEZE",
quota: 8, quota: 8,
description: "8 coups par joueur et par partie.", description: "8 coups par joueur.",
}, },
masters: { masters: {
label: "MASTERS", label: "MASTERS",
quota: 10, quota: 10,
description: "10 coups par joueur et par partie.", description: "10 coups par joueur.",
}, },
}; };
@@ -114,6 +114,8 @@ function initSetupPage() {
const summary = document.querySelector("#setupSummary"); const summary = document.querySelector("#setupSummary");
const loadDemoButton = document.querySelector("#loadDemoButton"); const loadDemoButton = document.querySelector("#loadDemoButton");
const resumeCard = document.querySelector("#resumeCard"); const resumeCard = document.querySelector("#resumeCard");
const moveSecondsField = document.querySelector("#moveSecondsField");
const moveSecondsInput = form?.querySelector('[name="moveSeconds"]');
if (!form || !summary || !loadDemoButton || !resumeCard) { if (!form || !summary || !loadDemoButton || !resumeCard) {
return; return;
@@ -125,17 +127,34 @@ function initSetupPage() {
const quota = PRESETS[preset].quota; const quota = PRESETS[preset].quota;
const blockDurationMs = getDurationInputMs(form, "blockSeconds", DEFAULT_BLOCK_DURATION_MS); const blockDurationMs = getDurationInputMs(form, "blockSeconds", DEFAULT_BLOCK_DURATION_MS);
const moveLimitMs = getDurationInputMs(form, "moveSeconds", DEFAULT_MOVE_LIMIT_MS); const moveLimitMs = getDurationInputMs(form, "moveSeconds", DEFAULT_MOVE_LIMIT_MS);
const moveLimitActive = usesMoveLimit(mode);
const timeImpact = const timeImpact =
mode === "time" mode === "time"
? "Chronos cumules de 10 minutes, ajustes apres chaque phase cube avec plafond de 120 s pris en compte." ? "Chronos cumules de 10 minutes par joueur, ajustes apres chaque phase cube avec plafond de 120 s pris en compte. Aucun temps par coup en mode Time."
: "Le gagnant du cube commence la partie suivante, avec double coup V2 possible."; : "Le gagnant du cube commence la partie suivante, avec double coup V2 possible.";
const timingText = moveLimitActive
? `Temps configures : partie ${formatClock(blockDurationMs)}, coup ${formatClock(moveLimitMs)}.`
: `Temps configure : Block ${formatClock(blockDurationMs)}.`;
const quotaText = moveLimitActive
? `Quota actif : ${quota} coups par joueur.`
: `Quota actif : ${quota} coups par joueur et par Block.`;
if (moveSecondsField instanceof HTMLElement) {
moveSecondsField.hidden = !moveLimitActive;
}
if (moveSecondsInput instanceof HTMLInputElement) {
moveSecondsInput.disabled = !moveLimitActive;
}
document.body.classList.toggle("time-setup-mode", !moveLimitActive);
summary.innerHTML = ` summary.innerHTML = `
<strong>${MODES[mode].label}</strong> <strong>${MODES[mode].label}</strong>
<span>${PRESETS[preset].description}</span> <span>${PRESETS[preset].description}</span>
<span>Temps configures : partie ${formatClock(blockDurationMs)}, coup ${formatClock(moveLimitMs)}.</span> <span>${timingText}</span>
<span>${timeImpact}</span> <span>${timeImpact}</span>
<span>Quota actif : ${quota} coups par joueur.</span> <span>${quotaText}</span>
`; `;
}; };
@@ -228,7 +247,9 @@ function initChronoPage() {
title: document.querySelector("#chronoTitle"), title: document.querySelector("#chronoTitle"),
subtitle: document.querySelector("#chronoSubtitle"), subtitle: document.querySelector("#chronoSubtitle"),
blockTimer: document.querySelector("#blockTimer"), blockTimer: document.querySelector("#blockTimer"),
blockTimerLabel: document.querySelector("#blockTimerLabel"),
moveTimer: document.querySelector("#moveTimer"), moveTimer: document.querySelector("#moveTimer"),
moveTimerCard: document.querySelector("#moveTimerCard"),
centerLabel: document.querySelector("#chronoCenterLabel"), centerLabel: document.querySelector("#chronoCenterLabel"),
centerValue: document.querySelector("#chronoCenterValue"), centerValue: document.querySelector("#chronoCenterValue"),
spineLabel: document.querySelector("#spineLabel"), spineLabel: document.querySelector("#spineLabel"),
@@ -299,7 +320,7 @@ function initChronoPage() {
return; return;
} }
requestBlockClosure(match, "Cloture manuelle de la partie demandee par l'arbitre."); requestBlockClosure(match, `Cloture manuelle ${getBlockGenitivePhrase(match)} demandee par l'arbitre.`);
dirty = true; dirty = true;
if (!match.result && match.phase === "cube") { if (!match.result && match.phase === "cube") {
goToCubePage(); goToCubePage();
@@ -310,7 +331,7 @@ function initChronoPage() {
refs.arbiterTimeoutButton?.addEventListener("click", () => { refs.arbiterTimeoutButton?.addEventListener("click", () => {
syncRunningState(match); syncRunningState(match);
if (match.result || match.phase !== "block") { if (match.result || match.phase !== "block" || !usesMoveLimit(match)) {
return; return;
} }
@@ -326,7 +347,9 @@ function initChronoPage() {
} }
match.currentTurn = opponentOf(match.currentTurn); match.currentTurn = opponentOf(match.currentTurn);
if (usesMoveLimit(match)) {
match.moveRemainingMs = getMoveLimitMs(match); match.moveRemainingMs = getMoveLimitMs(match);
}
logEvent(match, "Trait corrige manuellement par l'arbitre."); logEvent(match, "Trait corrige manuellement par l'arbitre.");
dirty = true; dirty = true;
render(); render();
@@ -416,17 +439,20 @@ function initChronoPage() {
const clock = isWhite ? refs.whiteClock : refs.blackClock; const clock = isWhite ? refs.whiteClock : refs.blackClock;
const hint = isWhite ? refs.whiteHint : refs.blackHint; const hint = isWhite ? refs.whiteHint : refs.blackHint;
const zone = isWhite ? refs.whiteZone : refs.blackZone; const zone = isWhite ? refs.whiteZone : refs.blackZone;
const actorName = playerName(match, color);
const active = match.currentTurn === color; const active = match.currentTurn === color;
const unitLabel = getBlockLabel(match);
const unitPhrase = getBlockPhrase(match);
name.textContent = actorName; name.textContent = playerName(match, color);
moves.textContent = `${match.moves[color]} / ${match.quota}`; moves.textContent = `${match.moves[color]} / ${match.quota}`;
clock.textContent = match.clocks clock.textContent = match.clocks ? formatSignedClock(match.clocks[color]) : `Dernier cube ${renderLastCube(match, color)}`;
? `Chrono ${formatSignedClock(match.clocks[color])}`
: `Dernier cube ${renderLastCube(match, color)}`;
button.classList.toggle("active-turn", active && !match.result); button.classList.toggle("active-turn", active && !match.result);
zone.classList.toggle("active-zone", active && !match.result); zone.classList.toggle("active-zone", active && !match.result);
zone.classList.toggle("has-player-clock", Boolean(match.clocks));
clock.classList.toggle("player-clock", Boolean(match.clocks));
clock.classList.toggle("negative-clock", Boolean(match.clocks) && match.clocks[color] < 0);
clock.classList.toggle("active-clock", Boolean(match.clocks) && active && !match.result);
if (match.result) { if (match.result) {
button.textContent = resultText(match); button.textContent = resultText(match);
@@ -436,10 +462,10 @@ function initChronoPage() {
} }
if (!match.running) { if (!match.running) {
button.textContent = "Partie en pause"; button.textContent = `${unitLabel} en pause`;
button.disabled = true; button.disabled = true;
hint.textContent = active hint.textContent = active
? "La partie n'a pas encore demarre ou a ete mise en pause." ? `${unitPhrase} n'a pas encore demarre ou a ete mis en pause.`
: `${playerName(match, match.currentTurn)} reprendra au demarrage.`; : `${playerName(match, match.currentTurn)} reprendra au demarrage.`;
return; return;
} }
@@ -480,11 +506,21 @@ function initChronoPage() {
return; return;
} }
const timeMode = isTimeMode(match);
const blockHeading = formatBlockHeading(match, match.blockNumber);
document.body.classList.toggle("time-mode", timeMode);
refs.title.textContent = match.config.matchLabel; refs.title.textContent = match.config.matchLabel;
refs.subtitle.textContent = `Partie ${match.blockNumber} - ${MODES[match.config.mode].label} - ${renderModeContext(match)}`; refs.subtitle.textContent = `${blockHeading} - ${MODES[match.config.mode].label} - ${renderModeContext(match)}`;
refs.blockTimerLabel.textContent = timeMode ? "Temps Block" : "Temps partie";
refs.blockTimer.textContent = formatClock(match.blockRemainingMs); refs.blockTimer.textContent = formatClock(match.blockRemainingMs);
refs.moveTimer.textContent = formatClock(match.moveRemainingMs); refs.moveTimer.textContent = usesMoveLimit(match) ? formatClock(match.moveRemainingMs) : "--:--";
refs.moveTimerCard.hidden = timeMode;
refs.arbiterTimeoutButton.hidden = timeMode;
refs.arbiterTimeoutButton.disabled = timeMode;
if (usesMoveLimit(match)) {
refs.arbiterTimeoutButton.textContent = `Depassement ${formatClock(getMoveLimitMs(match))}`; refs.arbiterTimeoutButton.textContent = `Depassement ${formatClock(getMoveLimitMs(match))}`;
}
if (match.result) { if (match.result) {
refs.centerLabel.textContent = "Resultat"; refs.centerLabel.textContent = "Resultat";
@@ -498,20 +534,20 @@ function initChronoPage() {
refs.centerLabel.textContent = "Trait"; refs.centerLabel.textContent = "Trait";
refs.centerValue.textContent = playerName(match, match.currentTurn); refs.centerValue.textContent = playerName(match, match.currentTurn);
refs.spineLabel.textContent = "Chrono en cours"; refs.spineLabel.textContent = "Chrono en cours";
refs.spineHeadline.textContent = `Partie ${match.blockNumber} active`; refs.spineHeadline.textContent = `${blockHeading} actif`;
refs.spineText.textContent = refs.spineText.textContent =
"Chaque joueur tape sur sa grande zone quand son coup est termine. La page cube s'ouvrira automatiquement a la fin de la phase chess."; "Chaque joueur tape sur sa grande zone quand son coup est termine. La page cube s'ouvrira automatiquement a la fin de la phase chess.";
refs.primaryButton.textContent = "Pause arbitre"; refs.primaryButton.textContent = "Pause arbitre";
refs.arbiterStatus.textContent = `Partie en cours. Joueur au trait : ${playerName(match, match.currentTurn)}.`; refs.arbiterStatus.textContent = `${blockHeading} en cours. Joueur au trait : ${playerName(match, match.currentTurn)}.`;
} else { } else {
refs.centerLabel.textContent = "Trait"; refs.centerLabel.textContent = "Trait";
refs.centerValue.textContent = playerName(match, match.currentTurn); refs.centerValue.textContent = playerName(match, match.currentTurn);
refs.spineLabel.textContent = "Pret"; refs.spineLabel.textContent = timeMode ? "Etat du Block" : "Pret";
refs.spineHeadline.textContent = `Partie ${match.blockNumber}`; refs.spineHeadline.textContent = blockHeading;
refs.spineText.textContent = refs.spineText.textContent =
"Demarrez la partie, puis laissez uniquement les deux grandes zones aux joueurs. La page cube prendra automatiquement le relais."; "Demarrez la partie, puis laissez uniquement les deux grandes zones aux joueurs. La page cube prendra automatiquement le relais.";
refs.primaryButton.textContent = "Demarrer la partie"; refs.primaryButton.textContent = timeMode ? "Demarrer le Block" : "Demarrer la partie";
refs.arbiterStatus.textContent = `Partie prete. ${playerName(match, match.currentTurn)} commencera.`; refs.arbiterStatus.textContent = `${blockHeading} pret. ${playerName(match, match.currentTurn)} commencera.`;
} }
refs.arbiterCloseBlockButton.textContent = "Passer au cube"; refs.arbiterCloseBlockButton.textContent = "Passer au cube";
@@ -555,6 +591,7 @@ function initCubePage() {
const refs = { const refs = {
title: document.querySelector("#cubeTitle"), title: document.querySelector("#cubeTitle"),
subtitle: document.querySelector("#cubeSubtitle"), subtitle: document.querySelector("#cubeSubtitle"),
blockLabelText: document.querySelector("#cubeBlockLabelText"),
blockLabel: document.querySelector("#cubeBlockLabel"), blockLabel: document.querySelector("#cubeBlockLabel"),
elapsed: document.querySelector("#cubeElapsed"), elapsed: document.querySelector("#cubeElapsed"),
centerLabel: document.querySelector("#cubeCenterLabel"), centerLabel: document.querySelector("#cubeCenterLabel"),
@@ -892,8 +929,11 @@ function initCubePage() {
} }
function render() { function render() {
const blockHeading = formatBlockHeading(match, match.blockNumber);
refs.title.textContent = match.cube.number ? `Cube n${match.cube.number}` : "Phase cube"; refs.title.textContent = match.cube.number ? `Cube n${match.cube.number}` : "Phase cube";
refs.subtitle.textContent = `Partie ${match.blockNumber} - ${MODES[match.config.mode].label} - ${renderModeContext(match)}`; refs.subtitle.textContent = `${blockHeading} - ${MODES[match.config.mode].label} - ${renderModeContext(match)}`;
refs.blockLabelText.textContent = getBlockLabel(match);
refs.blockLabel.textContent = `${match.blockNumber}`; refs.blockLabel.textContent = `${match.blockNumber}`;
refs.elapsed.textContent = renderCubeElapsed(match); refs.elapsed.textContent = renderCubeElapsed(match);
@@ -923,7 +963,9 @@ function initCubePage() {
refs.centerValue.textContent = "Phase cube complete"; refs.centerValue.textContent = "Phase cube complete";
refs.spineLabel.textContent = "Suite"; refs.spineLabel.textContent = "Suite";
refs.spineHeadline.textContent = "Ouvrir la page chrono"; refs.spineHeadline.textContent = "Ouvrir la page chrono";
refs.spineText.textContent = "Appliquer le resultat du cube pour preparer la partie suivante."; refs.spineText.textContent = isTimeMode(match)
? "Appliquer le resultat du cube pour preparer le Block suivant."
: "Appliquer le resultat du cube pour preparer la partie suivante.";
refs.primaryButton.textContent = "Appliquer et ouvrir la page chrono"; refs.primaryButton.textContent = "Appliquer et ouvrir la page chrono";
refs.helpStatus.textContent = refs.spineText.textContent; refs.helpStatus.textContent = refs.spineText.textContent;
} else if (match.cube.running) { } else if (match.cube.running) {
@@ -1033,9 +1075,11 @@ function createMatch(config) {
logEvent( logEvent(
newMatch, newMatch,
`Match cree en mode ${MODES[config.mode].label}, cadence ${PRESETS[config.preset].label}, partie ${formatClock(config.blockDurationMs)} et coup ${formatClock(config.moveLimitMs)}.`, usesMoveLimit(config.mode)
? `Match cree en mode ${MODES[config.mode].label}, cadence ${PRESETS[config.preset].label}, partie ${formatClock(config.blockDurationMs)} et coup ${formatClock(config.moveLimitMs)}.`
: `Match cree en mode ${MODES[config.mode].label}, cadence ${PRESETS[config.preset].label}, Block ${formatClock(config.blockDurationMs)} sans temps par coup.`,
); );
logEvent(newMatch, "Les Blancs commencent la partie 1."); logEvent(newMatch, `Les Blancs commencent ${formatBlockHeading(config, 1)}.`);
return newMatch; return newMatch;
} }
@@ -1163,7 +1207,9 @@ function syncRunningState(storedMatch) {
storedMatch.lastTickAt = now; storedMatch.lastTickAt = now;
storedMatch.blockRemainingMs = Math.max(0, storedMatch.blockRemainingMs - delta); storedMatch.blockRemainingMs = Math.max(0, storedMatch.blockRemainingMs - delta);
if (usesMoveLimit(storedMatch)) {
storedMatch.moveRemainingMs = Math.max(0, storedMatch.moveRemainingMs - delta); storedMatch.moveRemainingMs = Math.max(0, storedMatch.moveRemainingMs - delta);
}
if (storedMatch.clocks) { if (storedMatch.clocks) {
storedMatch.clocks[storedMatch.currentTurn] -= delta; storedMatch.clocks[storedMatch.currentTurn] -= delta;
@@ -1172,9 +1218,9 @@ function syncRunningState(storedMatch) {
if (storedMatch.blockRemainingMs === 0) { if (storedMatch.blockRemainingMs === 0) {
requestBlockClosure( requestBlockClosure(
storedMatch, storedMatch,
`Le temps de partie ${formatClock(getBlockDurationMs(storedMatch))} est ecoule.`, `Le temps ${getBlockGenitivePhrase(storedMatch)} ${formatClock(getBlockDurationMs(storedMatch))} est ecoule.`,
); );
} else if (storedMatch.moveRemainingMs === 0) { } else if (usesMoveLimit(storedMatch) && storedMatch.moveRemainingMs === 0) {
registerMoveTimeout(storedMatch, true); registerMoveTimeout(storedMatch, true);
} }
@@ -1193,8 +1239,8 @@ function startBlock(storedMatch) {
logEvent( logEvent(
storedMatch, storedMatch,
storedMatch.blockNumber === 1 && storedMatch.moves.white === 0 && storedMatch.moves.black === 0 storedMatch.blockNumber === 1 && storedMatch.moves.white === 0 && storedMatch.moves.black === 0
? "Partie 1 demarre." ? `${formatBlockHeading(storedMatch, 1)} demarre.`
: `Partie ${storedMatch.blockNumber} relance.`, : `${formatBlockHeading(storedMatch, storedMatch.blockNumber)} relance.`,
); );
} }
@@ -1205,7 +1251,7 @@ function pauseBlock(storedMatch) {
storedMatch.running = false; storedMatch.running = false;
storedMatch.lastTickAt = null; storedMatch.lastTickAt = null;
logEvent(storedMatch, `Partie ${storedMatch.blockNumber} mise en pause.`); logEvent(storedMatch, `${formatBlockHeading(storedMatch, storedMatch.blockNumber)} passe en pause.`);
} }
function requestBlockClosure(storedMatch, reason) { function requestBlockClosure(storedMatch, reason) {
@@ -1388,7 +1434,7 @@ function prepareNextTimeBlock(storedMatch) {
storedMatch.cube.number = null; storedMatch.cube.number = null;
logEvent( logEvent(
storedMatch, storedMatch,
`Partie ${storedMatch.blockNumber} prete. Le trait est conserve : ${playerName( `${formatBlockHeading(storedMatch, storedMatch.blockNumber)} pret. Le trait est conserve : ${playerName(
storedMatch, storedMatch,
storedMatch.currentTurn, storedMatch.currentTurn,
)} reprend.`, )} reprend.`,
@@ -1489,7 +1535,7 @@ function registerFreeDoubleMove(storedMatch) {
} }
function registerMoveTimeout(storedMatch, automatic) { function registerMoveTimeout(storedMatch, automatic) {
if (!storedMatch || storedMatch.phase !== "block") { if (!storedMatch || storedMatch.phase !== "block" || !usesMoveLimit(storedMatch)) {
return; return;
} }
@@ -1718,6 +1764,34 @@ function getDurationInputMs(form, name, fallbackMs) {
return seconds * 1000; return seconds * 1000;
} }
function isTimeMode(matchOrConfig) {
const mode =
typeof matchOrConfig === "string"
? matchOrConfig
: matchOrConfig?.config?.mode ?? matchOrConfig?.mode;
return mode === "time";
}
function usesMoveLimit(matchOrConfig) {
return !isTimeMode(matchOrConfig);
}
function getBlockLabel(matchOrConfig) {
return isTimeMode(matchOrConfig) ? "Block" : "Partie";
}
function getBlockPhrase(matchOrConfig) {
return isTimeMode(matchOrConfig) ? "Le Block" : "La partie";
}
function getBlockGenitivePhrase(matchOrConfig) {
return isTimeMode(matchOrConfig) ? "du Block" : "de la partie";
}
function formatBlockHeading(matchOrConfig, blockNumber) {
return `${getBlockLabel(matchOrConfig)} ${blockNumber}`;
}
function playerName(storedMatch, color) { function playerName(storedMatch, color) {
return color === "white" ? storedMatch.config.whiteName : storedMatch.config.blackName; return color === "white" ? storedMatch.config.whiteName : storedMatch.config.blackName;
} }

View File

@@ -46,7 +46,7 @@
<li>Configurer la rencontre</li> <li>Configurer la rencontre</li>
<li>Passer à la page chrono</li> <li>Passer à la page chrono</li>
<li>Basculer automatiquement sur la page cube</li> <li>Basculer automatiquement sur la page cube</li>
<li>Revenir sur la page chrono pour la partie suivante</li> <li>Revenir sur la page chrono pour le Block suivant</li>
</ol> </ol>
</div> </div>
</aside> </aside>
@@ -90,7 +90,7 @@
<input type="radio" name="mode" value="time" /> <input type="radio" name="mode" value="time" />
<strong>ChessCubing Time</strong> <strong>ChessCubing Time</strong>
<span> <span>
Même structure de parties, avec chronos cumulés et alternance Même structure de Blocks, avec chronos cumulés et alternance
bloc - / bloc +. bloc - / bloc +.
</span> </span>
</label> </label>
@@ -98,7 +98,7 @@
</fieldset> </fieldset>
<fieldset class="field span-2"> <fieldset class="field span-2">
<legend>Cadence de la partie</legend> <legend>Cadence du match</legend>
<div class="option-grid preset-grid"> <div class="option-grid preset-grid">
<label class="option-card"> <label class="option-card">
<input type="radio" name="preset" value="fast" checked /> <input type="radio" name="preset" value="fast" checked />
@@ -122,7 +122,7 @@
<legend>Temps personnalisés</legend> <legend>Temps personnalisés</legend>
<div class="timing-grid"> <div class="timing-grid">
<label class="field"> <label class="field">
<span>Temps partie (secondes)</span> <span>Temps du Block (secondes)</span>
<input <input
name="blockSeconds" name="blockSeconds"
type="number" type="number"
@@ -132,7 +132,7 @@
value="180" value="180"
/> />
</label> </label>
<label class="field"> <label class="field" id="moveSecondsField">
<span>Temps coup (secondes)</span> <span>Temps coup (secondes)</span>
<input <input
name="moveSeconds" name="moveSeconds"
@@ -218,7 +218,7 @@
<p> <p>
Chaque joueur dispose d'une grande zone tactile pour signaler la Chaque joueur dispose d'une grande zone tactile pour signaler la
fin de son coup, puis l'app ouvre automatiquement la phase cube fin de son coup, puis l'app ouvre automatiquement la phase cube
quand la partie d'échecs est terminée. quand le Block d'échecs est terminé.
</p> </p>
</article> </article>
<article class="rule-card"> <article class="rule-card">

View File

@@ -29,7 +29,7 @@
</a> </a>
<div class="phase-title"> <div class="phase-title">
<p class="eyebrow">Phase chrono</p> <p class="eyebrow">Phase chrono</p>
<h1 id="chronoTitle">Partie 1</h1> <h1 id="chronoTitle">Block 1</h1>
<p id="chronoSubtitle" class="phase-subtitle"></p> <p id="chronoSubtitle" class="phase-subtitle"></p>
</div> </div>
<button class="button ghost small utility-button" id="openArbiterButton" type="button"> <button class="button ghost small utility-button" id="openArbiterButton" type="button">
@@ -39,10 +39,10 @@
<section class="status-strip"> <section class="status-strip">
<article class="status-card"> <article class="status-card">
<span>Temps partie</span> <span id="blockTimerLabel">Temps Block</span>
<strong id="blockTimer">03:00</strong> <strong id="blockTimer">03:00</strong>
</article> </article>
<article class="status-card"> <article class="status-card" id="moveTimerCard">
<span>Temps coup</span> <span>Temps coup</span>
<strong id="moveTimer">00:20</strong> <strong id="moveTimer">00:20</strong>
</article> </article>
@@ -62,7 +62,7 @@
</div> </div>
<div class="zone-stats"> <div class="zone-stats">
<strong id="blackMovesChrono">0 / 6</strong> <strong id="blackMovesChrono">0 / 6</strong>
<span id="blackClockChrono"></span> <span class="player-clock" id="blackClockChrono"></span>
</div> </div>
</div> </div>
@@ -76,13 +76,13 @@
<article class="phase-spine"> <article class="phase-spine">
<div class="spine-card"> <div class="spine-card">
<p class="micro-label" id="spineLabel">État de la partie</p> <p class="micro-label" id="spineLabel">État du Block</p>
<strong id="spineHeadline">Prêt à démarrer</strong> <strong id="spineHeadline">Prêt à démarrer</strong>
<p id="spineText"></p> <p id="spineText"></p>
</div> </div>
<button class="button primary spine-button" id="primaryChronoButton" type="button"> <button class="button primary spine-button" id="primaryChronoButton" type="button">
Démarrer la partie Démarrer le Block
</button> </button>
</article> </article>
@@ -95,7 +95,7 @@
</div> </div>
<div class="zone-stats"> <div class="zone-stats">
<strong id="whiteMovesChrono">0 / 6</strong> <strong id="whiteMovesChrono">0 / 6</strong>
<span id="whiteClockChrono"></span> <span class="player-clock" id="whiteClockChrono"></span>
</div> </div>
</div> </div>

View File

@@ -39,7 +39,7 @@
<section class="status-strip"> <section class="status-strip">
<article class="status-card"> <article class="status-card">
<span>Partie</span> <span id="cubeBlockLabelText">Block</span>
<strong id="cubeBlockLabel">1</strong> <strong id="cubeBlockLabel">1</strong>
</article> </article>
<article class="status-card"> <article class="status-card">

View File

@@ -264,16 +264,16 @@
<div class="format-badges"> <div class="format-badges">
<span>Temps initial : 10 min / joueur</span> <span>Temps initial : 10 min / joueur</span>
<span>Partie : 180 s</span> <span>Block : 180 s</span>
<span>Cap cube pris en compte : 120 s</span> <span>Cap cube pris en compte : 120 s</span>
</div> </div>
<div class="format-section"> <div class="format-section">
<h4>Structure temporelle</h4> <h4>Structure temporelle</h4>
<ul class="rule-list compact"> <ul class="rule-list compact">
<li>La structure des parties est identique à celle du Twice.</li> <li>La structure des Blocks est identique à celle du Twice.</li>
<li>Les quotas de coups restent les mêmes : 6, 8 ou 10.</li> <li>Les quotas de coups restent les mêmes : 6, 8 ou 10.</li>
<li>Chaque partie est suivie d'une phase cube obligatoire.</li> <li>Chaque Block est suivi d'une phase cube obligatoire.</li>
<li>Le trait est conservé après la phase cube.</li> <li>Le trait est conservé après la phase cube.</li>
<li>Aucun système de priorité ou de double coup n'existe.</li> <li>Aucun système de priorité ou de double coup n'existe.</li>
</ul> </ul>
@@ -281,7 +281,7 @@
<div class="split-callouts"> <div class="split-callouts">
<article class="split-card minus-card"> <article class="split-card minus-card">
<span class="micro-label">Partie impaire</span> <span class="micro-label">Block impair</span>
<strong>Bloc -</strong> <strong>Bloc -</strong>
<p> <p>
Le temps de résolution du cube est retiré du chrono du Le temps de résolution du cube est retiré du chrono du
@@ -289,7 +289,7 @@
</p> </p>
</article> </article>
<article class="split-card plus-card"> <article class="split-card plus-card">
<span class="micro-label">Partie paire</span> <span class="micro-label">Block pair</span>
<strong>Bloc +</strong> <strong>Bloc +</strong>
<p> <p>
Le temps de résolution du cube est ajouté au chrono adverse, Le temps de résolution du cube est ajouté au chrono adverse,

View File

@@ -2389,3 +2389,72 @@ body[data-page="chrono"] .spine-button {
font-size: 0.9rem; font-size: 0.9rem;
} }
} }
body.time-setup-mode .timing-grid {
grid-template-columns: minmax(0, 1fr);
}
body[data-page="chrono"].time-mode .status-strip {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
body[data-page="chrono"] .has-player-clock .zone-stats {
gap: 0.45rem;
}
body[data-page="chrono"] .has-player-clock .zone-stats strong {
color: var(--muted);
font-size: 0.92rem;
letter-spacing: 0.08em;
}
body[data-page="chrono"] .player-clock {
display: inline-flex;
align-items: center;
justify-content: center;
justify-self: end;
min-width: min(100%, 10ch);
padding: 0.55rem 0.8rem;
border: 1px solid var(--panel-border-strong);
border-radius: 999px;
background: rgba(255, 255, 255, 0.06);
color: var(--text);
font-size: clamp(1.3rem, 3.6vw, 2.35rem);
font-weight: 900;
letter-spacing: 0.06em;
line-height: 1;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.03);
}
body[data-page="chrono"] #whiteClockChrono.player-clock {
color: var(--white-seat);
background: rgba(255, 177, 58, 0.12);
border-color: rgba(255, 177, 58, 0.28);
}
body[data-page="chrono"] #blackClockChrono.player-clock {
color: var(--dark-seat);
background: rgba(23, 98, 255, 0.12);
border-color: rgba(23, 98, 255, 0.28);
}
body[data-page="chrono"] .player-clock.active-clock {
box-shadow:
0 0 0 1px rgba(255, 255, 255, 0.08),
0 0 24px rgba(255, 255, 255, 0.06);
transform: translateY(-1px);
}
body[data-page="chrono"] .player-clock.negative-clock {
color: #ffd6dc;
background: rgba(255, 100, 127, 0.16);
border-color: rgba(255, 100, 127, 0.34);
}
@media (max-width: 900px) {
body[data-page="chrono"] .player-clock {
min-width: 7.7ch;
padding: 0.45rem 0.68rem;
font-size: clamp(1.05rem, 5.8vw, 1.7rem);
}
}