@@ -39,6 +39,8 @@ const App: React.FC = () => {
3939 const [ modelLoading , setModelLoading ] = useState ( false ) ;
4040 const [ modelError , setModelError ] = useState < string | null > ( null ) ;
4141 const [ isGeneratingTimeline , setIsGeneratingTimeline ] = useState ( false ) ;
42+ const [ rejectionMessage , setRejectionMessage ] = useState < string | null > ( null ) ;
43+ const [ timelineError , setTimelineError ] = useState < string | null > ( null ) ;
4244
4345 useEffect ( ( ) => {
4446 const initModel = async ( ) => {
@@ -64,12 +66,7 @@ const App: React.FC = () => {
6466 } , [ ] ) ;
6567
6668 const handleFilesDropped = useCallback ( async ( droppedFiles : File [ ] ) => {
67- if ( ! modelLoading ) {
68- loadModel ( ) . catch ( ( e ) => {
69- const errorMsg = e instanceof Error ? e . message : String ( e ) ;
70- appLogger . warn ( 'model_load_failed_background' , { errorMessage : errorMsg } ) ;
71- } ) ;
72- }
69+ setRejectionMessage ( null ) ;
7370
7471 const validated = droppedFiles . map ( ( f ) => ( { file : f , validation : validateFile ( f ) } ) ) ;
7572 const rejected = validated . filter ( ( v ) => ! v . validation . ok ) ;
@@ -83,7 +80,7 @@ const App: React.FC = () => {
8380 return `${ name } : ${ reasons } ` ;
8481 } )
8582 . join ( '\n' ) ;
86- alert ( `Some files were rejected:\n\n ${ message } ` ) ;
83+ setRejectionMessage ( message ) ;
8784 }
8885
8986 if ( accepted . length === 0 ) {
@@ -102,7 +99,7 @@ const App: React.FC = () => {
10299
103100 setFiles ( prev => [ ...prev , ...newFiles ] ) ;
104101 processQueue ( rawFiles , newFiles ) ;
105- } , [ modelLoading ] ) ;
102+ } , [ ] ) ;
106103
107104 const processQueue = async ( rawFiles : File [ ] , fileEntries : UIProcessedFile [ ] ) => {
108105 setIsProcessing ( true ) ;
@@ -153,7 +150,7 @@ const App: React.FC = () => {
153150 // 3. Formatting Stage
154151 updateFileStatus ( fileEntry . id , ProcessingStage . FORMATTING ) ;
155152
156- const processingTimeMs = performance . now ( ) - startTime ;
153+ const processingTimeMs = Math . round ( performance . now ( ) - startTime ) ;
157154 const markdown = formatToMarkdown ( fileEntry , mergedScrubResult , processingTimeMs ) ;
158155
159156 const stats = {
@@ -248,11 +245,12 @@ const App: React.FC = () => {
248245 const completedFiles = files . filter ( f => f . stage === ProcessingStage . COMPLETED && f . scrubbedText ) ;
249246
250247 if ( completedFiles . length === 0 ) {
251- alert ( 'No processed files to compile. Please process some documents first.' ) ;
248+ setTimelineError ( 'No processed files to compile. Please process some documents first.' ) ;
252249 return ;
253250 }
254251
255252 setIsGeneratingTimeline ( true ) ;
253+ setTimelineError ( null ) ;
256254
257255 try {
258256 if ( ! isProductionMode ( ) ) {
@@ -280,7 +278,7 @@ const App: React.FC = () => {
280278 } catch ( error ) {
281279 const errorMessage = error instanceof Error ? error . message : String ( error ) ;
282280 appLogger . error ( 'timeline_generate_failed' , { errorMessage } ) ;
283- alert ( 'Failed to generate timeline. Check console for details.' ) ;
281+ setTimelineError ( 'Failed to generate timeline. Check console for details.' ) ;
284282 } finally {
285283 setIsGeneratingTimeline ( false ) ;
286284 }
@@ -410,6 +408,33 @@ const App: React.FC = () => {
410408 { /* Action Area */ }
411409 < DropZone onFilesDropped = { handleFilesDropped } isProcessing = { isProcessing } />
412410
411+ { /* File Rejection Banner */ }
412+ { rejectionMessage && (
413+ < div className = "bg-amber-50 border-2 border-amber-500 p-4 mt-4" >
414+ < div className = "flex items-start justify-between gap-3" >
415+ < div >
416+ < h4 className = "text-sm font-bold text-amber-900 uppercase tracking-tight mb-1" > Some files were rejected</ h4 >
417+ < pre className = "text-xs font-mono text-amber-800 whitespace-pre-wrap" > { rejectionMessage } </ pre >
418+ </ div >
419+ < button onClick = { ( ) => setRejectionMessage ( null ) } className = "text-amber-600 hover:text-amber-800 text-xs font-bold uppercase shrink-0" >
420+ Dismiss
421+ </ button >
422+ </ div >
423+ </ div >
424+ ) }
425+
426+ { /* Timeline Error Banner */ }
427+ { timelineError && (
428+ < div className = "bg-rose-50 border-2 border-rose-500 p-4 mt-4" >
429+ < div className = "flex items-start justify-between gap-3" >
430+ < p className = "text-sm font-mono text-rose-800" > { timelineError } </ p >
431+ < button onClick = { ( ) => setTimelineError ( null ) } className = "text-rose-600 hover:text-rose-800 text-xs font-bold uppercase shrink-0" >
432+ Dismiss
433+ </ button >
434+ </ div >
435+ </ div >
436+ ) }
437+
413438 { /* Controls */ }
414439 { files . length > 0 && (
415440 < div className = "flex justify-between items-center pt-2 animate-in fade-in slide-in-from-bottom-2 duration-300" >
@@ -479,9 +504,9 @@ const App: React.FC = () => {
479504 </ div >
480505
481506 < div className = "flex flex-col md:items-end gap-1" >
482- < span className = "font-bold text-black" > © 2025 Forgestack.app </ span >
483- < a href = "mailto:hello@forgestack.app " className = "hover:text-accent-600 flex items-center gap-1" >
484- < Mail className = "w-3 h-3" /> hello@forgestack.app
507+ < span className = "font-bold text-black" > © 2025 Heyoub </ span >
508+ < a href = "mailto:hello@heyoub.dev " className = "hover:text-accent-600 flex items-center gap-1" >
509+ < Mail className = "w-3 h-3" /> hello@heyoub.dev
485510 </ a >
486511 </ div >
487512 </ div >
0 commit comments