@bitcobblers/wod-wiki-lang 0.11.43 → 0.35.2116

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { $ as ScriptRuntime, as as MemoryType, q as IRuntimeBlock, au as MemoryValueOf, ad as DisplayState, av as RoundState, a2 as TimerState, Y as IScriptRuntime, F as TypedMemoryReference, Q as ButtonConfig, K as StackSnapshot } from './browserRuntimeNow-je1Vb5bU.js';
2
- export { aC as createBrowserRuntimeNow, aH as getRuntimeNowMs, aK as runtimeNow } from './browserRuntimeNow-je1Vb5bU.js';
1
+ import { a0 as ScriptRuntime, au as MemoryType, q as IRuntimeBlock, aw as MemoryValueOf, ae as DisplayState, ax as RoundState, a3 as TimerState, Z as IScriptRuntime, F as TypedMemoryReference, V as ButtonConfig, L as StackSnapshot } from './browserRuntimeNow-CoR7dCNU.js';
2
+ export { aE as createBrowserRuntimeNow, aJ as getRuntimeNowMs, aM as runtimeNow } from './browserRuntimeNow-CoR7dCNU.js';
3
3
  import { IMetricSource, IOutputStatement, MetricContainer, TimeSpan } from '@bitcobblers/wod-wiki-core';
4
4
  import React$1, { ReactNode } from 'react';
5
5
 
@@ -315,7 +315,7 @@ interface FragmentSourceEntry {
315
315
  isLinked?: boolean;
316
316
  status?: FragmentSourceStatus;
317
317
  label?: string;
318
- metricGroups?: any[];
318
+ metricGroups?: readonly MetricContainer[];
319
319
  }
320
320
  /**
321
321
  * Represents a timer on the stack with its owning block context.
package/dist/react.mjs CHANGED
@@ -29,22 +29,28 @@ var useRuntimeExecution = (runtime) => {
29
29
  const [startTime, setStartTime] = useState(null);
30
30
  const intervalRef = useRef(null);
31
31
  const startTimeRef = useRef(null);
32
+ const stop = useCallback(() => {
33
+ if (intervalRef.current) {
34
+ clearInterval(intervalRef.current);
35
+ intervalRef.current = null;
36
+ }
37
+ setStatus("idle");
38
+ }, []);
32
39
  const executeStep = useCallback(() => {
33
40
  if (!runtime) return;
34
41
  try {
35
42
  const tickEvent = new TickEvent(void 0, runtime.nowProvider);
36
43
  runtime.handle(tickEvent);
37
44
  setStepCount((prev) => prev + 1);
38
- } catch (error) {
45
+ } catch {
39
46
  stop();
40
47
  setStatus("error");
41
- console.error("Runtime execution error:", error);
42
48
  }
43
- }, [runtime]);
49
+ }, [runtime, stop]);
44
50
  useEffect(() => {
45
51
  if (!runtime) return;
46
52
  const unsubscribe = runtime.subscribeToStack((snapshot) => {
47
- if (snapshot.blocks.length === 0 && (status === "running" || status === "paused")) {
53
+ if (snapshot.blocks.length === 0 && status !== "completed") {
48
54
  if (intervalRef.current) {
49
55
  clearInterval(intervalRef.current);
50
56
  intervalRef.current = null;
@@ -64,11 +70,11 @@ var useRuntimeExecution = (runtime) => {
64
70
  }, []);
65
71
  const start = useCallback(() => {
66
72
  if (!runtime) {
67
- console.warn("Cannot start execution: runtime is null");
73
+ console.warn("useRuntimeExecution: Cannot start execution without a runtime");
68
74
  return;
69
75
  }
70
76
  if (status === "running") {
71
- console.warn("Cannot start execution: already running");
77
+ console.warn("useRuntimeExecution: Runtime is already running");
72
78
  return;
73
79
  }
74
80
  if (status === "paused") {
@@ -100,13 +106,6 @@ var useRuntimeExecution = (runtime) => {
100
106
  }
101
107
  setStatus("paused");
102
108
  }, [runtime]);
103
- const stop = useCallback(() => {
104
- if (intervalRef.current) {
105
- clearInterval(intervalRef.current);
106
- intervalRef.current = null;
107
- }
108
- setStatus("idle");
109
- }, []);
110
109
  const reset = useCallback(() => {
111
110
  if (intervalRef.current) {
112
111
  clearInterval(intervalRef.current);
@@ -119,12 +118,7 @@ var useRuntimeExecution = (runtime) => {
119
118
  startTimeRef.current = null;
120
119
  }, [runtime]);
121
120
  const step = useCallback(() => {
122
- if (!runtime) {
123
- console.warn("Cannot step: runtime is null");
124
- return;
125
- }
126
- if (status === "running") {
127
- console.warn("Cannot step while running");
121
+ if (!runtime || status === "running") {
128
122
  return;
129
123
  }
130
124
  setStatus("paused");
@@ -136,11 +130,12 @@ var useRuntimeExecution = (runtime) => {
136
130
  setElapsedTime((runtime?.nowProvider?.nowMs() ?? wallClockNow.nowMs()) - startTimeRef.current);
137
131
  }, 100);
138
132
  return () => clearInterval(updateInterval);
139
- }, [status]);
133
+ }, [status, runtime?.nowProvider]);
140
134
  useEffect(() => {
141
135
  return () => {
142
136
  if (intervalRef.current) {
143
137
  clearInterval(intervalRef.current);
138
+ intervalRef.current = null;
144
139
  }
145
140
  };
146
141
  }, []);
@@ -686,7 +681,7 @@ function useTimerElapsed(blockKey) {
686
681
  return unsubscribe;
687
682
  }
688
683
  }, [block]);
689
- const timeSpans = timerState?.spans || [];
684
+ const timeSpans = useMemo(() => timerState?.spans ?? [], [timerState]);
690
685
  const isRunning = timeSpans.length > 0 && timeSpans[timeSpans.length - 1].ended === void 0;
691
686
  const [now, setNow] = useState(getRuntimeNowMs());
692
687
  const elapsed = useMemo(() => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime/events/TickEvent.ts","../src/react/hooks/useRuntimeExecution.ts","../src/utils/timeUtils.ts","../src/react/hooks/useBlockMemory.ts","../src/react/context/RuntimeContext.tsx","../src/react/hooks/useOutputStatements.ts","../src/react/hooks/useMemorySubscription.ts","../src/react/hooks/useStackSnapshot.ts","../src/react/hooks/useNextPreview.ts","../src/react/hooks/useStackDisplay.ts","../src/react/hooks/useTimerElapsed.ts","../src/react/components/BlockTimerDisplay.tsx"],"names":["start","useState","useEffect","useMemo","MetricContainer","jsx"],"mappings":"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","file":"react.mjs","sourcesContent":["import { IEvent } from '../contracts/events/IEvent';\nimport { INowProvider, wallClockNow } from '../INowProvider';\n\n/**\n * Event emitted on each tick of the runtime clock (typically every ~20ms).\n * \n * Behaviors subscribe to `'tick'` to receive periodic updates:\n * - `TimerTickBehavior` — updates timer elapsed time\n * - `TimerEndingBehavior` — checks if countdown has expired\n * - `SoundCueBehavior` — triggers countdown audio cues\n * \n * Production entry point: `runtime.handle(new TickEvent())`\n */\nexport class TickEvent implements IEvent {\n readonly name: string = 'tick';\n readonly timestamp: Date;\n readonly data?: unknown;\n private static _counter = 0;\n constructor(data?: unknown, private readonly now: INowProvider = wallClockNow) {\n const nowMs = this.now.nowMs();\n TickEvent._counter++;\n this.timestamp = new Date(nowMs + TickEvent._counter);\n this.data = data;\n }\n}\n","/**\n * useRuntimeExecution - Custom React hook for managing ScriptRuntime execution\n * \n * Encapsulates execution logic, interval management, and lifecycle cleanup.\n * \n * Architecture Decision:\n * - Fixed 20ms tick rate (50 ticks per second)\n * - No speed control (removed for simplicity)\n * - Automatic cleanup on unmount\n * - Reusable across components\n */\n\nimport { useState, useCallback, useRef, useEffect, useMemo } from 'react';\nimport type { ScriptRuntime } from '../../runtime/ScriptRuntime';\nimport { TickEvent } from '../../runtime/events/TickEvent';\nimport { wallClockNow } from '../../runtime/INowProvider';\n\n/** Fixed execution tick rate in milliseconds (50 ticks per second) */\nconst EXECUTION_TICK_RATE_MS = 20;\n\n/**\n * Execution status states\n */\nexport type ExecutionStatus =\n | 'idle' // No runtime or not started\n | 'running' // Active execution with interval\n | 'paused' // Execution stopped but state preserved\n | 'completed' // Runtime finished successfully\n | 'error'; // Runtime encountered error\n\n/**\n * Return type for useRuntimeExecution hook\n */\nexport interface UseRuntimeExecutionReturn {\n status: ExecutionStatus;\n elapsedTime: number;\n stepCount: number;\n startTime: number | null;\n start: () => void;\n pause: () => void;\n stop: () => void;\n reset: () => void;\n step: () => void;\n}\n\n/**\n * Manages ScriptRuntime execution lifecycle with fixed 20ms tick rate\n * \n * @param runtime - The ScriptRuntime instance to execute (null if not ready)\n * @returns Object with execution state and control methods\n */\nexport const useRuntimeExecution = (\n runtime: ScriptRuntime | null\n): UseRuntimeExecutionReturn => {\n const [status, setStatus] = useState<ExecutionStatus>('idle');\n const [elapsedTime, setElapsedTime] = useState(0);\n const [stepCount, setStepCount] = useState(0);\n const [startTime, setStartTime] = useState<number | null>(null);\n\n const intervalRef = useRef<NodeJS.Timeout | null>(null);\n const startTimeRef = useRef<number | null>(null);\n\n /**\n * Executes a single runtime step\n * Called by interval timer and step() method\n */\n const executeStep = useCallback(() => {\n if (!runtime) return;\n\n try {\n // Use TickEvent for periodic updates instead of NextEvent\n const tickEvent = new TickEvent(undefined, runtime.nowProvider);\n runtime.handle(tickEvent);\n setStepCount(prev => prev + 1);\n } catch (error) {\n stop();\n setStatus('error');\n console.error('Runtime execution error:', error);\n }\n }, [runtime]);\n\n /**\n * Reactive completion detection\n * Ensures 'completed' status is set immediately when the stack becomes empty,\n * regardless of whether the playback loop is active.\n */\n useEffect(() => {\n if (!runtime) return;\n \n // Subscribe to stack snapshots to detect when everything is finished\n const unsubscribe = runtime.subscribeToStack((snapshot) => {\n // If stack becomes empty and we were previously in an active state, we are done!\n if (snapshot.blocks.length === 0 && (status === 'running' || status === 'paused')) {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n setStatus('completed');\n }\n });\n\n return unsubscribe;\n }, [runtime, status]);\n\n // Cleanup interval on unmount\n useEffect(() => {\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, []);\n\n /**\n * Starts continuous execution at fixed 20ms tick rate\n */\n const start = useCallback(() => {\n if (!runtime) {\n console.warn('Cannot start execution: runtime is null');\n return;\n }\n\n if (status === 'running') {\n console.warn('Cannot start execution: already running');\n return;\n }\n\n // Emit timer:resume event when resuming from paused state\n if (status === 'paused') {\n runtime.handle({\n name: 'timer:resume',\n timestamp: new Date(),\n data: {}\n });\n }\n\n setStatus('running');\n const now = runtime?.nowProvider?.nowMs() ?? wallClockNow.nowMs();\n startTimeRef.current = now - elapsedTime; // Resume from paused time\n\n // Only set startTime on first start (not resume)\n if (elapsedTime === 0) {\n setStartTime(now);\n }\n\n // Start interval for subsequent steps\n intervalRef.current = setInterval(executeStep, EXECUTION_TICK_RATE_MS);\n }, [runtime, status, elapsedTime, executeStep]);\n\n /**\n * Pauses execution, preserving state\n */\n const pause = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n\n // Emit timer:pause event to update timer memory spans\n if (runtime) {\n runtime.handle({\n name: 'timer:pause',\n timestamp: new Date(),\n data: {}\n });\n }\n\n setStatus('paused');\n }, [runtime]);\n\n /**\n * Stops continuous execution without clearing metrics.\n * Preserves elapsedTime and startTime for completion reporting.\n */\n const stop = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n setStatus('idle');\n }, []);\n\n /**\n * Resets execution state and clears all metrics.\n * Used for \"replay\" functionality or starting fresh.\n */\n const reset = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n setElapsedTime(0);\n setStepCount(0);\n setStatus('idle');\n setStartTime(null);\n startTimeRef.current = null;\n\n // Reset runtime if available\n if (runtime) {\n // TODO: Add runtime.reset() method when available\n }\n }, [runtime]);\n\n /**\n * Executes a single step manually\n * Used for debugging and step-through execution\n */\n const step = useCallback(() => {\n if (!runtime) {\n console.warn('Cannot step: runtime is null');\n return;\n }\n\n if (status === 'running') {\n console.warn('Cannot step while running');\n return;\n }\n\n setStatus('paused');\n executeStep();\n }, [runtime, status, executeStep]);\n\n /**\n * Update elapsed time while running\n */\n useEffect(() => {\n if (status !== 'running' || !startTimeRef.current) return;\n\n const updateInterval = setInterval(() => {\n setElapsedTime((runtime?.nowProvider?.nowMs() ?? wallClockNow.nowMs()) - startTimeRef.current!);\n }, 100); // Update display every 100ms\n\n return () => clearInterval(updateInterval);\n }, [status]);\n\n /**\n * Cleanup on unmount - critical for preventing memory leaks\n */\n useEffect(() => {\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n }\n };\n }, []);\n\n /**\n * Reset state when runtime changes\n */\n useEffect(() => {\n reset();\n }, [runtime, reset]);\n\n return useMemo(() => ({\n status,\n elapsedTime,\n stepCount,\n startTime,\n start,\n pause,\n stop,\n reset,\n step\n }), [status, elapsedTime, stepCount, startTime, start, pause, stop, reset, step]);\n};\n","import { TimeSpan } from '@bitcobblers/wod-wiki-core';\n\n/**\n * Formats a **TimeStamp** (Date or epoch ms) into HH:MM:SS format.\n * Returns 'running' if date is undefined.\n *\n * @see docs/architecture/time-terminology.md — TimeStamp is the system time\n * (Date.now()) when a message is logged.\n */\nexport const formatTimestamp = (date?: Date | number): string => {\n if (!date) return 'running';\n\n const dateObj = typeof date === 'number' ? new Date(date) : date;\n\n return dateObj.toLocaleTimeString('en-US', {\n hour12: false,\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit'\n });\n};\n\n/**\n * Format milliseconds to MM:SS.ms display\n * e.g., 65432 -> \"01:05.43\"\n */\nexport const formatTime = (ms: number): string => {\n if (ms === undefined || ms === null || !Number.isFinite(ms)) {\n return '--:--.--';\n }\n const isNegative = ms < 0;\n const absMs = Math.abs(ms);\n const totalSeconds = Math.floor(absMs / 1000);\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n const milliseconds = Math.floor((absMs % 1000) / 10);\n const sign = isNegative ? '-' : '';\n return `${sign}${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(2, '0')}`;\n};\n\n/**\n * Calculates **Elapsed** (sum of span durations) from an array of time spans.\n *\n * Equivalent to `calculateElapsed()` in `src/runtime/time/calculateElapsed.ts`.\n * Prefer the runtime version for new code; this exists for component-layer compat.\n *\n * @see docs/architecture/time-terminology.md\n */\nexport const calculateDuration = (spans: ReadonlyArray<TimeSpan | { started: number, ended?: number } | { start: number | Date, stop?: number | Date }>, now: number): number => {\n if (!spans || !Array.isArray(spans)) return 0;\n return spans.reduce((total, span) => {\n // Handle canonical TimeSpan or raw objects with 'started'/'ended'\n if ('started' in span) {\n const start = (span as any).started;\n const end = (span as any).ended ?? now;\n return total + Math.max(0, end - start);\n }\n\n // Handle legacy objects with 'start'/'stop'\n const start = (span as any).start instanceof Date ? (span as any).start.getTime() : ((span as any).start || 0);\n const stop = (span as any).stop ? ((span as any).stop instanceof Date ? (span as any).stop.getTime() : (span as any).stop) : now;\n return total + Math.max(0, (stop as number) - (start as number));\n }, 0);\n};\n\n/**\n * Rounds a duration in seconds to the nearest 0.1s.\n */\nexport const roundToTenth = (seconds: number): number => {\n if (!Number.isFinite(seconds)) return 0;\n return Math.round(seconds * 10) / 10;\n};\n","import { useState, useEffect, useMemo } from 'react';\nimport { IRuntimeBlock } from '../../runtime/contracts/IRuntimeBlock';\nimport { MemoryType, MemoryValueOf, TimerState, RoundState, DisplayState } from '../../runtime/memory/MemoryTypes';\nimport { IMetricSource } from '@bitcobblers/wod-wiki-core';\nimport { MetricContainer } from '@bitcobblers/wod-wiki-core';\nimport { formatDurationSmart } from '../../utils/formatTime';\nimport { calculateDuration } from '../../utils/timeUtils';\nimport { MemoryTag } from '../../runtime/memory/MemoryLocation';\n\n/**\n * React hook to subscribe to a specific memory type on a block.\n * \n * Automatically subscribes when the block or type changes, and\n * unsubscribes on cleanup. Returns the current value and updates\n * reactively when behaviors modify the memory.\n * \n * @param block The runtime block to subscribe to (can be undefined)\n * @param type The memory type to subscribe to\n * @returns The current memory value or undefined\n * \n * @example\n * ```tsx\n * function TimerComponent({ block }: { block: IRuntimeBlock }) {\n * const timerState = useBlockMemory(block, 'time');\n * \n * if (!timerState) return null;\n * \n * return <div>Direction: {timerState.direction}</div>;\n * }\n * ```\n */\nexport function useBlockMemory<T extends MemoryType>(\n block: IRuntimeBlock | undefined,\n type: T\n): MemoryValueOf<T> | undefined {\n const tag = type as MemoryTag;\n const getVal = (): MemoryValueOf<T> | undefined => {\n const loc = block?.getMemoryByTag(tag)[0];\n if (!loc) return undefined;\n // 'round' tag: the metrics itself IS the value (CurrentRoundMetric satisfies RoundState)\n if (tag === 'round') return loc.metrics[0] as unknown as MemoryValueOf<T>;\n return loc.metrics[0]?.value as MemoryValueOf<T> | undefined;\n };\n const [value, setValue] = useState<MemoryValueOf<T> | undefined>(getVal);\n\n useEffect(() => {\n if (!block) {\n setValue(undefined);\n return;\n }\n\n // Get initial value\n setValue(getVal());\n\n // Subscribe to changes\n const loc = block.getMemoryByTag(tag)[0];\n if (loc) {\n const unsubscribe = loc.subscribe((newFrags) => {\n if (tag === 'round') {\n setValue(newFrags[0] as unknown as MemoryValueOf<T> | undefined);\n } else {\n setValue(newFrags[0]?.value as MemoryValueOf<T> | undefined);\n }\n });\n return unsubscribe;\n }\n\n return undefined;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [block, type]);\n\n return value;\n}\n\n/**\n * Hook for subscribing to timer state from a block.\n * \n * @param block The runtime block\n * @returns The current TimerState or undefined\n */\nexport function useTimerState(block: IRuntimeBlock | undefined): TimerState | undefined {\n return useBlockMemory(block, 'time');\n}\n\n/**\n * Hook for subscribing to round state from a block.\n * \n * @param block The runtime block\n * @returns The current RoundState or undefined\n */\nexport function useRoundState(block: IRuntimeBlock | undefined): RoundState | undefined {\n return useBlockMemory(block, 'round');\n}\n\n/**\n * Hook for subscribing to display state from a block.\n * \n * @param block The runtime block\n * @returns The current DisplayState or undefined\n */\nexport function useDisplayState(block: IRuntimeBlock | undefined): DisplayState | undefined {\n return useBlockMemory(block, 'display');\n}\n\n/**\n * Derived timer display values for UI rendering.\n */\nexport interface TimerDisplayValues {\n /** Total elapsed time in milliseconds */\n elapsed: number;\n /** Remaining time in milliseconds (for countdowns) */\n remaining: number | undefined;\n /** Whether the timer is currently running (not paused) */\n isRunning: boolean;\n /** Timer direction */\n direction: 'up' | 'down';\n /** Whether the timer has completed (countdown reached 0) */\n isComplete: boolean;\n /** Formatted time string (MM:SS or HH:MM:SS) */\n formatted: string;\n}\n\n/**\n * Hook for derived timer display values.\n * \n * Computes elapsed time, remaining time, and formatted display strings\n * from the raw timer state. Uses requestAnimationFrame for smooth 60fps\n * updates when the timer is running.\n * \n * Consolidated with useTimerElapsed - both now share calculateDuration utility.\n * \n * @param block The runtime block\n * @returns Derived timer display values or null if no timer\n * \n * @example\n * ```tsx\n * function TimerDisplay({ block }: { block: IRuntimeBlock }) {\n * const display = useTimerDisplay(block);\n * \n * if (!display) return <div>No timer</div>;\n * \n * return (\n * <div>\n * <span>{display.formatted}</span>\n * {display.direction === 'down' && (\n * <span>{formatTime(display.remaining ?? 0)} remaining</span>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useTimerDisplay(block: IRuntimeBlock | undefined): TimerDisplayValues | null {\n const timer = useTimerState(block);\n\n // Get the sender's clock time if available (Chromecast receiver), otherwise use local clock\n // We check for the global window property set by the receiver during Chromecast sessions\n const getNow = (): number => {\n // Check if we're in a Chromecast receiver context\n if (typeof (window as any).__chromecast_senderClockTimeMs === 'function') {\n return (window as any).__chromecast_senderClockTimeMs();\n }\n return Date.now();\n };\n\n const [now, setNow] = useState(getNow());\n\n // Determine if timer is running (last span has no end time)\n const isRunning = useMemo(() => {\n if (!timer || timer.spans.length === 0) return false;\n const lastSpan = timer.spans[timer.spans.length - 1];\n return lastSpan.ended === undefined;\n }, [timer]);\n\n // Animation frame loop for smooth updates when running\n useEffect(() => {\n if (!isRunning) return;\n\n let animationFrameId: number;\n let running = true;\n\n const tick = () => {\n if (running) {\n setNow(getNow());\n animationFrameId = requestAnimationFrame(tick);\n }\n };\n\n animationFrameId = requestAnimationFrame(tick);\n\n return () => {\n running = false;\n cancelAnimationFrame(animationFrameId);\n };\n }, [isRunning]);\n\n // Compute derived values using shared utilities\n return useMemo(() => {\n if (!timer) return null;\n\n const elapsed = calculateDuration(timer.spans, now);\n const remaining = timer.durationMs !== undefined\n ? Math.max(0, timer.durationMs - elapsed)\n : undefined;\n const isComplete = timer.durationMs !== undefined && elapsed >= timer.durationMs;\n\n // Format based on direction\n const displayMs = timer.direction === 'down' && remaining !== undefined\n ? remaining\n : elapsed;\n\n return {\n elapsed,\n remaining,\n isRunning,\n direction: timer.direction,\n isComplete,\n formatted: formatDurationSmart(displayMs)\n };\n }, [timer, now, isRunning]);\n}\n\n/**\n * Derived round display values for UI rendering.\n */\nexport interface RoundDisplayValues {\n /** Current round number */\n current: number;\n /** Total rounds (undefined for unbounded) */\n total: number | undefined;\n /** Formatted round label (e.g., \"Round 3 of 5\") */\n label: string;\n /** Progress as a fraction 0-1 (undefined for unbounded) */\n progress: number | undefined;\n}\n\n/**\n * Hook for derived round display values.\n * \n * @param block The runtime block\n * @returns Derived round display values or null if no rounds\n */\nexport function useRoundDisplay(block: IRuntimeBlock | undefined): RoundDisplayValues | null {\n const round = useRoundState(block);\n const display = useDisplayState(block);\n\n return useMemo(() => {\n if (!round) return null;\n\n const label = display?.roundDisplay ??\n (round.total !== undefined\n ? `Round ${round.current} of ${round.total}`\n : `Round ${round.current}`);\n\n const progress = round.total !== undefined\n ? Math.min(1, (round.current - 1) / round.total)\n : undefined;\n\n return {\n current: round.current,\n total: round.total,\n label,\n progress\n };\n }, [round, display?.roundDisplay]);\n}\n\n/**\n * Hook for subscribing to the display metrics source from a block.\n *\n * Returns the `IMetricSource` interface from the block's `'metric:display'`\n * memory entry (`DisplayMetricMemory`). The returned source provides\n * precedence-resolved metric that update reactively when the underlying\n * metric memory or runtime state changes.\n *\n * This is the primary hook for UI components that need to render metrics\n * from a runtime block with proper origin precedence (user > runtime > compiler > parser).\n *\n * @param block The runtime block (can be undefined)\n * @returns The IMetricSource for the block, or undefined if no metrics:display memory\n *\n * @example\n * ```tsx\n * function BlockView({ block }: { block: IRuntimeBlock }) {\n * const source = useFragmentSource(block);\n * if (!source) return null;\n *\n * const metrics = source.getDisplayMetrics();\n * return <MetricVisualizer metric={metric} />;\n * }\n * ```\n */\nexport function useFragmentSource(block: IRuntimeBlock | undefined): IMetricSource | undefined {\n const [version, setVersion] = useState(0);\n\n useEffect(() => {\n if (!block) return;\n const loc = block.getMemoryByTag('metric:display')[0];\n if (!loc) return;\n return loc.subscribe(() => setVersion(v => v + 1));\n }, [block]);\n\n return useMemo(() => {\n if (!block) return undefined;\n const locs = block.getMemoryByTag('metric:display');\n if (locs.length === 0) return undefined;\n const allFragments = MetricContainer.empty(block.key.toString());\n for (const loc of locs) allFragments.merge(loc.metrics);\n return allFragments;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [block, version]);\n}\n","import { createContext, useContext, ReactNode } from 'react';\nimport { IScriptRuntime } from '../../runtime/contracts/IScriptRuntime';\n\n/**\n * React Context for providing IScriptRuntime to components.\n * \n * This is a simple dependency injection context — it passes a pre-created\n * IScriptRuntime instance to children. No lifecycle management.\n * \n * For lifecycle management (creation, disposal, error tracking),\n * use RuntimeLifecycleProvider from '@/contexts/RuntimeLifecycleProvider' instead.\n * \n * Components can use useScriptRuntime() to access runtime memory and state.\n */\nconst ScriptRuntimeContext = createContext<IScriptRuntime | undefined>(undefined);\n\nexport interface ScriptRuntimeProviderProps {\n runtime: IScriptRuntime;\n children: ReactNode;\n}\n\n/**\n * Provides IScriptRuntime context to child components.\n * Must wrap any components that need access to runtime memory or state.\n * \n * @example\n * ```tsx\n * const runtime = new ScriptRuntime();\n * const script = parseWorkout('For Time: 30 Jumping Jacks');\n * runtime.execute(script);\n * \n * return (\n * <ScriptRuntimeProvider runtime={runtime}>\n * <ClockAnchor blockKey=\"block-001\" />\n * </ScriptRuntimeProvider>\n * );\n * ```\n */\nexport const ScriptRuntimeProvider: React.FC<ScriptRuntimeProviderProps> = ({\n runtime,\n children\n}) => {\n return (\n <ScriptRuntimeContext.Provider value={runtime}>\n {children}\n </ScriptRuntimeContext.Provider>\n );\n};\n\n/**\n * Hook to access the IScriptRuntime from context.\n * Must be used within a ScriptRuntimeProvider.\n * \n * @throws Error if called outside ScriptRuntimeProvider\n * @returns IScriptRuntime instance\n */\nexport function useScriptRuntime(): IScriptRuntime {\n const runtime = useContext(ScriptRuntimeContext);\n if (!runtime) {\n throw new Error('useScriptRuntime must be used within ScriptRuntimeProvider');\n }\n return runtime;\n}\n\n","import { useState, useEffect, useMemo } from 'react';\nimport { useScriptRuntime } from '../context/RuntimeContext';\nimport { IScriptRuntime } from '../../runtime/contracts/IScriptRuntime';\nimport { IOutputStatement } from '@bitcobblers/wod-wiki-core';\n\n/**\n * Data structure returned by useOutputStatements hook\n * Consolidated from separate implementations in runtime/hooks and clock/hooks\n */\nexport interface OutputStatementsData {\n /** All output statements sorted by start time */\n outputs: IOutputStatement[];\n /** Outputs indexed by ID for quick lookup */\n byId: Map<number, IOutputStatement>;\n /** Outputs indexed by sourceBlockKey */\n byBlockKey: Map<string, IOutputStatement[]>;\n /** Segment outputs (timed execution segments) */\n segments: IOutputStatement[];\n /** Completion outputs */\n completions: IOutputStatement[];\n /** Metric outputs */\n metrics: IOutputStatement[];\n}\n\n/**\n * React hook that subscribes to output statement updates from a runtime.\n * \n * Unified hook that works with or without a runtime parameter:\n * - useOutputStatements() - uses runtime from context\n * - useOutputStatements(runtime) - uses provided runtime parameter\n * \n * Returns enriched OutputStatementsData with indexed maps for performance.\n * Consolidated from separate runtime/hooks and clock/hooks implementations.\n *\n * @param runtime Optional runtime instance. If not provided, uses context.\n * @returns OutputStatementsData containing outputs and indexed maps\n *\n * @example\n * ```tsx\n * // With context (default)\n * const data = useOutputStatements();\n * \n * // With explicit runtime parameter\n * const data = useOutputStatements(myRuntime);\n * ```\n */\nexport function useOutputStatements(runtime?: IScriptRuntime | null): OutputStatementsData {\n // Always call useScriptRuntime (satisfies Rules of Hooks — no conditional calls).\n // Use the explicit runtime if provided, otherwise fall back to context.\n const contextRuntime = useScriptRuntime();\n const resolvedRuntime = (runtime !== undefined) ? runtime : contextRuntime;\n\n const [outputs, setOutputs] = useState<IOutputStatement[]>(() => {\n return resolvedRuntime?.getOutputStatements?.() ?? [];\n });\n\n useEffect(() => {\n if (!resolvedRuntime) {\n setOutputs([]);\n return;\n }\n\n // Initial load\n setOutputs(resolvedRuntime.getOutputStatements());\n\n // Subscribe to new output statements\n const unsubscribe = resolvedRuntime.subscribeToOutput?.(() => {\n setOutputs(resolvedRuntime.getOutputStatements());\n });\n\n return unsubscribe;\n }, [resolvedRuntime]);\n\n // Compute derived data (memoized for performance)\n const data = useMemo<OutputStatementsData>(() => {\n const byId = new Map<number, IOutputStatement>();\n const byBlockKey = new Map<string, IOutputStatement[]>();\n const segments: IOutputStatement[] = [];\n const metrics: IOutputStatement[] = [];\n\n for (const output of outputs) {\n // Index by ID\n byId.set(output.id, output);\n\n // Index by block key\n const blockOutputs = byBlockKey.get(output.sourceBlockKey) ?? [];\n blockOutputs.push(output);\n byBlockKey.set(output.sourceBlockKey, blockOutputs);\n\n // Categorize by type\n switch (output.outputType) {\n case 'segment':\n segments.push(output);\n break;\n case 'metric':\n metrics.push(output);\n break;\n }\n }\n // 'completion' is folded into segment outputs (completionReason field);\n // surface completed-block segments as the completions collection.\n const completions = segments.filter(s => s.completionReason !== undefined);\n\n return { outputs, byId, byBlockKey, segments, completions, metrics };\n }, [outputs]);\n\n return data;\n}\n\n/**\n * Live Tier-2 (running-total) snapshot for display during an active session.\n *\n * Returns the analytics engine's current projection set — totals like total\n * volume / distance / load, recomputed per segment. These values are ephemeral:\n * they are shown live but never persisted; only the finalized values reach the\n * records after the WOD completes. Reuses the output subscription as its change\n * signal (the snapshot is replaced, not appended).\n */\nexport function useLiveAnalytics(runtime?: IScriptRuntime | null): IOutputStatement[] {\n const contextRuntime = useScriptRuntime();\n const resolvedRuntime = runtime !== undefined ? runtime : contextRuntime;\n\n const [analytics, setAnalytics] = useState<IOutputStatement[]>(() =>\n resolvedRuntime?.getLiveAnalytics?.() ?? [],\n );\n\n useEffect(() => {\n if (!resolvedRuntime) {\n setAnalytics([]);\n return;\n }\n setAnalytics(resolvedRuntime.getLiveAnalytics?.() ?? []);\n const unsubscribe = resolvedRuntime.subscribeToOutput?.(() => {\n setAnalytics(resolvedRuntime.getLiveAnalytics?.() ?? []);\n });\n return unsubscribe;\n }, [resolvedRuntime]);\n\n return analytics;\n}\n\n/**\n * Hook to get a specific output by ID\n */\nexport function useOutputStatement(\n runtime: IScriptRuntime | null,\n id: number | null\n): IOutputStatement | undefined {\n const { byId } = useOutputStatements(runtime);\n\n if (id === null) return undefined;\n return byId.get(id);\n}\n\n/**\n * Hook to get outputs for a specific block\n */\nexport function useBlockOutputs(\n runtime: IScriptRuntime | null,\n blockKey: string | null\n): IOutputStatement[] {\n const { byBlockKey } = useOutputStatements(runtime);\n\n if (!blockKey) return [];\n return byBlockKey.get(blockKey) ?? [];\n}\n\n/**\n * Hook to compute output hierarchy (parent-child relationships) based on stackLevel\n */\nexport function useOutputHierarchy(runtime: IScriptRuntime | null): Map<number, number> {\n const { outputs } = useOutputStatements(runtime);\n\n return useMemo(() => {\n const depthMap = new Map<number, number>();\n\n for (const output of outputs) {\n depthMap.set(output.id, output.stackLevel);\n }\n\n return depthMap;\n }, [outputs]);\n}\n","import { useState, useEffect } from 'react';\nimport { TypedMemoryReference } from '../../runtime/impl/TypedMemoryReference';\n\n/**\n * React hook for subscribing to memory reference changes.\n * \n * This hook automatically manages the subscription lifecycle:\n * - Subscribes when the component mounts or when memoryRef changes\n * - Unsubscribes when the component unmounts or when memoryRef changes\n * - Returns the current value and updates when the memory changes\n * \n * @param memoryRef The memory reference to subscribe to (can be undefined)\n * @returns The current value from the memory reference, or undefined if not available\n * \n * @example\n * ```tsx\n * const runtime = useScriptRuntime();\n * const timeSpansRef = runtime.memory.search({ \n * type: 'timer-time-spans', \n * ownerId: blockKey \n * })[0] as TypedMemoryReference<TimeSpan[]>;\n * \n * const timeSpans = useMemorySubscription(timeSpansRef);\n * ```\n */\nexport function useMemorySubscription<T>(\n memoryRef: TypedMemoryReference<T> | undefined\n): T | undefined {\n const [value, setValue] = useState<T | undefined>(memoryRef?.get());\n\n useEffect(() => {\n if (!memoryRef) {\n setValue(undefined);\n return;\n }\n\n // Set initial value\n setValue(memoryRef.get());\n\n // Subscribe to memory changes\n const unsubscribe = memoryRef.subscribe((newValue: T | undefined) => {\n setValue(newValue);\n });\n\n return unsubscribe;\n }, [memoryRef]);\n\n return value;\n}\n","import { useState, useEffect } from 'react';\nimport { useScriptRuntime } from '../context/RuntimeContext';\nimport { IRuntimeBlock } from '../../runtime/contracts/IRuntimeBlock';\nimport { StackSnapshot } from '../../runtime/contracts/IRuntimeStack';\n\n/**\n * Hook that subscribes to stack snapshots via `runtime.subscribeToStack()`.\n *\n * Returns the latest StackSnapshot, which contains:\n * - `blocks` — current stack (bottom-to-top)\n * - `affectedBlock` — the block that was pushed/popped\n * - `depth` — stack depth after the event\n * - `clockTime` — runtime clock time at event\n * - `type` — 'push' | 'pop' | 'clear' | 'initial'\n *\n * This is the primary hook for UI components that need to react to stack\n * structural changes. For high-frequency timer updates, subscribe to\n * block memory separately.\n *\n * @returns The latest StackSnapshot\n *\n * @example\n * ```tsx\n * function StackDisplay() {\n * const snapshot = useStackSnapshot();\n *\n * return (\n * <div>\n * <p>Depth: {snapshot.depth}</p>\n * {snapshot.blocks.map(block => (\n * <div key={block.key.toString()}>{block.label}</div>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useStackSnapshot(): StackSnapshot {\n const runtime = useScriptRuntime();\n const [snapshot, setSnapshot] = useState<StackSnapshot>({\n type: 'initial',\n blocks: runtime.stack.blocks,\n depth: runtime.stack.count,\n clockTime: runtime.clock.currentDate,\n });\n\n useEffect(() => {\n const unsubscribe = runtime.subscribeToStack((s) => {\n setSnapshot(s);\n });\n\n return unsubscribe;\n }, [runtime]);\n\n return snapshot;\n}\n\n/**\n * Hook that returns the current stack blocks from the most recent snapshot.\n * Convenience wrapper over useStackSnapshot().\n *\n * @returns Readonly array of blocks currently on the stack\n */\nexport function useSnapshotBlocks(): readonly IRuntimeBlock[] {\n const snapshot = useStackSnapshot();\n return snapshot.blocks;\n}\n\n/**\n * Hook that returns the current (top) block on the stack from the snapshot.\n * Returns undefined if the stack is empty.\n *\n * @returns The top block on the stack, or undefined if empty\n */\nexport function useSnapshotCurrentBlock(): IRuntimeBlock | undefined {\n const snapshot = useStackSnapshot();\n return snapshot.blocks.length > 0\n ? snapshot.blocks[snapshot.blocks.length - 1]\n : undefined;\n}\n","import { useState, useEffect, useMemo } from 'react';\nimport { useSnapshotBlocks } from './useStackSnapshot';\nimport { IRuntimeBlock } from '../../runtime/contracts/IRuntimeBlock';\nimport { MetricContainer } from '@bitcobblers/wod-wiki-core';\n\n/**\n * Result of the next-preview resolution.\n *\n * `metrics` contains the display metrics for the next child that\n * will be pushed by the deepest block on the stack that has a\n * `metrics:next` memory location.\n */\nexport interface NextPreview {\n /** Fragments describing the next child to be executed */\n metrics: MetricContainer;\n /** The block that owns this preview (the parent that will push the child) */\n block: IRuntimeBlock;\n}\n\n/**\n * Hook that resolves the \"Up Next\" preview from the runtime stack.\n *\n * Walks the stack from leaf (index 0) to root, finding the first block\n * that has a non-empty `metrics:next` memory location. This gives the\n * deepest (most specific) preview of what's coming next.\n *\n * The hook subscribes to `metrics:next` memory changes on all stack\n * blocks, so it re-renders when the preview updates (e.g., after a\n * child completes and the next child is queued).\n *\n * @returns NextPreview if any block has a next preview, or null\n *\n * @example\n * ```tsx\n * function UpNextCard() {\n * const preview = useNextPreview();\n * if (!preview) return <span>End of session</span>;\n *\n * return <MetricSourceRow metric={preview.metrics} />;\n * }\n * ```\n */\nexport function useNextPreview(): NextPreview | null {\n const blocks = useSnapshotBlocks();\n const [version, setVersion] = useState(0);\n\n // Subscribe to metrics:next memory changes on all blocks\n useEffect(() => {\n const unsubscribes: (() => void)[] = [];\n\n for (const block of blocks) {\n const nextLocs = block.getMemoryByTag('metric:next');\n for (const loc of nextLocs) {\n const unsub = loc.subscribe(() => {\n setVersion(v => v + 1);\n });\n unsubscribes.push(unsub);\n }\n }\n\n return () => {\n unsubscribes.forEach(fn => fn());\n };\n }, [blocks]);\n\n return useMemo(() => {\n // Walk leaf-to-root (blocks[0] is leaf, blocks[length-1] is root)\n for (const block of blocks) {\n const nextLocs = block.getMemoryByTag('metric:next');\n if (nextLocs.length === 0) continue;\n\n // Collect metrics from all metrics:next locations\n const metrics = MetricContainer.empty(block.key.toString());\n for (const loc of nextLocs) metrics.merge(loc.metrics);\n if (metrics.length > 0) {\n return { metrics, block };\n }\n }\n\n return null;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blocks, version]);\n}\n","import { useState, useEffect, useMemo } from 'react';\nimport { IRuntimeBlock } from '../../runtime/contracts/IRuntimeBlock';\nimport { TimerState, ButtonConfig } from '../../runtime/memory/MemoryTypes';\nimport { IMetricSource } from '@bitcobblers/wod-wiki-core';\nimport { MetricContainer } from '@bitcobblers/wod-wiki-core';\nexport type FragmentSourceStatus = 'pending' | 'active' | 'completed' | 'failed' | 'skipped';\n\nexport interface FragmentSourceEntry {\n source: IMetricSource;\n depth: number;\n isLeaf?: boolean;\n isHeader?: boolean;\n isLinked?: boolean;\n status?: FragmentSourceStatus;\n label?: string;\n metricGroups?: any[];\n}\nimport { useSnapshotBlocks } from './useStackSnapshot';\n\n// ============================================================================\n// Stack-Driven Timer Hooks\n// ============================================================================\n\n/**\n * Represents a timer on the stack with its owning block context.\n */\nexport interface StackTimerEntry {\n /** The block that owns this timer */\n block: IRuntimeBlock;\n /** The timer state from block memory */\n timer: TimerState;\n /** Whether this timer is pinned to the primary display */\n isPinned: boolean;\n}\n\n/**\n * Hook that scans the runtime stack for all blocks with timer memory.\n * Returns timer entries ordered from bottom of stack (index 0) to top.\n *\n * Timer data is read directly from block memory via \block.getMemory('time').\n * When a block is popped from the stack, its memory subscriptions are cleaned\n * up automatically, and the stack event triggers a re-render here.\n *\n * @returns Array of stack timer entries\n */\nexport function useStackTimers(): StackTimerEntry[] {\n const blocks = useSnapshotBlocks();\n const [version, setVersion] = useState(0);\n\n // Subscribe to timer memory changes on all blocks\n useEffect(() => {\n const unsubscribes: (() => void)[] = [];\n\n for (const block of blocks) {\n const timerLoc = block.getMemoryByTag('time')[0];\n if (timerLoc) {\n const unsub = timerLoc.subscribe(() => {\n setVersion(v => v + 1);\n });\n unsubscribes.push(unsub);\n }\n }\n\n return () => {\n unsubscribes.forEach(fn => fn());\n };\n }, [blocks]);\n\n return useMemo(() => {\n const entries: StackTimerEntry[] = [];\n\n for (const block of blocks) {\n const timerLoc = block.getMemoryByTag('time')[0];\n if (!timerLoc) continue;\n\n const timer = timerLoc.metrics[0]?.value as TimerState | undefined;\n if (!timer) continue;\n\n entries.push({\n block,\n timer,\n isPinned: timer.role === 'primary'\n });\n }\n\n return entries;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blocks, version]);\n}\n\n/**\n * Determines which timer should be displayed as the primary (big) timer.\n *\n * Pin Resolution Strategy:\n * 1. If the top-of-stack block is a Rest block, its timer is ALWAYS primary.\n * Rest countdowns should be front-and-center so users see time remaining.\n * 2. Otherwise, find the LOWEST (root-closest) block with role === 'primary'.\n * This lets parent blocks (AMRAP, EMOM) keep their timer pinned while\n * child exercises execute beneath them.\n * 3. If no pinned timer exists, use the leaf (top-of-stack) timer.\n *\n * @returns The primary timer entry, or undefined if no timers on stack\n */\nexport function usePrimaryTimer(): StackTimerEntry | undefined {\n const timers = useStackTimers();\n\n return useMemo(() => {\n if (timers.length === 0) return undefined;\n\n // Blocks are delivered in leaf→root order (index 0 = top of stack / leaf,\n // index last = bottom of stack / root). timers mirrors that order.\n const leaf = timers[0];\n\n // Rest blocks always become the pinned timer when pushed.\n // Their countdown should override any parent's pinned timer.\n if (leaf.block.blockType === 'Rest') return leaf;\n\n // Find the lowest (root-closest / LAST in array) pinned timer.\n // Parent blocks (AMRAP, EMOM) pin their countdown so it stays visible\n // while child exercises execute beneath them.\n // Search from root end (last index) toward leaf so the root-closest pinned\n // timer is returned when multiple blocks are pinned.\n const pinned = [...timers].reverse().find(t => t.isPinned);\n if (pinned) return pinned;\n\n // Fallback: leaf timer (top of stack = index 0 in leaf→root order)\n return leaf;\n }, [timers]);\n}\n\n/**\n * Returns all timers that are NOT the primary timer.\n * These are displayed as secondary/context timers in the UI.\n */\nexport function useSecondaryTimers(): StackTimerEntry[] {\n const timers = useStackTimers();\n const primary = usePrimaryTimer();\n\n return useMemo(() => {\n if (!primary) return [];\n return timers.filter(t => t.block !== primary.block);\n }, [timers, primary]);\n}\n\n// ============================================================================\n// Stack-Driven Controls Hook\n// ============================================================================\n\n/**\n * Aggregates control buttons from all blocks on the stack.\n *\n * Button Resolution Strategy:\n * - Collect pinned buttons (isPinned === true) from ALL stack levels\n * - Collect non-pinned buttons from the TOP block only\n * - Deduplicate by button ID (pinned take precedence)\n *\n * When a block is popped, its buttons are automatically removed because\n * the stack subscription triggers a re-scan.\n *\n * @returns Array of active button configurations\n */\nexport function useActiveControls(): ButtonConfig[] {\n const blocks = useSnapshotBlocks();\n const [version, setVersion] = useState(0);\n\n // Subscribe to controls memory changes on all blocks\n useEffect(() => {\n const unsubscribes: (() => void)[] = [];\n\n for (const block of blocks) {\n const controlsLocs = block.getMemoryByTag('controls');\n for (const loc of controlsLocs) {\n const unsub = loc.subscribe(() => {\n setVersion(v => v + 1);\n });\n unsubscribes.push(unsub);\n }\n }\n\n return () => {\n unsubscribes.forEach(fn => fn());\n };\n }, [blocks]);\n\n return useMemo(() => {\n const pinned: ButtonConfig[] = [];\n const topButtons: ButtonConfig[] = [];\n\n for (let i = 0; i < blocks.length; i++) {\n const controlsLocs = blocks[i].getMemoryByTag('controls');\n if (controlsLocs.length === 0) continue;\n\n // Each metrics in the controls location IS a button config\n const buttons = controlsLocs[0].metrics\n .map(f => f.value as ButtonConfig)\n .filter(Boolean);\n const isTop = i === blocks.length - 1;\n\n for (const btn of buttons) {\n if (btn.isPinned) {\n pinned.push(btn);\n } else if (isTop) {\n topButtons.push(btn);\n }\n }\n }\n\n // Deduplicate by ID: pinned take precedence\n const seen = new Set<string>();\n const result: ButtonConfig[] = [];\n\n for (const btn of pinned) {\n if (!seen.has(btn.id)) {\n seen.add(btn.id);\n result.push(btn);\n }\n }\n for (const btn of topButtons) {\n if (!seen.has(btn.id)) {\n seen.add(btn.id);\n result.push(btn);\n }\n }\n\n return result;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blocks, version]);\n}\n\n// ============================================================================\n// Stack-Driven Fragment Source Hook\n// ============================================================================\n\n/**\n * Represents a metrics source on the stack with layout context.\n *\n * Extends the generic FragmentSourceEntry with a block reference for\n * runtime-specific scenarios (accessing memory, dispatching events).\n */\nexport interface StackFragmentEntry extends FragmentSourceEntry {\n /** The owning runtime block */\n block: IRuntimeBlock;\n}\n\n/**\n * Hook that converts the runtime stack into an array of IMetricSource entries.\n *\n * Each block on the stack with a `'metric:display'` memory entry produces a\n * `StackFragmentEntry` containing the `IMetricSource` and layout context\n * (depth, leaf status, label).\n *\n * This is the preferred way for UI components to access metrics data from\n * the stack, replacing `useStackDisplayItems()` which returns `IDisplayItem[]`.\n *\n * @returns Array of stack metrics entries, or undefined if no blocks on stack\n *\n * @example\n * ```tsx\n * function StackView() {\n * const entries = useStackFragmentSources();\n * if (!entries) return null;\n *\n * return entries.map(entry => (\n * <MetricVisualizer\n * key={entry.source.id}\n * metrics={entry.source.getDisplayMetrics()}\n * />\n * ));\n * }\n * ```\n */\nexport function useStackFragmentSources(): StackFragmentEntry[] | undefined {\n const blocks = useSnapshotBlocks();\n const [version, setVersion] = useState(0);\n\n // Subscribe to metrics:display memory changes on all blocks\n useEffect(() => {\n const unsubscribes: (() => void)[] = [];\n\n for (const block of blocks) {\n const displayLocs = block.getMemoryByTag('metric:display');\n for (const loc of displayLocs) {\n const unsub = loc.subscribe(() => {\n setVersion(v => v + 1);\n });\n unsubscribes.push(unsub);\n }\n }\n\n return () => {\n unsubscribes.forEach(fn => fn());\n };\n }, [blocks]);\n\n return useMemo(() => {\n if (blocks.length === 0) return undefined;\n\n const entries: StackFragmentEntry[] = [];\n const orderedBlocks = [...blocks].reverse();\n\n orderedBlocks.forEach((block, index) => {\n // Skip root blocks without meaningful labels\n if (block.blockType === 'Root' && !block.label) return;\n\n // Get all metrics:display locations from list-based memory\n const displayLocs = block.getMemoryByTag('metric:display');\n\n // Create fallback source for blocks without metrics:display memory\n let source: IMetricSource;\n if (displayLocs.length === 0) {\n // Fallback: create synthetic metrics source from block label\n if (!block.label) return; // Skip blocks with no display entry AND no label\n\n source = MetricContainer.empty(block.key.toString());\n } else {\n // Create an IMetricSource adapter from the memory locations\n const mergedMetrics = MetricContainer.empty(block.key.toString());\n for (const loc of displayLocs) mergedMetrics.merge(loc.metrics);\n source = mergedMetrics;\n }\n\n const isLeaf = index === orderedBlocks.length - 1;\n\n // Get raw metrics groups from list-based memory for multi-line display\n const metricGroups = displayLocs.length > 1\n ? displayLocs.map(loc => loc.metrics)\n : undefined;\n\n entries.push({\n source,\n block,\n depth: index,\n isLeaf,\n label: block.label,\n metricGroups,\n });\n });\n\n return entries.length > 0 ? entries : undefined;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blocks, version]);\n}\n\n// ============================================================================\n// Stack-Driven Display Rows Hook (List-Based Memory API)\n// ============================================================================\n\n/**\n * Represents a display entry with list-based memory access.\n *\n * Replaces IMetricSource adapter with direct IMetric[][] access.\n */\nexport interface StackDisplayEntry {\n /** The owning runtime block */\n block: IRuntimeBlock;\n /** Display rows - each row is an array of metrics */\n displayRows: MetricContainer[];\n /** Display label from block */\n label: string;\n /** Nesting depth for indentation (0 = root level) */\n depth: number;\n /** Whether this is the leaf (most active) entry */\n isLeaf: boolean;\n /** Raw metrics groups from block memory (for backward compatibility) */\n metricGroups?: readonly MetricContainer[];\n}\n\n/**\n * Hook that reads list-based memory directly from runtime stack blocks.\n *\n * This is the Phase 3 UI migration hook that consumes the list-based memory\n * API populated by Phase 2 behaviors. It subscribes to all 'metric:display'\n * memory locations and returns display rows without requiring IMetricSource\n * adapter layer.\n *\n * Each block can have multiple 'metric:display' memory locations (non-unique\n * tags in list-based API), where each location stores IMetric[]. These\n * are returned as displayRows: IMetric[][].\n *\n * @returns Array of stack display entries, or undefined if no blocks on stack\n *\n * @example\n * ```tsx\n * function StackView() {\n * const entries = useStackDisplayRows();\n * if (!entries) return null;\n *\n * return entries.map(entry => (\n * <div key={entry.block.key.toString()}>\n * {entry.displayRows.map((row, idx) => (\n * <MetricSourceRow key={idx} metrics={row} />\n * ))}\n * </div>\n * ));\n * }\n * ```\n */\nexport function useStackDisplayRows(): StackDisplayEntry[] | undefined {\n const blocks = useSnapshotBlocks();\n const [version, setVersion] = useState(0);\n\n // Subscribe to all metrics:display memory locations on all blocks\n useEffect(() => {\n const unsubscribes: (() => void)[] = [];\n\n for (const block of blocks) {\n const displayLocs = block.getMemoryByTag('metric:display');\n for (const loc of displayLocs) {\n const unsub = loc.subscribe(() => {\n setVersion(v => v + 1);\n });\n unsubscribes.push(unsub);\n }\n }\n\n return () => {\n unsubscribes.forEach(fn => fn());\n };\n }, [blocks]);\n\n return useMemo(() => {\n if (blocks.length === 0) return undefined;\n\n const entries: StackDisplayEntry[] = [];\n const orderedBlocks = [...blocks].reverse();\n\n orderedBlocks.forEach((block, index) => {\n // Skip root blocks without meaningful labels\n if (block.blockType === 'Root' && !block.label) return;\n\n // Get all metrics:display locations from list-based memory\n const displayLocs = block.getMemoryByTag('metric:display');\n\n // Convert locations to display rows\n const displayRows = displayLocs.map(loc => loc.metrics);\n\n // Skip blocks with no display data AND no label\n if (displayRows.length === 0 && !block.label) return;\n\n const isLeaf = index === orderedBlocks.length - 1;\n\n // Get raw metrics groups from list-based memory for multi-line display\n const metricGroups = displayLocs.length > 1\n ? displayLocs.map(loc => loc.metrics)\n : undefined;\n\n entries.push({\n block,\n displayRows,\n label: block.label,\n depth: index,\n isLeaf,\n metricGroups,\n });\n });\n\n return entries.length > 0 ? entries : undefined;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blocks, version]);\n}\n","import { useMemo, useState, useEffect } from 'react';\nimport { useScriptRuntime } from '../context/RuntimeContext';\nimport { TimeSpan } from '@bitcobblers/wod-wiki-core';\nimport { TimerState } from '../../runtime/memory/MemoryTypes';\nimport { calculateDuration } from '../../utils/timeUtils';\nimport { getRuntimeNowMs } from './runtimeNow';\n\n\n/**\n * Result from useTimerElapsed hook.\n */\nexport interface UseTimerElapsedResult {\n elapsed: number; // Total milliseconds across all spans\n isRunning: boolean; // Current running state\n timeSpans: readonly TimeSpan[]; // Array of start/stop pairs (number timestamps)\n}\n\n/**\n * Hook to calculate elapsed time for a timer block by key.\n * \n * This hook uses the unified TimerState from runtime memory to provide\n * real-time elapsed time calculation. It polls for updates when the timer\n * is running to show current time, but only subscribes to memory changes\n * for start/stop/pause/resume events.\n * \n * ## When to Use This Hook\n * \n * Use this hook when your component receives a `blockKey: string` from the\n * display stack system. This is the standard integration pattern for:\n * - ClockAnchor\n * - DigitalClock\n * - Display stack consumers\n * \n * ## When to Use useTimerDisplay Instead\n * \n * If you have direct access to an `IRuntimeBlock` reference (e.g., from\n * `runtime.stack.current()`), prefer `useTimerDisplay(block)` which provides\n * formatted time strings and richer display values.\n * \n * ## Features\n * - Zero polling for completed timers\n * - Subscription-based updates for state changes\n * - 60fps animation frame updates when running\n * \n * @param blockKey The block key to track elapsed time for\n * @returns Object with elapsed time, running state, and time spans\n * \n * @example\n * ```tsx\n * // Display stack integration (this hook)\n * function KeyBasedTimer({ blockKey }: { blockKey: string }) {\n * const { elapsed, isRunning } = useTimerElapsed(blockKey);\n * const formatted = formatMs(elapsed);\n * return <div className={isRunning ? 'running' : ''}>{formatted}</div>;\n * }\n * \n * // Direct block access (use useTimerDisplay instead)\n * function BlockTimer({ block }: { block: IRuntimeBlock }) {\n * const display = useTimerDisplay(block);\n * return <div>{display?.formatted}</div>;\n * }\n * ```\n */\nexport function useTimerElapsed(blockKey: string): UseTimerElapsedResult {\n const runtime = useScriptRuntime();\n \n // Find the block from the stack by key\n const block = useMemo(() => {\n return runtime.stack.blocks.find(b => b.key.toString() === blockKey);\n }, [runtime, blockKey]);\n \n // Subscribe to block-level timer memory\n const [timerState, setTimerState] = useState<TimerState | undefined>(() => {\n return block?.getMemoryByTag('time')[0]?.metrics[0]?.value as TimerState | undefined;\n });\n \n // Subscribe to stack changes to update block reference\n useEffect(() => {\n const unsubscribe = runtime.stack.subscribe(() => {\n const foundBlock = runtime.stack.blocks.find(b => b.key.toString() === blockKey);\n if (foundBlock) {\n const timerLoc = foundBlock.getMemoryByTag('time')[0];\n setTimerState(timerLoc?.metrics[0]?.value as TimerState | undefined);\n } else {\n setTimerState(undefined);\n }\n });\n return unsubscribe;\n }, [runtime, blockKey]);\n \n // Subscribe to timer memory changes on the block\n useEffect(() => {\n if (!block) {\n setTimerState(undefined);\n return;\n }\n \n const timerLoc = block.getMemoryByTag('time')[0];\n setTimerState(timerLoc?.metrics[0]?.value as TimerState | undefined);\n \n if (timerLoc) {\n const unsubscribe = timerLoc.subscribe((newFrags) => {\n setTimerState(newFrags[0]?.value as TimerState | undefined);\n });\n return unsubscribe;\n }\n }, [block]);\n\n // Extract spans from timer state\n const timeSpans = timerState?.spans || [];\n\n // Derive isRunning from spans - last span has no end time\n const isRunning = timeSpans.length > 0 && timeSpans[timeSpans.length - 1].ended === undefined;\n\n const [now, setNow] = useState(getRuntimeNowMs());\n\n // Calculate elapsed time using shared utility\n const elapsed = useMemo(() => {\n if (timeSpans.length === 0) return 0;\n return calculateDuration(timeSpans, now);\n }, [timeSpans, now]);\n\n // Animation frame for running timers\n useEffect(() => {\n if (!isRunning) return;\n\n let animationFrameId: number;\n\n const update = () => {\n setNow(getRuntimeNowMs());\n animationFrameId = requestAnimationFrame(update);\n };\n\n update(); // Initial update\n\n return () => cancelAnimationFrame(animationFrameId);\n }, [isRunning]);\n\n return {\n elapsed,\n isRunning,\n timeSpans\n };\n}\n","import React from 'react';\nimport { IRuntimeBlock } from '../../runtime/contracts/IRuntimeBlock';\nimport { useTimerDisplay, useRoundDisplay } from '../hooks/useBlockMemory';\nfunction cn(...classes: (string | boolean | undefined | null)[]) { return classes.filter(Boolean).join(\" \"); }\n\nexport interface BlockTimerDisplayProps {\n /** The runtime block to display timer for */\n block: IRuntimeBlock | undefined;\n /** Optional CSS class name */\n className?: string;\n /** Show round information if available */\n showRounds?: boolean;\n /** Compact mode for smaller displays */\n compact?: boolean;\n}\n\n/**\n * BlockTimerDisplay - Timer display using new behavior-based hooks.\n * \n * This component demonstrates the preferred pattern for displaying timer\n * state from runtime blocks using the new useTimerDisplay and useRoundDisplay\n * hooks.\n * \n * ## Features\n * - 60fps animation for running timers\n * - Automatic format detection (countdown vs countup)\n * - Round display integration\n * - Zero polling for completed timers\n * \n * ## Usage\n * \n * When you have direct access to an IRuntimeBlock (e.g., from the stack),\n * use this component:\n * \n * \\\\\\\tsx\n * const block = runtime.stack.current();\n * return <BlockTimerDisplay block={block} showRounds />;\n * \\\\\\\n * \n * For display stack integration where you only have a blockKey string,\n * continue using DigitalClock or ClockAnchor with useTimerElapsed.\n * \n * @see useTimerDisplay\n * @see useRoundDisplay\n */\nexport const BlockTimerDisplay: React.FC<BlockTimerDisplayProps> = ({\n block,\n className,\n showRounds = true,\n compact = false\n}) => {\n const timer = useTimerDisplay(block);\n const round = useRoundDisplay(block);\n\n if (!timer) {\n return (\n <div className={cn('flex items-center justify-center p-4 text-muted-foreground', className)}>\n No timer active\n </div>\n );\n }\n\n return (\n <div className={cn('flex flex-col items-center', className)}>\n {/* Timer Display */}\n <div className={cn(\n 'font-mono font-bold tabular-nums',\n compact ? 'text-3xl' : 'text-5xl md:text-6xl',\n timer.isRunning ? 'text-primary' : 'text-foreground'\n )}>\n {timer.formatted}\n </div>\n\n {/* Status Indicator */}\n <div className={cn(\n 'text-xs uppercase tracking-wider mt-2',\n timer.isRunning ? 'text-success' : 'text-muted-foreground'\n )}>\n {timer.isComplete ? 'Complete' : timer.isRunning ? 'Running' : 'Paused'}\n </div>\n\n {/* Elapsed/Remaining Info */}\n {timer.remaining !== undefined && timer.remaining > 0 && (\n <div className=\"text-sm text-muted-foreground mt-1\">\n {Math.ceil(timer.remaining / 1000)}s remaining\n </div>\n )}\n\n {/* Round Display */}\n {showRounds && round && (\n <div className={cn(\n 'mt-3 px-3 py-1 rounded-full text-sm',\n 'bg-secondary text-secondary-foreground'\n )}>\n {round.label}\n </div>\n )}\n </div>\n );\n};\n\nexport default BlockTimerDisplay;\n"]}
1
+ {"version":3,"sources":["../src/runtime/events/TickEvent.ts","../src/react/hooks/useRuntimeExecution.ts","../src/utils/timeUtils.ts","../src/react/hooks/useBlockMemory.ts","../src/react/context/RuntimeContext.tsx","../src/react/hooks/useOutputStatements.ts","../src/react/hooks/useMemorySubscription.ts","../src/react/hooks/useStackSnapshot.ts","../src/react/hooks/useNextPreview.ts","../src/react/hooks/useStackDisplay.ts","../src/react/hooks/useTimerElapsed.ts","../src/react/components/BlockTimerDisplay.tsx"],"names":["start","useState","useEffect","useMemo","MetricContainer","jsx"],"mappings":"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","file":"react.mjs","sourcesContent":["import { IEvent } from '../contracts/events/IEvent';\nimport { INowProvider, wallClockNow } from '../INowProvider';\n\n/**\n * Event emitted on each tick of the runtime clock (typically every ~20ms).\n * \n * Behaviors subscribe to `'tick'` to receive periodic updates:\n * - `TimerTickBehavior` — updates timer elapsed time\n * - `TimerEndingBehavior` — checks if countdown has expired\n * - `SoundCueBehavior` — triggers countdown audio cues\n * \n * Production entry point: `runtime.handle(new TickEvent())`\n */\nexport class TickEvent implements IEvent {\n readonly name: string = 'tick';\n readonly timestamp: Date;\n readonly data?: unknown;\n private static _counter = 0;\n constructor(data?: unknown, private readonly now: INowProvider = wallClockNow) {\n const nowMs = this.now.nowMs();\n TickEvent._counter++;\n this.timestamp = new Date(nowMs + TickEvent._counter);\n this.data = data;\n }\n}\n","/**\n * useRuntimeExecution - Custom React hook for managing ScriptRuntime execution\n * \n * Encapsulates execution logic, interval management, and lifecycle cleanup.\n * \n * Architecture Decision:\n * - Fixed 20ms tick rate (50 ticks per second)\n * - No speed control (removed for simplicity)\n * - Automatic cleanup on unmount\n * - Reusable across components\n */\n\nimport { useState, useCallback, useRef, useEffect, useMemo } from 'react';\nimport type { ScriptRuntime } from '../../runtime/ScriptRuntime';\nimport { TickEvent } from '../../runtime/events/TickEvent';\nimport { wallClockNow } from '../../runtime/INowProvider';\n\n/** Fixed execution tick rate in milliseconds (50 ticks per second) */\nconst EXECUTION_TICK_RATE_MS = 20;\n\n/**\n * Execution status states\n */\nexport type ExecutionStatus =\n | 'idle' // No runtime or not started\n | 'running' // Active execution with interval\n | 'paused' // Execution stopped but state preserved\n | 'completed' // Runtime finished successfully\n | 'error'; // Runtime encountered error\n\n/**\n * Return type for useRuntimeExecution hook\n */\nexport interface UseRuntimeExecutionReturn {\n status: ExecutionStatus;\n elapsedTime: number;\n stepCount: number;\n startTime: number | null;\n start: () => void;\n pause: () => void;\n stop: () => void;\n reset: () => void;\n step: () => void;\n}\n\n/**\n * Manages ScriptRuntime execution lifecycle with fixed 20ms tick rate\n * \n * @param runtime - The ScriptRuntime instance to execute (null if not ready)\n * @returns Object with execution state and control methods\n */\nexport const useRuntimeExecution = (\n runtime: ScriptRuntime | null\n): UseRuntimeExecutionReturn => {\n const [status, setStatus] = useState<ExecutionStatus>('idle');\n const [elapsedTime, setElapsedTime] = useState(0);\n const [stepCount, setStepCount] = useState(0);\n const [startTime, setStartTime] = useState<number | null>(null);\n\n const intervalRef = useRef<NodeJS.Timeout | null>(null);\n const startTimeRef = useRef<number | null>(null);\n\n /**\n * Stops continuous execution without clearing metrics.\n * Preserves elapsedTime and startTime for completion reporting.\n */\n const stop = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n setStatus('idle');\n }, []);\n\n /**\n * Executes a single runtime step\n * Called by interval timer and step() method\n */\n const executeStep = useCallback(() => {\n if (!runtime) return;\n\n try {\n // Use TickEvent for periodic updates instead of NextEvent\n const tickEvent = new TickEvent(undefined, runtime.nowProvider);\n runtime.handle(tickEvent);\n setStepCount(prev => prev + 1);\n } catch {\n stop();\n setStatus('error');\n }\n }, [runtime, stop]);\n\n /**\n * Reactive completion detection\n * Ensures 'completed' status is set immediately when the stack becomes empty,\n * regardless of whether the playback loop is active. A run advanced purely\n * by Next events never enters 'running' — a drained stack still means done.\n */\n useEffect(() => {\n if (!runtime) return;\n\n // Subscribe to stack snapshots to detect when everything is finished\n const unsubscribe = runtime.subscribeToStack((snapshot) => {\n if (snapshot.blocks.length === 0 && status !== 'completed') {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n setStatus('completed');\n }\n });\n\n return unsubscribe;\n }, [runtime, status]);\n\n // Cleanup interval on unmount\n useEffect(() => {\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, []);\n\n /**\n * Starts continuous execution at fixed 20ms tick rate\n */\n const start = useCallback(() => {\n if (!runtime) {\n console.warn('useRuntimeExecution: Cannot start execution without a runtime');\n return;\n }\n if (status === 'running') {\n console.warn('useRuntimeExecution: Runtime is already running');\n return;\n }\n\n // Emit timer:resume event when resuming from paused state\n if (status === 'paused') {\n runtime.handle({\n name: 'timer:resume',\n timestamp: new Date(),\n data: {}\n });\n }\n\n setStatus('running');\n const now = runtime?.nowProvider?.nowMs() ?? wallClockNow.nowMs();\n startTimeRef.current = now - elapsedTime; // Resume from paused time\n\n // Only set startTime on first start (not resume)\n if (elapsedTime === 0) {\n setStartTime(now);\n }\n\n // Start interval for subsequent steps\n intervalRef.current = setInterval(executeStep, EXECUTION_TICK_RATE_MS);\n }, [runtime, status, elapsedTime, executeStep]);\n\n /**\n * Pauses execution, preserving state\n */\n const pause = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n\n // Emit timer:pause event to update timer memory spans\n if (runtime) {\n runtime.handle({\n name: 'timer:pause',\n timestamp: new Date(),\n data: {}\n });\n }\n\n setStatus('paused');\n }, [runtime]);\n\n /**\n * Resets execution state and clears all metrics.\n * Used for \"replay\" functionality or starting fresh.\n */\n const reset = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n setElapsedTime(0);\n setStepCount(0);\n setStatus('idle');\n setStartTime(null);\n startTimeRef.current = null;\n\n // Reset runtime if available\n if (runtime) {\n // TODO: Add runtime.reset() method when available\n }\n }, [runtime]);\n\n /**\n * Executes a single step manually\n * Used for debugging and step-through execution\n */\n const step = useCallback(() => {\n if (!runtime || status === 'running') {\n return;\n }\n\n setStatus('paused');\n executeStep();\n }, [runtime, status, executeStep]);\n\n /**\n * Update elapsed time while running\n */\n useEffect(() => {\n if (status !== 'running' || !startTimeRef.current) return;\n\n const updateInterval = setInterval(() => {\n setElapsedTime((runtime?.nowProvider?.nowMs() ?? wallClockNow.nowMs()) - startTimeRef.current!);\n }, 100); // Update display every 100ms\n\n return () => clearInterval(updateInterval);\n }, [status, runtime?.nowProvider]);\n\n /**\n * Cleanup on unmount - critical for preventing memory leaks\n */\n useEffect(() => {\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, []);\n\n /**\n * Reset state when runtime changes\n */\n useEffect(() => {\n reset();\n }, [runtime, reset]);\n\n return useMemo(() => ({\n status,\n elapsedTime,\n stepCount,\n startTime,\n start,\n pause,\n stop,\n reset,\n step\n }), [status, elapsedTime, stepCount, startTime, start, pause, stop, reset, step]);\n};\n","import { TimeSpan } from '@bitcobblers/wod-wiki-core';\n\n/**\n * Formats a **TimeStamp** (Date or epoch ms) into HH:MM:SS format.\n * Returns 'running' if date is undefined.\n *\n * @see docs/architecture/time-terminology.md — TimeStamp is the system time\n * (Date.now()) when a message is logged.\n */\nexport const formatTimestamp = (date?: Date | number): string => {\n if (!date) return 'running';\n\n const dateObj = typeof date === 'number' ? new Date(date) : date;\n\n return dateObj.toLocaleTimeString('en-US', {\n hour12: false,\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit'\n });\n};\n\n/**\n * Format milliseconds to MM:SS.ms display\n * e.g., 65432 -> \"01:05.43\"\n */\nexport const formatTime = (ms: number): string => {\n if (ms === undefined || ms === null || !Number.isFinite(ms)) {\n return '--:--.--';\n }\n const isNegative = ms < 0;\n const absMs = Math.abs(ms);\n const totalSeconds = Math.floor(absMs / 1000);\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n const milliseconds = Math.floor((absMs % 1000) / 10);\n const sign = isNegative ? '-' : '';\n return `${sign}${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(2, '0')}`;\n};\n\n/**\n * Calculates **Elapsed** (sum of span durations) from an array of time spans.\n *\n * Equivalent to `calculateElapsed()` in `src/runtime/time/calculateElapsed.ts`.\n * Prefer the runtime version for new code; this exists for component-layer compat.\n *\n * @see docs/architecture/time-terminology.md\n */\nexport const calculateDuration = (spans: ReadonlyArray<TimeSpan | { started: number, ended?: number } | { start: number | Date, stop?: number | Date }>, now: number): number => {\n if (!spans || !Array.isArray(spans)) return 0;\n return spans.reduce((total, span) => {\n // Handle canonical TimeSpan or raw objects with 'started'/'ended'\n if ('started' in span) {\n const start = span.started;\n const end = span.ended ?? now;\n return total + Math.max(0, end - start);\n }\n\n // Handle legacy objects with 'start'/'stop'\n const start = span.start instanceof Date ? span.start.getTime() : (span.start || 0);\n const stop = span.stop ? (span.stop instanceof Date ? span.stop.getTime() : span.stop) : now;\n return total + Math.max(0, stop - start);\n }, 0);\n};\n\n/**\n * Rounds a duration in seconds to the nearest 0.1s.\n */\nexport const roundToTenth = (seconds: number): number => {\n if (!Number.isFinite(seconds)) return 0;\n return Math.round(seconds * 10) / 10;\n};\n","import { useState, useEffect, useMemo } from 'react';\nimport { IRuntimeBlock } from '../../runtime/contracts/IRuntimeBlock';\nimport { MemoryType, MemoryValueOf, TimerState, RoundState, DisplayState } from '../../runtime/memory/MemoryTypes';\nimport { IMetricSource } from '@bitcobblers/wod-wiki-core';\nimport { MetricContainer } from '@bitcobblers/wod-wiki-core';\nimport { formatDurationSmart } from '../../utils/formatTime';\nimport { calculateDuration } from '../../utils/timeUtils';\nimport { MemoryTag } from '../../runtime/memory/MemoryLocation';\n\n/**\n * React hook to subscribe to a specific memory type on a block.\n * \n * Automatically subscribes when the block or type changes, and\n * unsubscribes on cleanup. Returns the current value and updates\n * reactively when behaviors modify the memory.\n * \n * @param block The runtime block to subscribe to (can be undefined)\n * @param type The memory type to subscribe to\n * @returns The current memory value or undefined\n * \n * @example\n * ```tsx\n * function TimerComponent({ block }: { block: IRuntimeBlock }) {\n * const timerState = useBlockMemory(block, 'time');\n * \n * if (!timerState) return null;\n * \n * return <div>Direction: {timerState.direction}</div>;\n * }\n * ```\n */\nexport function useBlockMemory<T extends MemoryType>(\n block: IRuntimeBlock | undefined,\n type: T\n): MemoryValueOf<T> | undefined {\n const tag = type as MemoryTag;\n const getVal = (): MemoryValueOf<T> | undefined => {\n const loc = block?.getMemoryByTag(tag)[0];\n if (!loc) return undefined;\n // 'round' tag: the metrics itself IS the value (CurrentRoundMetric satisfies RoundState)\n if (tag === 'round') return loc.metrics[0] as unknown as MemoryValueOf<T>;\n return loc.metrics[0]?.value as MemoryValueOf<T> | undefined;\n };\n const [value, setValue] = useState<MemoryValueOf<T> | undefined>(getVal);\n\n useEffect(() => {\n if (!block) {\n setValue(undefined);\n return;\n }\n\n // Get initial value\n setValue(getVal());\n\n // Subscribe to changes\n const loc = block.getMemoryByTag(tag)[0];\n if (loc) {\n const unsubscribe = loc.subscribe((newFrags) => {\n if (tag === 'round') {\n setValue(newFrags[0] as unknown as MemoryValueOf<T> | undefined);\n } else {\n setValue(newFrags[0]?.value as MemoryValueOf<T> | undefined);\n }\n });\n return unsubscribe;\n }\n\n return undefined;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [block, type]);\n\n return value;\n}\n\n/**\n * Hook for subscribing to timer state from a block.\n * \n * @param block The runtime block\n * @returns The current TimerState or undefined\n */\nexport function useTimerState(block: IRuntimeBlock | undefined): TimerState | undefined {\n return useBlockMemory(block, 'time');\n}\n\n/**\n * Hook for subscribing to round state from a block.\n * \n * @param block The runtime block\n * @returns The current RoundState or undefined\n */\nexport function useRoundState(block: IRuntimeBlock | undefined): RoundState | undefined {\n return useBlockMemory(block, 'round');\n}\n\n/**\n * Hook for subscribing to display state from a block.\n * \n * @param block The runtime block\n * @returns The current DisplayState or undefined\n */\nexport function useDisplayState(block: IRuntimeBlock | undefined): DisplayState | undefined {\n return useBlockMemory(block, 'display');\n}\n\n/**\n * Derived timer display values for UI rendering.\n */\nexport interface TimerDisplayValues {\n /** Total elapsed time in milliseconds */\n elapsed: number;\n /** Remaining time in milliseconds (for countdowns) */\n remaining: number | undefined;\n /** Whether the timer is currently running (not paused) */\n isRunning: boolean;\n /** Timer direction */\n direction: 'up' | 'down';\n /** Whether the timer has completed (countdown reached 0) */\n isComplete: boolean;\n /** Formatted time string (MM:SS or HH:MM:SS) */\n formatted: string;\n}\n\n/**\n * Hook for derived timer display values.\n * \n * Computes elapsed time, remaining time, and formatted display strings\n * from the raw timer state. Uses requestAnimationFrame for smooth 60fps\n * updates when the timer is running.\n * \n * Consolidated with useTimerElapsed - both now share calculateDuration utility.\n * \n * @param block The runtime block\n * @returns Derived timer display values or null if no timer\n * \n * @example\n * ```tsx\n * function TimerDisplay({ block }: { block: IRuntimeBlock }) {\n * const display = useTimerDisplay(block);\n * \n * if (!display) return <div>No timer</div>;\n * \n * return (\n * <div>\n * <span>{display.formatted}</span>\n * {display.direction === 'down' && (\n * <span>{formatTime(display.remaining ?? 0)} remaining</span>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useTimerDisplay(block: IRuntimeBlock | undefined): TimerDisplayValues | null {\n const timer = useTimerState(block);\n\n // Get the sender's clock time if available (Chromecast receiver), otherwise use local clock\n // We check for the global window property set by the receiver during Chromecast sessions\n const getNow = (): number => {\n // Check if we're in a Chromecast receiver context\n if (typeof (window as unknown as { __chromecast_senderClockTimeMs?: unknown }).__chromecast_senderClockTimeMs === 'function') {\n return (window as unknown as { __chromecast_senderClockTimeMs: () => number }).__chromecast_senderClockTimeMs();\n }\n return Date.now();\n };\n\n const [now, setNow] = useState(getNow());\n\n // Determine if timer is running (last span has no end time)\n const isRunning = useMemo(() => {\n if (!timer || timer.spans.length === 0) return false;\n const lastSpan = timer.spans[timer.spans.length - 1];\n return lastSpan.ended === undefined;\n }, [timer]);\n\n // Animation frame loop for smooth updates when running\n useEffect(() => {\n if (!isRunning) return;\n\n let animationFrameId: number;\n let running = true;\n\n const tick = () => {\n if (running) {\n setNow(getNow());\n animationFrameId = requestAnimationFrame(tick);\n }\n };\n\n animationFrameId = requestAnimationFrame(tick);\n\n return () => {\n running = false;\n cancelAnimationFrame(animationFrameId);\n };\n }, [isRunning]);\n\n // Compute derived values using shared utilities\n return useMemo(() => {\n if (!timer) return null;\n\n const elapsed = calculateDuration(timer.spans, now);\n const remaining = timer.durationMs !== undefined\n ? Math.max(0, timer.durationMs - elapsed)\n : undefined;\n const isComplete = timer.durationMs !== undefined && elapsed >= timer.durationMs;\n\n // Format based on direction\n const displayMs = timer.direction === 'down' && remaining !== undefined\n ? remaining\n : elapsed;\n\n return {\n elapsed,\n remaining,\n isRunning,\n direction: timer.direction,\n isComplete,\n formatted: formatDurationSmart(displayMs)\n };\n }, [timer, now, isRunning]);\n}\n\n/**\n * Derived round display values for UI rendering.\n */\nexport interface RoundDisplayValues {\n /** Current round number */\n current: number;\n /** Total rounds (undefined for unbounded) */\n total: number | undefined;\n /** Formatted round label (e.g., \"Round 3 of 5\") */\n label: string;\n /** Progress as a fraction 0-1 (undefined for unbounded) */\n progress: number | undefined;\n}\n\n/**\n * Hook for derived round display values.\n * \n * @param block The runtime block\n * @returns Derived round display values or null if no rounds\n */\nexport function useRoundDisplay(block: IRuntimeBlock | undefined): RoundDisplayValues | null {\n const round = useRoundState(block);\n const display = useDisplayState(block);\n\n return useMemo(() => {\n if (!round) return null;\n\n const label = display?.roundDisplay ??\n (round.total !== undefined\n ? `Round ${round.current} of ${round.total}`\n : `Round ${round.current}`);\n\n const progress = round.total !== undefined\n ? Math.min(1, (round.current - 1) / round.total)\n : undefined;\n\n return {\n current: round.current,\n total: round.total,\n label,\n progress\n };\n }, [round, display?.roundDisplay]);\n}\n\n/**\n * Hook for subscribing to the display metrics source from a block.\n *\n * Returns the `IMetricSource` interface from the block's `'metric:display'`\n * memory entry (`DisplayMetricMemory`). The returned source provides\n * precedence-resolved metric that update reactively when the underlying\n * metric memory or runtime state changes.\n *\n * This is the primary hook for UI components that need to render metrics\n * from a runtime block with proper origin precedence (user > runtime > compiler > parser).\n *\n * @param block The runtime block (can be undefined)\n * @returns The IMetricSource for the block, or undefined if no metrics:display memory\n *\n * @example\n * ```tsx\n * function BlockView({ block }: { block: IRuntimeBlock }) {\n * const source = useFragmentSource(block);\n * if (!source) return null;\n *\n * const metrics = source.getDisplayMetrics();\n * return <MetricVisualizer metric={metric} />;\n * }\n * ```\n */\nexport function useFragmentSource(block: IRuntimeBlock | undefined): IMetricSource | undefined {\n const [version, setVersion] = useState(0);\n\n useEffect(() => {\n if (!block) return;\n const loc = block.getMemoryByTag('metric:display')[0];\n if (!loc) return;\n return loc.subscribe(() => setVersion(v => v + 1));\n }, [block]);\n\n return useMemo(() => {\n if (!block) return undefined;\n const locs = block.getMemoryByTag('metric:display');\n if (locs.length === 0) return undefined;\n const allFragments = MetricContainer.empty(block.key.toString());\n for (const loc of locs) allFragments.merge(loc.metrics);\n return allFragments;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [block, version]);\n}\n","import { createContext, useContext, ReactNode } from 'react';\nimport { IScriptRuntime } from '../../runtime/contracts/IScriptRuntime';\n\n/**\n * React Context for providing IScriptRuntime to components.\n * \n * This is a simple dependency injection context — it passes a pre-created\n * IScriptRuntime instance to children. No lifecycle management.\n * \n * For lifecycle management (creation, disposal, error tracking),\n * use RuntimeLifecycleProvider from '@/contexts/RuntimeLifecycleProvider' instead.\n * \n * Components can use useScriptRuntime() to access runtime memory and state.\n */\nconst ScriptRuntimeContext = createContext<IScriptRuntime | undefined>(undefined);\n\nexport interface ScriptRuntimeProviderProps {\n runtime: IScriptRuntime;\n children: ReactNode;\n}\n\n/**\n * Provides IScriptRuntime context to child components.\n * Must wrap any components that need access to runtime memory or state.\n * \n * @example\n * ```tsx\n * const runtime = new ScriptRuntime();\n * const script = parseWorkout('For Time: 30 Jumping Jacks');\n * runtime.execute(script);\n * \n * return (\n * <ScriptRuntimeProvider runtime={runtime}>\n * <ClockAnchor blockKey=\"block-001\" />\n * </ScriptRuntimeProvider>\n * );\n * ```\n */\nexport const ScriptRuntimeProvider: React.FC<ScriptRuntimeProviderProps> = ({\n runtime,\n children\n}) => {\n return (\n <ScriptRuntimeContext.Provider value={runtime}>\n {children}\n </ScriptRuntimeContext.Provider>\n );\n};\n\n/**\n * Hook to access the IScriptRuntime from context.\n * Must be used within a ScriptRuntimeProvider.\n * \n * @throws Error if called outside ScriptRuntimeProvider\n * @returns IScriptRuntime instance\n */\nexport function useScriptRuntime(): IScriptRuntime {\n const runtime = useContext(ScriptRuntimeContext);\n if (!runtime) {\n throw new Error('useScriptRuntime must be used within ScriptRuntimeProvider');\n }\n return runtime;\n}\n\n","import { useState, useEffect, useMemo } from 'react';\nimport { useScriptRuntime } from '../context/RuntimeContext';\nimport { IScriptRuntime } from '../../runtime/contracts/IScriptRuntime';\nimport { IOutputStatement } from '@bitcobblers/wod-wiki-core';\n\n/**\n * Data structure returned by useOutputStatements hook\n * Consolidated from separate implementations in runtime/hooks and clock/hooks\n */\nexport interface OutputStatementsData {\n /** All output statements sorted by start time */\n outputs: IOutputStatement[];\n /** Outputs indexed by ID for quick lookup */\n byId: Map<number, IOutputStatement>;\n /** Outputs indexed by sourceBlockKey */\n byBlockKey: Map<string, IOutputStatement[]>;\n /** Segment outputs (timed execution segments) */\n segments: IOutputStatement[];\n /** Completion outputs */\n completions: IOutputStatement[];\n /** Metric outputs */\n metrics: IOutputStatement[];\n}\n\n/**\n * React hook that subscribes to output statement updates from a runtime.\n * \n * Unified hook that works with or without a runtime parameter:\n * - useOutputStatements() - uses runtime from context\n * - useOutputStatements(runtime) - uses provided runtime parameter\n * \n * Returns enriched OutputStatementsData with indexed maps for performance.\n * Consolidated from separate runtime/hooks and clock/hooks implementations.\n *\n * @param runtime Optional runtime instance. If not provided, uses context.\n * @returns OutputStatementsData containing outputs and indexed maps\n *\n * @example\n * ```tsx\n * // With context (default)\n * const data = useOutputStatements();\n * \n * // With explicit runtime parameter\n * const data = useOutputStatements(myRuntime);\n * ```\n */\nexport function useOutputStatements(runtime?: IScriptRuntime | null): OutputStatementsData {\n // Always call useScriptRuntime (satisfies Rules of Hooks — no conditional calls).\n // Use the explicit runtime if provided, otherwise fall back to context.\n const contextRuntime = useScriptRuntime();\n const resolvedRuntime = (runtime !== undefined) ? runtime : contextRuntime;\n\n const [outputs, setOutputs] = useState<IOutputStatement[]>(() => {\n return resolvedRuntime?.getOutputStatements?.() ?? [];\n });\n\n useEffect(() => {\n if (!resolvedRuntime) {\n setOutputs([]);\n return;\n }\n\n // Initial load\n setOutputs(resolvedRuntime.getOutputStatements());\n\n // Subscribe to new output statements\n const unsubscribe = resolvedRuntime.subscribeToOutput?.(() => {\n setOutputs(resolvedRuntime.getOutputStatements());\n });\n\n return unsubscribe;\n }, [resolvedRuntime]);\n\n // Compute derived data (memoized for performance)\n const data = useMemo<OutputStatementsData>(() => {\n const byId = new Map<number, IOutputStatement>();\n const byBlockKey = new Map<string, IOutputStatement[]>();\n const segments: IOutputStatement[] = [];\n const metrics: IOutputStatement[] = [];\n\n for (const output of outputs) {\n // Index by ID\n byId.set(output.id, output);\n\n // Index by block key\n const blockOutputs = byBlockKey.get(output.sourceBlockKey) ?? [];\n blockOutputs.push(output);\n byBlockKey.set(output.sourceBlockKey, blockOutputs);\n\n // Categorize by type\n switch (output.outputType) {\n case 'segment':\n segments.push(output);\n break;\n case 'metric':\n metrics.push(output);\n break;\n }\n }\n // 'completion' is folded into segment outputs (completionReason field);\n // surface completed-block segments as the completions collection.\n const completions = segments.filter(s => s.completionReason !== undefined);\n\n return { outputs, byId, byBlockKey, segments, completions, metrics };\n }, [outputs]);\n\n return data;\n}\n\n/**\n * Live Tier-2 (running-total) snapshot for display during an active session.\n *\n * Returns the analytics engine's current projection set — totals like total\n * volume / distance / load, recomputed per segment. These values are ephemeral:\n * they are shown live but never persisted; only the finalized values reach the\n * records after the WOD completes. Reuses the output subscription as its change\n * signal (the snapshot is replaced, not appended).\n */\nexport function useLiveAnalytics(runtime?: IScriptRuntime | null): IOutputStatement[] {\n const contextRuntime = useScriptRuntime();\n const resolvedRuntime = runtime !== undefined ? runtime : contextRuntime;\n\n const [analytics, setAnalytics] = useState<IOutputStatement[]>(() =>\n resolvedRuntime?.getLiveAnalytics?.() ?? [],\n );\n\n useEffect(() => {\n if (!resolvedRuntime) {\n setAnalytics([]);\n return;\n }\n setAnalytics(resolvedRuntime.getLiveAnalytics?.() ?? []);\n const unsubscribe = resolvedRuntime.subscribeToOutput?.(() => {\n setAnalytics(resolvedRuntime.getLiveAnalytics?.() ?? []);\n });\n return unsubscribe;\n }, [resolvedRuntime]);\n\n return analytics;\n}\n\n/**\n * Hook to get a specific output by ID\n */\nexport function useOutputStatement(\n runtime: IScriptRuntime | null,\n id: number | null\n): IOutputStatement | undefined {\n const { byId } = useOutputStatements(runtime);\n\n if (id === null) return undefined;\n return byId.get(id);\n}\n\n/**\n * Hook to get outputs for a specific block\n */\nexport function useBlockOutputs(\n runtime: IScriptRuntime | null,\n blockKey: string | null\n): IOutputStatement[] {\n const { byBlockKey } = useOutputStatements(runtime);\n\n if (!blockKey) return [];\n return byBlockKey.get(blockKey) ?? [];\n}\n\n/**\n * Hook to compute output hierarchy (parent-child relationships) based on stackLevel\n */\nexport function useOutputHierarchy(runtime: IScriptRuntime | null): Map<number, number> {\n const { outputs } = useOutputStatements(runtime);\n\n return useMemo(() => {\n const depthMap = new Map<number, number>();\n\n for (const output of outputs) {\n depthMap.set(output.id, output.stackLevel);\n }\n\n return depthMap;\n }, [outputs]);\n}\n","import { useState, useEffect } from 'react';\nimport { TypedMemoryReference } from '../../runtime/impl/TypedMemoryReference';\n\n/**\n * React hook for subscribing to memory reference changes.\n * \n * This hook automatically manages the subscription lifecycle:\n * - Subscribes when the component mounts or when memoryRef changes\n * - Unsubscribes when the component unmounts or when memoryRef changes\n * - Returns the current value and updates when the memory changes\n * \n * @param memoryRef The memory reference to subscribe to (can be undefined)\n * @returns The current value from the memory reference, or undefined if not available\n * \n * @example\n * ```tsx\n * const runtime = useScriptRuntime();\n * const timeSpansRef = runtime.memory.search({ \n * type: 'timer-time-spans', \n * ownerId: blockKey \n * })[0] as TypedMemoryReference<TimeSpan[]>;\n * \n * const timeSpans = useMemorySubscription(timeSpansRef);\n * ```\n */\nexport function useMemorySubscription<T>(\n memoryRef: TypedMemoryReference<T> | undefined\n): T | undefined {\n const [value, setValue] = useState<T | undefined>(memoryRef?.get());\n\n useEffect(() => {\n if (!memoryRef) {\n setValue(undefined);\n return;\n }\n\n // Set initial value\n setValue(memoryRef.get());\n\n // Subscribe to memory changes\n const unsubscribe = memoryRef.subscribe((newValue: T | undefined) => {\n setValue(newValue);\n });\n\n return unsubscribe;\n }, [memoryRef]);\n\n return value;\n}\n","import { useState, useEffect } from 'react';\nimport { useScriptRuntime } from '../context/RuntimeContext';\nimport { IRuntimeBlock } from '../../runtime/contracts/IRuntimeBlock';\nimport { StackSnapshot } from '../../runtime/contracts/IRuntimeStack';\n\n/**\n * Hook that subscribes to stack snapshots via `runtime.subscribeToStack()`.\n *\n * Returns the latest StackSnapshot, which contains:\n * - `blocks` — current stack (bottom-to-top)\n * - `affectedBlock` — the block that was pushed/popped\n * - `depth` — stack depth after the event\n * - `clockTime` — runtime clock time at event\n * - `type` — 'push' | 'pop' | 'clear' | 'initial'\n *\n * This is the primary hook for UI components that need to react to stack\n * structural changes. For high-frequency timer updates, subscribe to\n * block memory separately.\n *\n * @returns The latest StackSnapshot\n *\n * @example\n * ```tsx\n * function StackDisplay() {\n * const snapshot = useStackSnapshot();\n *\n * return (\n * <div>\n * <p>Depth: {snapshot.depth}</p>\n * {snapshot.blocks.map(block => (\n * <div key={block.key.toString()}>{block.label}</div>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useStackSnapshot(): StackSnapshot {\n const runtime = useScriptRuntime();\n const [snapshot, setSnapshot] = useState<StackSnapshot>({\n type: 'initial',\n blocks: runtime.stack.blocks,\n depth: runtime.stack.count,\n clockTime: runtime.clock.currentDate,\n });\n\n useEffect(() => {\n const unsubscribe = runtime.subscribeToStack((s) => {\n setSnapshot(s);\n });\n\n return unsubscribe;\n }, [runtime]);\n\n return snapshot;\n}\n\n/**\n * Hook that returns the current stack blocks from the most recent snapshot.\n * Convenience wrapper over useStackSnapshot().\n *\n * @returns Readonly array of blocks currently on the stack\n */\nexport function useSnapshotBlocks(): readonly IRuntimeBlock[] {\n const snapshot = useStackSnapshot();\n return snapshot.blocks;\n}\n\n/**\n * Hook that returns the current (top) block on the stack from the snapshot.\n * Returns undefined if the stack is empty.\n *\n * @returns The top block on the stack, or undefined if empty\n */\nexport function useSnapshotCurrentBlock(): IRuntimeBlock | undefined {\n const snapshot = useStackSnapshot();\n return snapshot.blocks.length > 0\n ? snapshot.blocks[snapshot.blocks.length - 1]\n : undefined;\n}\n","import { useState, useEffect, useMemo } from 'react';\nimport { useSnapshotBlocks } from './useStackSnapshot';\nimport { IRuntimeBlock } from '../../runtime/contracts/IRuntimeBlock';\nimport { MetricContainer } from '@bitcobblers/wod-wiki-core';\n\n/**\n * Result of the next-preview resolution.\n *\n * `metrics` contains the display metrics for the next child that\n * will be pushed by the deepest block on the stack that has a\n * `metrics:next` memory location.\n */\nexport interface NextPreview {\n /** Fragments describing the next child to be executed */\n metrics: MetricContainer;\n /** The block that owns this preview (the parent that will push the child) */\n block: IRuntimeBlock;\n}\n\n/**\n * Hook that resolves the \"Up Next\" preview from the runtime stack.\n *\n * Walks the stack from leaf (index 0) to root, finding the first block\n * that has a non-empty `metrics:next` memory location. This gives the\n * deepest (most specific) preview of what's coming next.\n *\n * The hook subscribes to `metrics:next` memory changes on all stack\n * blocks, so it re-renders when the preview updates (e.g., after a\n * child completes and the next child is queued).\n *\n * @returns NextPreview if any block has a next preview, or null\n *\n * @example\n * ```tsx\n * function UpNextCard() {\n * const preview = useNextPreview();\n * if (!preview) return <span>End of session</span>;\n *\n * return <MetricSourceRow metric={preview.metrics} />;\n * }\n * ```\n */\nexport function useNextPreview(): NextPreview | null {\n const blocks = useSnapshotBlocks();\n const [version, setVersion] = useState(0);\n\n // Subscribe to metrics:next memory changes on all blocks\n useEffect(() => {\n const unsubscribes: (() => void)[] = [];\n\n for (const block of blocks) {\n const nextLocs = block.getMemoryByTag('metric:next');\n for (const loc of nextLocs) {\n const unsub = loc.subscribe(() => {\n setVersion(v => v + 1);\n });\n unsubscribes.push(unsub);\n }\n }\n\n return () => {\n unsubscribes.forEach(fn => fn());\n };\n }, [blocks]);\n\n return useMemo(() => {\n // Walk leaf-to-root (blocks[0] is leaf, blocks[length-1] is root)\n for (const block of blocks) {\n const nextLocs = block.getMemoryByTag('metric:next');\n if (nextLocs.length === 0) continue;\n\n // Collect metrics from all metrics:next locations\n const metrics = MetricContainer.empty(block.key.toString());\n for (const loc of nextLocs) metrics.merge(loc.metrics);\n if (metrics.length > 0) {\n return { metrics, block };\n }\n }\n\n return null;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blocks, version]);\n}\n","import { useState, useEffect, useMemo } from 'react';\nimport { IRuntimeBlock } from '../../runtime/contracts/IRuntimeBlock';\nimport { TimerState, ButtonConfig } from '../../runtime/memory/MemoryTypes';\nimport { IMetricSource } from '@bitcobblers/wod-wiki-core';\nimport { MetricContainer } from '@bitcobblers/wod-wiki-core';\nexport type FragmentSourceStatus = 'pending' | 'active' | 'completed' | 'failed' | 'skipped';\n\nexport interface FragmentSourceEntry {\n source: IMetricSource;\n depth: number;\n isLeaf?: boolean;\n isHeader?: boolean;\n isLinked?: boolean;\n status?: FragmentSourceStatus;\n label?: string;\n metricGroups?: readonly MetricContainer[];\n}\nimport { useSnapshotBlocks } from './useStackSnapshot';\n\n// ============================================================================\n// Stack-Driven Timer Hooks\n// ============================================================================\n\n/**\n * Represents a timer on the stack with its owning block context.\n */\nexport interface StackTimerEntry {\n /** The block that owns this timer */\n block: IRuntimeBlock;\n /** The timer state from block memory */\n timer: TimerState;\n /** Whether this timer is pinned to the primary display */\n isPinned: boolean;\n}\n\n/**\n * Hook that scans the runtime stack for all blocks with timer memory.\n * Returns timer entries ordered from bottom of stack (index 0) to top.\n *\n * Timer data is read directly from block memory via \block.getMemory('time').\n * When a block is popped from the stack, its memory subscriptions are cleaned\n * up automatically, and the stack event triggers a re-render here.\n *\n * @returns Array of stack timer entries\n */\nexport function useStackTimers(): StackTimerEntry[] {\n const blocks = useSnapshotBlocks();\n const [version, setVersion] = useState(0);\n\n // Subscribe to timer memory changes on all blocks\n useEffect(() => {\n const unsubscribes: (() => void)[] = [];\n\n for (const block of blocks) {\n const timerLoc = block.getMemoryByTag('time')[0];\n if (timerLoc) {\n const unsub = timerLoc.subscribe(() => {\n setVersion(v => v + 1);\n });\n unsubscribes.push(unsub);\n }\n }\n\n return () => {\n unsubscribes.forEach(fn => fn());\n };\n }, [blocks]);\n\n return useMemo(() => {\n const entries: StackTimerEntry[] = [];\n\n for (const block of blocks) {\n const timerLoc = block.getMemoryByTag('time')[0];\n if (!timerLoc) continue;\n\n const timer = timerLoc.metrics[0]?.value as TimerState | undefined;\n if (!timer) continue;\n\n entries.push({\n block,\n timer,\n isPinned: timer.role === 'primary'\n });\n }\n\n return entries;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blocks, version]);\n}\n\n/**\n * Determines which timer should be displayed as the primary (big) timer.\n *\n * Pin Resolution Strategy:\n * 1. If the top-of-stack block is a Rest block, its timer is ALWAYS primary.\n * Rest countdowns should be front-and-center so users see time remaining.\n * 2. Otherwise, find the LOWEST (root-closest) block with role === 'primary'.\n * This lets parent blocks (AMRAP, EMOM) keep their timer pinned while\n * child exercises execute beneath them.\n * 3. If no pinned timer exists, use the leaf (top-of-stack) timer.\n *\n * @returns The primary timer entry, or undefined if no timers on stack\n */\nexport function usePrimaryTimer(): StackTimerEntry | undefined {\n const timers = useStackTimers();\n\n return useMemo(() => {\n if (timers.length === 0) return undefined;\n\n // Blocks are delivered in leaf→root order (index 0 = top of stack / leaf,\n // index last = bottom of stack / root). timers mirrors that order.\n const leaf = timers[0];\n\n // Rest blocks always become the pinned timer when pushed.\n // Their countdown should override any parent's pinned timer.\n if (leaf.block.blockType === 'Rest') return leaf;\n\n // Find the lowest (root-closest / LAST in array) pinned timer.\n // Parent blocks (AMRAP, EMOM) pin their countdown so it stays visible\n // while child exercises execute beneath them.\n // Search from root end (last index) toward leaf so the root-closest pinned\n // timer is returned when multiple blocks are pinned.\n const pinned = [...timers].reverse().find(t => t.isPinned);\n if (pinned) return pinned;\n\n // Fallback: leaf timer (top of stack = index 0 in leaf→root order)\n return leaf;\n }, [timers]);\n}\n\n/**\n * Returns all timers that are NOT the primary timer.\n * These are displayed as secondary/context timers in the UI.\n */\nexport function useSecondaryTimers(): StackTimerEntry[] {\n const timers = useStackTimers();\n const primary = usePrimaryTimer();\n\n return useMemo(() => {\n if (!primary) return [];\n return timers.filter(t => t.block !== primary.block);\n }, [timers, primary]);\n}\n\n// ============================================================================\n// Stack-Driven Controls Hook\n// ============================================================================\n\n/**\n * Aggregates control buttons from all blocks on the stack.\n *\n * Button Resolution Strategy:\n * - Collect pinned buttons (isPinned === true) from ALL stack levels\n * - Collect non-pinned buttons from the TOP block only\n * - Deduplicate by button ID (pinned take precedence)\n *\n * When a block is popped, its buttons are automatically removed because\n * the stack subscription triggers a re-scan.\n *\n * @returns Array of active button configurations\n */\nexport function useActiveControls(): ButtonConfig[] {\n const blocks = useSnapshotBlocks();\n const [version, setVersion] = useState(0);\n\n // Subscribe to controls memory changes on all blocks\n useEffect(() => {\n const unsubscribes: (() => void)[] = [];\n\n for (const block of blocks) {\n const controlsLocs = block.getMemoryByTag('controls');\n for (const loc of controlsLocs) {\n const unsub = loc.subscribe(() => {\n setVersion(v => v + 1);\n });\n unsubscribes.push(unsub);\n }\n }\n\n return () => {\n unsubscribes.forEach(fn => fn());\n };\n }, [blocks]);\n\n return useMemo(() => {\n const pinned: ButtonConfig[] = [];\n const topButtons: ButtonConfig[] = [];\n\n for (let i = 0; i < blocks.length; i++) {\n const controlsLocs = blocks[i].getMemoryByTag('controls');\n if (controlsLocs.length === 0) continue;\n\n // Each metrics in the controls location IS a button config\n const buttons = controlsLocs[0].metrics\n .map(f => f.value as ButtonConfig)\n .filter(Boolean);\n const isTop = i === blocks.length - 1;\n\n for (const btn of buttons) {\n if (btn.isPinned) {\n pinned.push(btn);\n } else if (isTop) {\n topButtons.push(btn);\n }\n }\n }\n\n // Deduplicate by ID: pinned take precedence\n const seen = new Set<string>();\n const result: ButtonConfig[] = [];\n\n for (const btn of pinned) {\n if (!seen.has(btn.id)) {\n seen.add(btn.id);\n result.push(btn);\n }\n }\n for (const btn of topButtons) {\n if (!seen.has(btn.id)) {\n seen.add(btn.id);\n result.push(btn);\n }\n }\n\n return result;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blocks, version]);\n}\n\n// ============================================================================\n// Stack-Driven Fragment Source Hook\n// ============================================================================\n\n/**\n * Represents a metrics source on the stack with layout context.\n *\n * Extends the generic FragmentSourceEntry with a block reference for\n * runtime-specific scenarios (accessing memory, dispatching events).\n */\nexport interface StackFragmentEntry extends FragmentSourceEntry {\n /** The owning runtime block */\n block: IRuntimeBlock;\n}\n\n/**\n * Hook that converts the runtime stack into an array of IMetricSource entries.\n *\n * Each block on the stack with a `'metric:display'` memory entry produces a\n * `StackFragmentEntry` containing the `IMetricSource` and layout context\n * (depth, leaf status, label).\n *\n * This is the preferred way for UI components to access metrics data from\n * the stack, replacing `useStackDisplayItems()` which returns `IDisplayItem[]`.\n *\n * @returns Array of stack metrics entries, or undefined if no blocks on stack\n *\n * @example\n * ```tsx\n * function StackView() {\n * const entries = useStackFragmentSources();\n * if (!entries) return null;\n *\n * return entries.map(entry => (\n * <MetricVisualizer\n * key={entry.source.id}\n * metrics={entry.source.getDisplayMetrics()}\n * />\n * ));\n * }\n * ```\n */\nexport function useStackFragmentSources(): StackFragmentEntry[] | undefined {\n const blocks = useSnapshotBlocks();\n const [version, setVersion] = useState(0);\n\n // Subscribe to metrics:display memory changes on all blocks\n useEffect(() => {\n const unsubscribes: (() => void)[] = [];\n\n for (const block of blocks) {\n const displayLocs = block.getMemoryByTag('metric:display');\n for (const loc of displayLocs) {\n const unsub = loc.subscribe(() => {\n setVersion(v => v + 1);\n });\n unsubscribes.push(unsub);\n }\n }\n\n return () => {\n unsubscribes.forEach(fn => fn());\n };\n }, [blocks]);\n\n return useMemo(() => {\n if (blocks.length === 0) return undefined;\n\n const entries: StackFragmentEntry[] = [];\n const orderedBlocks = [...blocks].reverse();\n\n orderedBlocks.forEach((block, index) => {\n // Skip root blocks without meaningful labels\n if (block.blockType === 'Root' && !block.label) return;\n\n // Get all metrics:display locations from list-based memory\n const displayLocs = block.getMemoryByTag('metric:display');\n\n // Create fallback source for blocks without metrics:display memory\n let source: IMetricSource;\n if (displayLocs.length === 0) {\n // Fallback: create synthetic metrics source from block label\n if (!block.label) return; // Skip blocks with no display entry AND no label\n\n source = MetricContainer.empty(block.key.toString());\n } else {\n // Create an IMetricSource adapter from the memory locations\n const mergedMetrics = MetricContainer.empty(block.key.toString());\n for (const loc of displayLocs) mergedMetrics.merge(loc.metrics);\n source = mergedMetrics;\n }\n\n const isLeaf = index === orderedBlocks.length - 1;\n\n // Get raw metrics groups from list-based memory for multi-line display\n const metricGroups = displayLocs.length > 1\n ? displayLocs.map(loc => loc.metrics)\n : undefined;\n\n entries.push({\n source,\n block,\n depth: index,\n isLeaf,\n label: block.label,\n metricGroups,\n });\n });\n\n return entries.length > 0 ? entries : undefined;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blocks, version]);\n}\n\n// ============================================================================\n// Stack-Driven Display Rows Hook (List-Based Memory API)\n// ============================================================================\n\n/**\n * Represents a display entry with list-based memory access.\n *\n * Replaces IMetricSource adapter with direct IMetric[][] access.\n */\nexport interface StackDisplayEntry {\n /** The owning runtime block */\n block: IRuntimeBlock;\n /** Display rows - each row is an array of metrics */\n displayRows: MetricContainer[];\n /** Display label from block */\n label: string;\n /** Nesting depth for indentation (0 = root level) */\n depth: number;\n /** Whether this is the leaf (most active) entry */\n isLeaf: boolean;\n /** Raw metrics groups from block memory (for backward compatibility) */\n metricGroups?: readonly MetricContainer[];\n}\n\n/**\n * Hook that reads list-based memory directly from runtime stack blocks.\n *\n * This is the Phase 3 UI migration hook that consumes the list-based memory\n * API populated by Phase 2 behaviors. It subscribes to all 'metric:display'\n * memory locations and returns display rows without requiring IMetricSource\n * adapter layer.\n *\n * Each block can have multiple 'metric:display' memory locations (non-unique\n * tags in list-based API), where each location stores IMetric[]. These\n * are returned as displayRows: IMetric[][].\n *\n * @returns Array of stack display entries, or undefined if no blocks on stack\n *\n * @example\n * ```tsx\n * function StackView() {\n * const entries = useStackDisplayRows();\n * if (!entries) return null;\n *\n * return entries.map(entry => (\n * <div key={entry.block.key.toString()}>\n * {entry.displayRows.map((row, idx) => (\n * <MetricSourceRow key={idx} metrics={row} />\n * ))}\n * </div>\n * ));\n * }\n * ```\n */\nexport function useStackDisplayRows(): StackDisplayEntry[] | undefined {\n const blocks = useSnapshotBlocks();\n const [version, setVersion] = useState(0);\n\n // Subscribe to all metrics:display memory locations on all blocks\n useEffect(() => {\n const unsubscribes: (() => void)[] = [];\n\n for (const block of blocks) {\n const displayLocs = block.getMemoryByTag('metric:display');\n for (const loc of displayLocs) {\n const unsub = loc.subscribe(() => {\n setVersion(v => v + 1);\n });\n unsubscribes.push(unsub);\n }\n }\n\n return () => {\n unsubscribes.forEach(fn => fn());\n };\n }, [blocks]);\n\n return useMemo(() => {\n if (blocks.length === 0) return undefined;\n\n const entries: StackDisplayEntry[] = [];\n const orderedBlocks = [...blocks].reverse();\n\n orderedBlocks.forEach((block, index) => {\n // Skip root blocks without meaningful labels\n if (block.blockType === 'Root' && !block.label) return;\n\n // Get all metrics:display locations from list-based memory\n const displayLocs = block.getMemoryByTag('metric:display');\n\n // Convert locations to display rows\n const displayRows = displayLocs.map(loc => loc.metrics);\n\n // Skip blocks with no display data AND no label\n if (displayRows.length === 0 && !block.label) return;\n\n const isLeaf = index === orderedBlocks.length - 1;\n\n // Get raw metrics groups from list-based memory for multi-line display\n const metricGroups = displayLocs.length > 1\n ? displayLocs.map(loc => loc.metrics)\n : undefined;\n\n entries.push({\n block,\n displayRows,\n label: block.label,\n depth: index,\n isLeaf,\n metricGroups,\n });\n });\n\n return entries.length > 0 ? entries : undefined;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blocks, version]);\n}\n","import { useMemo, useState, useEffect } from 'react';\nimport { useScriptRuntime } from '../context/RuntimeContext';\nimport { TimeSpan } from '@bitcobblers/wod-wiki-core';\nimport { TimerState } from '../../runtime/memory/MemoryTypes';\nimport { calculateDuration } from '../../utils/timeUtils';\nimport { getRuntimeNowMs } from './runtimeNow';\n\n\n/**\n * Result from useTimerElapsed hook.\n */\nexport interface UseTimerElapsedResult {\n elapsed: number; // Total milliseconds across all spans\n isRunning: boolean; // Current running state\n timeSpans: readonly TimeSpan[]; // Array of start/stop pairs (number timestamps)\n}\n\n/**\n * Hook to calculate elapsed time for a timer block by key.\n * \n * This hook uses the unified TimerState from runtime memory to provide\n * real-time elapsed time calculation. It polls for updates when the timer\n * is running to show current time, but only subscribes to memory changes\n * for start/stop/pause/resume events.\n * \n * ## When to Use This Hook\n * \n * Use this hook when your component receives a `blockKey: string` from the\n * display stack system. This is the standard integration pattern for:\n * - ClockAnchor\n * - DigitalClock\n * - Display stack consumers\n * \n * ## When to Use useTimerDisplay Instead\n * \n * If you have direct access to an `IRuntimeBlock` reference (e.g., from\n * `runtime.stack.current()`), prefer `useTimerDisplay(block)` which provides\n * formatted time strings and richer display values.\n * \n * ## Features\n * - Zero polling for completed timers\n * - Subscription-based updates for state changes\n * - 60fps animation frame updates when running\n * \n * @param blockKey The block key to track elapsed time for\n * @returns Object with elapsed time, running state, and time spans\n * \n * @example\n * ```tsx\n * // Display stack integration (this hook)\n * function KeyBasedTimer({ blockKey }: { blockKey: string }) {\n * const { elapsed, isRunning } = useTimerElapsed(blockKey);\n * const formatted = formatMs(elapsed);\n * return <div className={isRunning ? 'running' : ''}>{formatted}</div>;\n * }\n * \n * // Direct block access (use useTimerDisplay instead)\n * function BlockTimer({ block }: { block: IRuntimeBlock }) {\n * const display = useTimerDisplay(block);\n * return <div>{display?.formatted}</div>;\n * }\n * ```\n */\nexport function useTimerElapsed(blockKey: string): UseTimerElapsedResult {\n const runtime = useScriptRuntime();\n \n // Find the block from the stack by key\n const block = useMemo(() => {\n return runtime.stack.blocks.find(b => b.key.toString() === blockKey);\n }, [runtime, blockKey]);\n \n // Subscribe to block-level timer memory\n const [timerState, setTimerState] = useState<TimerState | undefined>(() => {\n return block?.getMemoryByTag('time')[0]?.metrics[0]?.value as TimerState | undefined;\n });\n \n // Subscribe to stack changes to update block reference\n useEffect(() => {\n const unsubscribe = runtime.stack.subscribe(() => {\n const foundBlock = runtime.stack.blocks.find(b => b.key.toString() === blockKey);\n if (foundBlock) {\n const timerLoc = foundBlock.getMemoryByTag('time')[0];\n setTimerState(timerLoc?.metrics[0]?.value as TimerState | undefined);\n } else {\n setTimerState(undefined);\n }\n });\n return unsubscribe;\n }, [runtime, blockKey]);\n \n // Subscribe to timer memory changes on the block\n useEffect(() => {\n if (!block) {\n setTimerState(undefined);\n return;\n }\n \n const timerLoc = block.getMemoryByTag('time')[0];\n setTimerState(timerLoc?.metrics[0]?.value as TimerState | undefined);\n \n if (timerLoc) {\n const unsubscribe = timerLoc.subscribe((newFrags) => {\n setTimerState(newFrags[0]?.value as TimerState | undefined);\n });\n return unsubscribe;\n }\n }, [block]);\n\n // Extract spans from timer state\n const timeSpans = useMemo(() => timerState?.spans ?? [], [timerState]);\n\n // Derive isRunning from spans - last span has no end time\n const isRunning = timeSpans.length > 0 && timeSpans[timeSpans.length - 1].ended === undefined;\n\n const [now, setNow] = useState(getRuntimeNowMs());\n\n // Calculate elapsed time using shared utility\n const elapsed = useMemo(() => {\n if (timeSpans.length === 0) return 0;\n return calculateDuration(timeSpans, now);\n }, [timeSpans, now]);\n\n // Animation frame for running timers\n useEffect(() => {\n if (!isRunning) return;\n\n let animationFrameId: number;\n\n const update = () => {\n setNow(getRuntimeNowMs());\n animationFrameId = requestAnimationFrame(update);\n };\n\n update(); // Initial update\n\n return () => cancelAnimationFrame(animationFrameId);\n }, [isRunning]);\n\n return {\n elapsed,\n isRunning,\n timeSpans\n };\n}\n","import React from 'react';\nimport { IRuntimeBlock } from '../../runtime/contracts/IRuntimeBlock';\nimport { useTimerDisplay, useRoundDisplay } from '../hooks/useBlockMemory';\nfunction cn(...classes: (string | boolean | undefined | null)[]) { return classes.filter(Boolean).join(\" \"); }\n\nexport interface BlockTimerDisplayProps {\n /** The runtime block to display timer for */\n block: IRuntimeBlock | undefined;\n /** Optional CSS class name */\n className?: string;\n /** Show round information if available */\n showRounds?: boolean;\n /** Compact mode for smaller displays */\n compact?: boolean;\n}\n\n/**\n * BlockTimerDisplay - Timer display using new behavior-based hooks.\n * \n * This component demonstrates the preferred pattern for displaying timer\n * state from runtime blocks using the new useTimerDisplay and useRoundDisplay\n * hooks.\n * \n * ## Features\n * - 60fps animation for running timers\n * - Automatic format detection (countdown vs countup)\n * - Round display integration\n * - Zero polling for completed timers\n * \n * ## Usage\n * \n * When you have direct access to an IRuntimeBlock (e.g., from the stack),\n * use this component:\n * \n * \\\\\\\tsx\n * const block = runtime.stack.current();\n * return <BlockTimerDisplay block={block} showRounds />;\n * \\\\\\\n * \n * For display stack integration where you only have a blockKey string,\n * continue using DigitalClock or ClockAnchor with useTimerElapsed.\n * \n * @see useTimerDisplay\n * @see useRoundDisplay\n */\nexport const BlockTimerDisplay: React.FC<BlockTimerDisplayProps> = ({\n block,\n className,\n showRounds = true,\n compact = false\n}) => {\n const timer = useTimerDisplay(block);\n const round = useRoundDisplay(block);\n\n if (!timer) {\n return (\n <div className={cn('flex items-center justify-center p-4 text-muted-foreground', className)}>\n No timer active\n </div>\n );\n }\n\n return (\n <div className={cn('flex flex-col items-center', className)}>\n {/* Timer Display */}\n <div className={cn(\n 'font-mono font-bold tabular-nums',\n compact ? 'text-3xl' : 'text-5xl md:text-6xl',\n timer.isRunning ? 'text-primary' : 'text-foreground'\n )}>\n {timer.formatted}\n </div>\n\n {/* Status Indicator */}\n <div className={cn(\n 'text-xs uppercase tracking-wider mt-2',\n timer.isRunning ? 'text-success' : 'text-muted-foreground'\n )}>\n {timer.isComplete ? 'Complete' : timer.isRunning ? 'Running' : 'Paused'}\n </div>\n\n {/* Elapsed/Remaining Info */}\n {timer.remaining !== undefined && timer.remaining > 0 && (\n <div className=\"text-sm text-muted-foreground mt-1\">\n {Math.ceil(timer.remaining / 1000)}s remaining\n </div>\n )}\n\n {/* Round Display */}\n {showRounds && round && (\n <div className={cn(\n 'mt-3 px-3 py-1 rounded-full text-sm',\n 'bg-secondary text-secondary-foreground'\n )}>\n {round.label}\n </div>\n )}\n </div>\n );\n};\n\nexport default BlockTimerDisplay;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitcobblers/wod-wiki-lang",
3
- "version": "0.11.43",
3
+ "version": "0.35.2116",
4
4
  "description": "Whiteboard Language parser, JIT compiler, runtime, and dialect execution",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -25,7 +25,7 @@
25
25
  "access": "public"
26
26
  },
27
27
  "dependencies": {
28
- "@bitcobblers/wod-wiki-core": "^0.11.43",
28
+ "@bitcobblers/wod-wiki-core": "^0.35.2116",
29
29
  "uuid": "^11.1.0"
30
30
  },
31
31
  "peerDependencies": {
@@ -72,7 +72,7 @@
72
72
  "@types/react": "^19.0.10",
73
73
  "@types/react-dom": "^19.0.4",
74
74
  "@types/uuid": "^10.0.0",
75
- "happy-dom": "^17.4.7",
75
+ "happy-dom": "^20.11.6",
76
76
  "react": "^19.0.0",
77
77
  "react-dom": "^19.0.0"
78
78
  },