feat: v0.16.1 — ISS-007 smooth client-side sim clock with per-snapshot re-sync
CI / build + unit + e2e (pull_request) Failing after 6m42s

This commit is contained in:
2026-09-19 14:00:47 +00:00
parent 05d956f71b
commit 7d2f8f07c8
6 changed files with 169 additions and 2 deletions
+8 -1
View File
@@ -5,6 +5,7 @@ import type { PlaneState, StateSnapshot } from '@/lib/game/state';
import { PLANE_TYPES } from '@/lib/game/catalog';
import { formatCash, formatFuel, formatSimClock } from '@/lib/format';
import { TIME_SCALES, scaleToSlider, sliderToScale } from '@/lib/timeScale';
import { useSimClock } from '@/lib/useSimClock';
import type { HoverTarget } from './hoverTypes';
interface LeftPaneProps {
@@ -167,6 +168,12 @@ export default function LeftPane({ state, loading, error, onRefresh, onHover }:
const displayedScale = pendingScale ?? timeScale;
// ISS-007: the clock ticks client-side between polls, re-syncing to server truth on every
// snapshot. Fed with EFFECTIVE controls so a scale drag / pause click takes effect immediately,
// not when the confirming snapshot lands. Only the sim-clock paragraph uses this — deadline and
// penalty previews stay snapshot-based (deterministic against the server clock).
const simClockMs = useSimClock(state, { timeScale: displayedScale, paused });
return (
<aside data-testid="left-pane" className="pane left-pane">
<h2 className="pane-title">Assets</h2>
@@ -221,7 +228,7 @@ export default function LeftPane({ state, loading, error, onRefresh, onHover }:
<section className="pane-section">
<h3 className="pane-heading">Time</h3>
<p data-testid="sim-clock" className="sim-clock">
{formatSimClock(state.simState.clockMs)}
{simClockMs !== null && formatSimClock(simClockMs)}
{paused ? ' (paused)' : ''}
</p>
<div className="time-controls">