diff --git a/frontend/components/block-state-tracker/slow-motion-control.tsx b/frontend/components/block-state-tracker/slow-motion-control.tsx index 10a5927..b2a1384 100644 --- a/frontend/components/block-state-tracker/slow-motion-control.tsx +++ b/frontend/components/block-state-tracker/slow-motion-control.tsx @@ -1,6 +1,12 @@ 'use client' import { Timer, X } from 'lucide-react' +import type { ReactElement } from 'react' +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip' interface SlowMotionControlProps { isActive: boolean @@ -9,6 +15,23 @@ interface SlowMotionControlProps { onStop: () => void } +function SlowModeTooltipWrapper({ children }: { children: ReactElement }) { + return ( + + {children} + +

+ Slow mode doesn't affect the chain. It only slows UI updates, so the + display may lag behind the chain tip. +

+
+
+ ) +} + export function SlowMotionControl({ isActive, remainingSeconds, @@ -17,33 +40,36 @@ export function SlowMotionControl({ }: SlowMotionControlProps) { if (isActive) { return ( -
-
- - Slow Mode - ({remainingSeconds}s) + +
+
+ + Slow Mode + ({remainingSeconds}s) +
+
- -
+ ) } return ( - + + + ) } diff --git a/frontend/constants/block-state.ts b/frontend/constants/block-state.ts index 2b29afd..7538b24 100644 --- a/frontend/constants/block-state.ts +++ b/frontend/constants/block-state.ts @@ -1,7 +1,8 @@ import type { BlockState } from '@/types/block' export const SLOW_MOTION_DURATION_SECONDS = 30 -export const SLOW_MOTION_EVENT_INTERVAL_MS = 75 +export const SLOW_MOTION_CHUNK_INTERVAL_MS = 2000 +export const SLOW_MOTION_CHUNK_NEW_BLOCKS = 2 /** * Color tokens for block states (CSS variables for consistency). diff --git a/frontend/hooks/use-blockchain-slow-motion.ts b/frontend/hooks/use-blockchain-slow-motion.ts index 2631ec8..32708e6 100644 --- a/frontend/hooks/use-blockchain-slow-motion.ts +++ b/frontend/hooks/use-blockchain-slow-motion.ts @@ -2,8 +2,9 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { + SLOW_MOTION_CHUNK_INTERVAL_MS, + SLOW_MOTION_CHUNK_NEW_BLOCKS, SLOW_MOTION_DURATION_SECONDS, - SLOW_MOTION_EVENT_INTERVAL_MS, } from '@/constants/block-state' import type { SerializableEventData } from '@/types/events' @@ -93,14 +94,30 @@ export function useBlockchainSlowMotion({ eventQueueRef.current = [] // Start the slow processing interval + // Process queued events in chunks so the UI advances in visible steps. + // A "chunk" aims to include ~N new events (BlockStart events) plus any subsequent state transitions for those blocks that are already queued. processIntervalRef.current = setInterval(() => { - if (eventQueueRef.current.length > 0) { - const event = eventQueueRef.current.shift() - if (event) { - onProcessEventRef.current(event) + if (eventQueueRef.current.length === 0) return + + const chunk: SerializableEventData[] = [] + let newBlocksInChunk = 0 + + while ( + eventQueueRef.current.length > 0 && + newBlocksInChunk < SLOW_MOTION_CHUNK_NEW_BLOCKS + ) { + const next = eventQueueRef.current.shift() + if (!next) break + chunk.push(next) + if (next.payload.type === 'BlockStart') { + newBlocksInChunk += 1 } } - }, SLOW_MOTION_EVENT_INTERVAL_MS) + + if (chunk.length > 0) { + onFlushEventsRef.current(chunk) + } + }, SLOW_MOTION_CHUNK_INTERVAL_MS) // Start the countdown timer using timestamp-based calculation countdownIntervalRef.current = setInterval(updateRemainingSeconds, 1000)