@@ -19,68 +19,41 @@ export default function ControlPanel({
1919} ) {
2020 const [ isOpen , setIsOpen ] = useState ( false ) ;
2121 const [ fileName , setFileName ] = useState < string | null > ( null ) ;
22- const setTimeline = useTimelineStore ( ( state ) => state . setTimeline ) ;
23-
24- // ref so we can clear input.value after upload
2522 const fileInputRef = useRef < HTMLInputElement > ( null ) ;
23+ const prevKeyRef = useRef < string | null > ( null ) ;
24+ const setTimeline = useTimelineStore ( ( state ) => state . setTimeline ) ;
2625
2726 const handleFileUpload = async (
2827 event : React . ChangeEvent < HTMLInputElement >
2928 ) => {
3029 const uploaded = event . target . files ?. [ 0 ] ;
3130 if ( ! uploaded ) return ;
32-
3331 setFileName ( uploaded . name ) ;
34-
35- try {
36- const entries = await parseLogFile ( uploaded ) ;
37- const timeline : TimelineData = buildTimeline ( entries ) ;
38- setTimeline ( timeline ) ;
39- reset ( ) ;
40- console . log ( "timeline loaded:" , timeline ) ;
41- } catch ( err ) {
42- console . error ( "Failed to parse log file:" , err ) ;
43- }
44-
45- // reset the input so same filename can be re‐picked
46- if ( fileInputRef . current ) {
47- fileInputRef . current . value = "" ;
48- }
32+ const entries = await parseLogFile ( uploaded ) ;
33+ const timeline : TimelineData = buildTimeline ( entries ) ;
34+ setTimeline ( timeline ) ;
35+ reset ( ) ;
36+ fileInputRef . current ! . value = "" ;
4937 } ;
5038
51- const handleContextMenu = ( event : React . MouseEvent ) => {
52- event . preventDefault ( ) ;
53- setIsOpen ( ( o ) => ! o ) ;
54- } ;
55-
56- // auto‐load from Flask if folder+file props provided
5739 useEffect ( ( ) => {
5840 if ( ! folder || ! file ) return ;
59-
60- const fetchLog = async ( ) => {
41+ const key = `${ folder } /${ file } ` ;
42+ if ( prevKeyRef . current === key ) return ;
43+ prevKeyRef . current = key ;
44+ ( async ( ) => {
6145 const url = `http://localhost:5000/raw_logs/${ encodeURIComponent (
6246 folder
6347 ) } /${ encodeURIComponent ( file ) } `;
64-
65- try {
66- const res = await fetch ( url ) ;
67- if ( ! res . ok ) {
68- throw new Error ( `Failed to fetch ${ file } (status ${ res . status } )` ) ;
69- }
70- const text = await res . text ( ) ;
71- const fakeFile = new File ( [ text ] , file , { type : "text/plain" } ) ;
72- const entries = await parseLogFile ( fakeFile ) ;
73- const timeline = buildTimeline ( entries ) ;
74- setTimeline ( timeline ) ;
75- reset ( ) ;
76- setFileName ( file ) ;
77- console . log ( "Timeline loaded from Flask API:" , timeline ) ;
78- } catch ( err ) {
79- console . error ( "Failed to load remote log file:" , err ) ;
80- }
81- } ;
82-
83- fetchLog ( ) ;
48+ const res = await fetch ( url ) ;
49+ const text = await res . text ( ) ;
50+ const fakeFile = new File ( [ text ] , file , { type : "text/plain" } ) ;
51+ const entries = await parseLogFile ( fakeFile ) ;
52+ const timeline = buildTimeline ( entries ) ;
53+ setTimeline ( timeline ) ;
54+ reset ( ) ;
55+ setFileName ( file ) ;
56+ } ) ( ) ;
8457 } , [ folder , file , reset , setTimeline ] ) ;
8558
8659 return (
@@ -89,7 +62,10 @@ export default function ControlPanel({
8962 className = { `fixed top-0 left-0 h-full w-48 max-w-[90vw] bg-black/80 text-white shadow-lg transform transition-transform duration-300 ease-in-out z-40 overflow-y-auto flex flex-col ${
9063 isOpen ? "translate-x-0" : "-translate-x-full"
9164 } `}
92- onContextMenu = { handleContextMenu }
65+ onContextMenu = { ( e ) => {
66+ e . preventDefault ( ) ;
67+ setIsOpen ( ( o ) => ! o ) ;
68+ } }
9369 >
9470 { isOpen && (
9571 < >
@@ -100,12 +76,10 @@ export default function ControlPanel({
10076 < button
10177 onClick = { ( ) => setIsOpen ( false ) }
10278 className = "text-white p-1 rounded hover:bg-white/10"
103- title = "Close Panel"
10479 >
10580 < RxCross2 className = "w-4 h-4" />
10681 </ button >
10782 </ div >
108-
10983 < div className = "p-4" >
11084 < label className = "inline-flex items-center gap-2 cursor-pointer text-gray-300 text-sm px-4 py-2 border border-gray-500 rounded hover:bg-white/10 transition" >
11185 < TiUpload className = "w-4 h-4" />
@@ -125,17 +99,14 @@ export default function ControlPanel({
12599 </ >
126100 ) }
127101 </ div >
128-
129102 { ! isOpen && (
130103 < button
131104 onClick = { ( ) => setIsOpen ( true ) }
132105 className = "fixed top-1/2 left-0 -translate-y-1/2 z-50 bg-black/80 text-white w-3 h-8 flex items-center justify-center rounded-r hover:bg-black border-l border-white/20"
133- title = "Open Panel"
134- onContextMenu = { ( e ) => e . preventDefault ( ) }
135106 >
136107 < span className = "text-xs" > ⟩</ span >
137108 </ button >
138109 ) }
139110 </ >
140111 ) ;
141- }
112+ }
0 commit comments