Files
galaxy-game/ui/frontend/src/lib/battle-player/playback-controls.svelte
T
Ilia Denisov 4ad96b0ef7
Tests · UI / test (push) Successful in 2m11s
Tests · UI / test (pull_request) Successful in 2m7s
feat(ui): migrate all view bodies to design tokens (F1b)
Tokenize every remaining component <style> — calculator, order tab,
inspectors, tables, report sections, lobby, auth, mail, battle viewer,
toasts, map overlays. A scripted pass handled the unambiguous core
palette (text/bg/surface/border/accent/danger/muted), the rest were
mapped to the semantic/grey tokens by role.

Remaining colour literals are the documented exceptions only: the
battle-scene SVG data-visualisation palette (fixed dark, like the WebGL
map canvas), overlay scrims (modal / map-canvas), and directional or
deliberate drop shadows. The default theme stays dark until light
coherence is signed off across the views.

Updates ui/docs/design-system.md (migration status + exceptions).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 07:24:02 +02:00

156 lines
3.8 KiB
Svelte

<!--
PlaybackControls — rewind / step-back / play-pause / step-forward
plus a single cycling speed button (1x → 2x → 4x → 6x → 1x) and a
"log" toggle that the orchestrator uses to collapse the always-on
text protocol when the user wants more space for the scene. Owns no
state of its own; binds `playing`, `frameIndex`, `speed`, and
`logOpen` from the orchestrator. Disables step/rewind when there's
nowhere to go and step-forward when the timeline is at its end.
-->
<script lang="ts">
import { i18n } from "$lib/i18n/index.svelte";
export type PlaybackSpeed = 1 | 2 | 4 | 6;
const SPEED_CYCLE: PlaybackSpeed[] = [1, 2, 4, 6];
let {
playing = $bindable(),
frameIndex = $bindable(),
speed = $bindable(),
logOpen = $bindable(),
frameCount,
}: {
playing: boolean;
frameIndex: number;
speed: PlaybackSpeed;
logOpen: boolean;
frameCount: number;
} = $props();
function rewind() {
playing = false;
frameIndex = 0;
}
function stepBack() {
playing = false;
if (frameIndex > 0) frameIndex = frameIndex - 1;
}
function togglePlay() {
if (frameIndex >= frameCount - 1) {
frameIndex = 0;
}
playing = !playing;
}
function stepForward() {
playing = false;
if (frameIndex < frameCount - 1) frameIndex = frameIndex + 1;
}
function cycleSpeed() {
const idx = SPEED_CYCLE.indexOf(speed);
const next = SPEED_CYCLE[(idx + 1) % SPEED_CYCLE.length];
speed = next;
}
function toggleLog() {
logOpen = !logOpen;
}
const speedLabel = $derived(`${speed}x`);
</script>
<div class="controls" data-testid="battle-controls">
<button
type="button"
onclick={rewind}
disabled={frameIndex === 0}
aria-label={i18n.t("game.battle.controls.rewind")}
data-testid="battle-control-rewind"
></button>
<button
type="button"
onclick={stepBack}
disabled={frameIndex === 0}
aria-label={i18n.t("game.battle.controls.step_backward")}
data-testid="battle-control-step-back"
>◀︎◀︎</button>
<button
type="button"
onclick={togglePlay}
aria-label={playing
? i18n.t("game.battle.controls.pause")
: i18n.t("game.battle.controls.play")}
data-testid="battle-control-play"
data-playing={playing ? "true" : "false"}
>{playing ? "⏸" : "▶︎"}</button>
<button
type="button"
onclick={stepForward}
disabled={frameIndex >= frameCount - 1}
aria-label={i18n.t("game.battle.controls.step_forward")}
data-testid="battle-control-step-forward"
>▶︎▶︎</button>
<div class="spacer" aria-hidden="true"></div>
<button
type="button"
class="speed-btn"
onclick={cycleSpeed}
title={i18n.t("game.battle.controls.speed_label")}
aria-label={i18n.t("game.battle.controls.speed_label")}
data-testid="battle-control-speed"
data-speed={speed}
>{speedLabel}</button>
<button
type="button"
class="log-toggle"
class:active={logOpen}
onclick={toggleLog}
aria-pressed={logOpen}
aria-label={i18n.t("game.battle.controls.log_toggle")}
data-testid="battle-control-log-toggle"
>{i18n.t("game.battle.controls.log_toggle")} {logOpen ? "▲" : "▼"}</button>
</div>
<style>
.controls {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.5rem 0.75rem;
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: 4px;
}
.spacer {
flex: 1 1 auto;
}
button {
appearance: none;
background: var(--color-surface-raised);
color: var(--color-text);
border: 1px solid var(--color-border);
padding: 0.35rem 0.7rem;
border-radius: 3px;
cursor: pointer;
font-size: 0.9rem;
font-family: inherit;
min-width: 2.5rem;
}
button:hover:not(:disabled) {
background: var(--color-surface-hover);
}
button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.speed-btn {
min-width: 3rem;
font-variant-numeric: tabular-nums;
}
.log-toggle.active {
background: var(--color-surface-hover);
}
</style>