@@ -10,12 +10,21 @@ import {
1010 StepBack ,
1111 StepForward ,
1212} from "lucide-react" ;
13- import React from "react" ;
13+ import React , { useCallback , useEffect } from "react" ;
1414
1515import { useI18n } from "@/i18n" ;
1616// import { LogCategory, logger } from "@/services/monitoring";
1717import { cn } from "@/utils" ;
1818
19+ // Speed marker configuration
20+ const SPEED_MARKERS = [
21+ { position : "0%" , value : "0.1×" , emphasized : false } ,
22+ { position : "23.08%" , value : "1×" , emphasized : true } ,
23+ { position : "48.72%" , value : "2×" , emphasized : false } ,
24+ { position : "74.36%" , value : "3×" , emphasized : false } ,
25+ { position : "100%" , value : "4×" , emphasized : false } ,
26+ ] as const ;
27+
1928type Props = {
2029 playing : boolean ;
2130 direction : 1 | - 1 ;
@@ -52,20 +61,56 @@ export default function Transport(p: Props) {
5261 onSeek,
5362 } = p ;
5463
55- const formatSpeed = ( speed : number ) : string => {
64+ const formatSpeed = useCallback ( ( speed : number ) : string => {
5665 if ( speed < 1 ) {
5766 return `${ Math . round ( speed * 100 ) } %` ;
5867 }
5968 return `${ speed . toFixed ( 1 ) } ×` ;
60- } ;
69+ } , [ ] ) ;
6170
62- const getSpeedColor = ( speed : number ) : string => {
71+ const getSpeedColor = useCallback ( ( speed : number ) : string => {
6372 if ( speed < 0.5 ) return "text-blue-600" ;
6473 if ( speed < 1 ) return "text-green-600" ;
6574 if ( speed === 1 ) return "text-slate-900 dark:text-slate-100 font-semibold" ;
6675 if ( speed <= 2 ) return "text-orange-600" ;
6776 return "text-red-600" ;
68- } ;
77+ } , [ ] ) ;
78+
79+ const handlePlayPause = useCallback ( ( ) => {
80+ const _action = playing ? "pause" : "play_forward" ;
81+ // logger.info(
82+ // LogCategory.USER_INTERACTION,
83+ // `Transport ${_action} clicked`,
84+ // {
85+ // currentStep: idx,
86+ // totalSteps: total,
87+ // currentSpeed: speed,
88+ // direction: playing ? direction : 1,
89+ // timestamp: new Date().toISOString(),
90+ // }
91+ // );
92+
93+ if ( playing ) {
94+ onPause ( ) ;
95+ } else {
96+ onPlayForward ( ) ;
97+ }
98+ } , [ playing , onPause , onPlayForward ] ) ;
99+
100+ // Development-time prop validation
101+ useEffect ( ( ) => {
102+ if ( process . env . NODE_ENV === "development" ) {
103+ if ( total < 0 ) {
104+ console . warn ( "Transport: total should not be negative" , { total } ) ;
105+ }
106+ if ( idx < 0 || ( total > 0 && idx >= total ) ) {
107+ console . warn ( "Transport: idx is out of bounds" , { idx, total } ) ;
108+ }
109+ if ( speed <= 0 ) {
110+ console . warn ( "Transport: speed should be positive" , { speed } ) ;
111+ }
112+ }
113+ } , [ total , idx , speed ] ) ;
69114
70115 // Calculate progress percentage
71116 const progress = total > 1 ? ( idx / ( total - 1 ) ) * 100 : 0 ;
@@ -145,26 +190,7 @@ export default function Transport(p: Props) {
145190
146191 { /* Play/Pause */ }
147192 < button
148- onClick = { ( ) => {
149- const _action = playing ? "pause" : "play_forward" ;
150- // logger.info(
151- // LogCategory.USER_INTERACTION,
152- // `Transport ${_action} clicked`,
153- // {
154- // currentStep: idx,
155- // totalSteps: total,
156- // currentSpeed: speed,
157- // direction: playing ? direction : 1,
158- // timestamp: new Date().toISOString(),
159- // }
160- // );
161-
162- if ( playing ) {
163- onPause ( ) ;
164- } else {
165- onPlayForward ( ) ;
166- }
167- } }
193+ onClick = { handlePlayPause }
168194 className = { cn (
169195 "rounded-lg p-3 transition-all duration-200" ,
170196 "bg-primary-600 hover:bg-primary-700 text-white shadow-sm" ,
@@ -294,26 +320,26 @@ export default function Transport(p: Props) {
294320
295321 { /* Speed markers */ }
296322 < div className = "relative mt-1 h-4" >
297- < span className = "absolute left-0 text-xs text-slate-400" > 0.1× </ span >
298- < span
299- className = "absolute text-xs font-semibold text-slate-600 dark:text-slate-300"
300- style = { { left : "23.08%" , transform : "translateX(-50%)" } }
301- >
302- 1×
303- </ span >
304- < span
305- className = "absolute text-xs text-slate-400"
306- style = { { left : "48.72%" , transform : "translateX(-50%)" } }
307- >
308- 2×
309- </ span >
310- < span
311- className = "absolute text-xs text-slate-400"
312- style = { { left : "74.36%" , transform : "translateX(-50%)" } }
313- >
314- 3×
315- </ span >
316- < span className = "absolute right-0 text-xs text-slate-400" > 4× </ span >
323+ { SPEED_MARKERS . map ( ( marker , index ) => (
324+ < span
325+ key = { marker . value }
326+ className = { cn (
327+ "absolute text-xs" ,
328+ marker . emphasized
329+ ? "font-semibold text-slate-600 dark:text-slate-300"
330+ : "text-slate-400"
331+ ) }
332+ style = {
333+ index === 0
334+ ? { left : marker . position }
335+ : index === SPEED_MARKERS . length - 1
336+ ? { right : "0" }
337+ : { left : marker . position , transform : "translateX(-50%)" }
338+ }
339+ >
340+ { marker . value }
341+ </ span >
342+ ) ) }
317343 </ div >
318344 </ div >
319345 </ div >
0 commit comments