feat: v0.9.2 — power-of-two time scale: slider steps through the 1/2/4/8/16/32/64 ladder, POST /api/sim clamp widened to [1,64]
This commit is contained in:
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useRef, useState, type ChangeEvent } from 'reac
|
||||
import type { ContractState, PlaneState, StateSnapshot } from '@/lib/game/state';
|
||||
import { PLANE_TYPES } from '@/lib/game/catalog';
|
||||
import { formatCash, formatFuel, formatSimClock } from '@/lib/format';
|
||||
import { scaleToSlider, sliderToScale } from '@/lib/timeScale';
|
||||
import { TIME_SCALES, scaleToSlider, sliderToScale } from '@/lib/timeScale';
|
||||
|
||||
interface LeftPaneProps {
|
||||
state: StateSnapshot | null;
|
||||
@@ -14,9 +14,9 @@ interface LeftPaneProps {
|
||||
onRefresh: () => void;
|
||||
}
|
||||
|
||||
// Log-scale slider (v0.9.1): position t ∈ [0,1] → scale = round(60^t), so equal distances
|
||||
// are equal multiplicative steps. 60 discrete positions keep keyboard arrows usable (~7% per step).
|
||||
const SCALE_SLIDER_STEP = 1 / 59;
|
||||
// Power-of-two slider (v0.9.2): position t ∈ [0,1] → nearest step of the ladder
|
||||
// 1×/2×/4×/8×/16×/32×/64× — each of the 7 positions doubles the speed.
|
||||
const SCALE_SLIDER_STEP = 1 / (TIME_SCALES.length - 1); // one doubling per keyboard-arrow step
|
||||
const SCALE_COMMIT_DEBOUNCE_MS = 250;
|
||||
|
||||
function planeLocation(p: PlaneState, contracts: ContractState[]): string {
|
||||
@@ -34,7 +34,7 @@ export default function LeftPane({ state, loading, error, onRefresh }: LeftPaneP
|
||||
// controls without waiting for the next poll tick. On failure keep the stale UI — the
|
||||
// running poll retries on its own and surfaces the error state. A commit that lands while
|
||||
// another POST is in flight is held in a single slot and flushed right after, so the last
|
||||
// user intent always reaches the server (the server's [1,60] clamp stays as backstop).
|
||||
// user intent always reaches the server (the server's [1,64] clamp stays as backstop).
|
||||
const queuedRef = useRef<{ timeScale?: number; paused?: boolean } | null>(null);
|
||||
|
||||
const applySimControls = useCallback(
|
||||
@@ -73,7 +73,7 @@ export default function LeftPane({ state, loading, error, onRefresh }: LeftPaneP
|
||||
// Slider commit (v0.9.1): the displayed value follows the drag optimistically via
|
||||
// pendingScale; POST /api/sim fires after a 250 ms debounce, or immediately when the
|
||||
// user releases the slider (browsers fire a native 'change' on release). The server's
|
||||
// [1,60] clamp stays as the backstop.
|
||||
// [1,64] clamp stays as the backstop.
|
||||
const [pendingScale, setPendingScaleState] = useState<number | null>(null);
|
||||
const pendingRef = useRef<number | null>(null);
|
||||
const commitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
Reference in New Issue
Block a user