@@ -154,6 +154,17 @@ function TimelineBar({ plotLayouts = [] }: TimelineBarProps) {
154154 const replayFileInputRef = useRef < HTMLInputElement | null > ( null ) ;
155155 const configFileInputRef = useRef < HTMLInputElement | null > ( null ) ;
156156
157+ // In-app replacement for native alert() on import — shown as a dismissible
158+ // notice strip below. Success/info auto-dismisses; errors stay until dismissed.
159+ const [ importNotice , setImportNotice ] = useState <
160+ { kind : "info" | "error" ; message : string } | null
161+ > ( null ) ;
162+ useEffect ( ( ) => {
163+ if ( importNotice ?. kind !== "info" ) return ;
164+ const timer = setTimeout ( ( ) => setImportNotice ( null ) , 6000 ) ;
165+ return ( ) => clearTimeout ( timer ) ;
166+ } , [ importNotice ] ) ;
167+
157168 const handleImportConfigOnly = async ( event : React . ChangeEvent < HTMLInputElement > ) => {
158169 const file = event . target . files ?. [ 0 ] ;
159170 if ( ! file ) return ;
@@ -188,13 +199,13 @@ function TimelineBar({ plotLayouts = [] }: TimelineBarProps) {
188199 applied . push ( "DBC" ) ;
189200 }
190201
191- window . alert (
202+ setImportNotice (
192203 applied . length > 0
193- ? `Config imported: ${ applied . join ( ", " ) } `
194- : " No plot/DBC config found in file."
204+ ? { kind : "info" , message : `Config imported: ${ applied . join ( ", " ) } ` }
205+ : { kind : "info" , message : " No plot/DBC config found in file." }
195206 ) ;
196207 } catch ( err ) {
197- window . alert ( `Config import failed: ${ err instanceof Error ? err . message : "unknown error" } ` ) ;
208+ setImportNotice ( { kind : "error" , message : `Config import failed: ${ err instanceof Error ? err . message : "unknown error" } ` } ) ;
198209 } finally {
199210 event . target . value = "" ;
200211 }
@@ -479,7 +490,7 @@ function TimelineBar({ plotLayouts = [] }: TimelineBarProps) {
479490 const parseResult = await parseReplayFile ( file ) ;
480491 if ( parseResult . errors . length > 0 || parseResult . frames . length === 0 ) {
481492 const firstError = parseResult . errors [ 0 ] ?. message ?? "No valid frames found in file." ;
482- window . alert ( `Replay import failed: ${ firstError } ` ) ;
493+ setImportNotice ( { kind : "error" , message : `Replay import failed: ${ firstError } ` } ) ;
483494 return ;
484495 }
485496
@@ -502,11 +513,11 @@ function TimelineBar({ plotLayouts = [] }: TimelineBarProps) {
502513 }
503514
504515 if ( parseResult . warnings . length > 0 ) {
505- window . alert ( `Replay imported with ${ parseResult . warnings . length } warning(s).` ) ;
516+ setImportNotice ( { kind : "info" , message : `Replay imported with ${ parseResult . warnings . length } warning(s).` } ) ;
506517 }
507518 } catch ( error ) {
508519 const message = error instanceof Error ? error . message : "Unknown import error" ;
509- window . alert ( `Replay import failed: ${ message } ` ) ;
520+ setImportNotice ( { kind : "error" , message : `Replay import failed: ${ message } ` } ) ;
510521 } finally {
511522 setIsImportingReplay ( false ) ;
512523 event . target . value = "" ;
@@ -595,6 +606,25 @@ function TimelineBar({ plotLayouts = [] }: TimelineBarProps) {
595606 } }
596607 />
597608 ) }
609+ { importNotice && (
610+ < div
611+ className = { `flex items-center gap-2 mb-1.5 rounded px-2.5 py-1 text-[11px] border ${
612+ importNotice . kind === "error"
613+ ? "bg-red-500/15 border-red-400/40 text-red-300"
614+ : "bg-sky-500/15 border-sky-400/40 text-sky-200"
615+ } `}
616+ >
617+ < span className = "flex-1" > { importNotice . kind === "error" ? "⚠ " : "" } { importNotice . message } </ span >
618+ < button
619+ type = "button"
620+ className = "font-bold leading-none opacity-70 hover:opacity-100"
621+ onClick = { ( ) => setImportNotice ( null ) }
622+ title = "Dismiss"
623+ >
624+ ×
625+ </ button >
626+ </ div >
627+ ) }
598628 { coldWarning && (
599629 < div className = "flex items-center gap-2 mb-1.5 rounded bg-amber-500/15 border border-amber-400/40 px-2.5 py-1 text-[11px] text-amber-300" >
600630 < span className = "flex-1" > ⚠ { coldWarning } </ span >
0 commit comments