@@ -494,7 +494,7 @@ class TimelineApp {
494494
495495 if ( ! message ) return ;
496496
497- // Add user message
497+ // Add user message immediately
498498 this . addMessage ( botName , message , 'user' ) ;
499499 input . value = '' ;
500500
@@ -503,11 +503,11 @@ class TimelineApp {
503503 let response ;
504504 // Neural models (seq2seq and gpt) use async
505505 if ( botName === 'gpt' || botName === 'seq2seq' ) {
506- // Show typing indicator for neural models
507- this . addMessage ( botName , 'Generating response...' , 'bot-typing' ) ;
506+ // Show animated typing indicator for neural models
507+ this . addTypingIndicator ( botName ) ;
508508 response = await this . bots [ botName ] . getResponse ( message ) ;
509509 // Remove typing indicator
510- this . removeLastMessage ( botName ) ;
510+ this . removeTypingIndicator ( botName ) ;
511511 } else {
512512 // Rule-based bots are synchronous
513513 response = this . bots [ botName ] . getResponse ( message ) ;
@@ -520,7 +520,7 @@ class TimelineApp {
520520
521521 } catch ( error ) {
522522 console . error ( `Error getting response from ${ botName } :` , error ) ;
523- this . removeLastMessage ( botName ) ; // Remove typing indicator if present
523+ this . removeTypingIndicator ( botName ) ; // Remove typing indicator if present
524524 this . addMessage ( botName , "Sorry, I encountered an error." , 'bot' ) ;
525525 }
526526 }
@@ -532,6 +532,26 @@ class TimelineApp {
532532 }
533533 }
534534
535+ addTypingIndicator ( botName ) {
536+ const messagesContainer = document . getElementById ( `${ botName } -messages` ) ;
537+ const typingDiv = document . createElement ( 'div' ) ;
538+ typingDiv . className = 'message bot typing-indicator' ;
539+ typingDiv . id = `${ botName } -typing` ;
540+ // Create three animated dots using DOM methods
541+ for ( let i = 0 ; i < 3 ; i ++ ) {
542+ typingDiv . appendChild ( document . createElement ( 'span' ) ) ;
543+ }
544+ messagesContainer . appendChild ( typingDiv ) ;
545+ messagesContainer . scrollTop = messagesContainer . scrollHeight ;
546+ }
547+
548+ removeTypingIndicator ( botName ) {
549+ const typingDiv = document . getElementById ( `${ botName } -typing` ) ;
550+ if ( typingDiv ) {
551+ typingDiv . remove ( ) ;
552+ }
553+ }
554+
535555 addMessage ( botName , text , type ) {
536556 const messagesContainer = document . getElementById ( `${ botName } -messages` ) ;
537557 const messageDiv = document . createElement ( 'div' ) ;
@@ -542,71 +562,118 @@ class TimelineApp {
542562 }
543563
544564 async compareAllBots ( ) {
545- const prompt = document . getElementById ( 'compare-prompt' ) . value . trim ( ) ;
565+ const promptInput = document . getElementById ( 'compare-prompt' ) ;
566+ const prompt = promptInput . value . trim ( ) ;
546567
547568 if ( ! prompt ) {
548569 alert ( 'Please enter a prompt to compare' ) ;
549570 return ;
550571 }
551572
552573 const resultsDiv = document . getElementById ( 'comparison-results' ) ;
553- resultsDiv . innerHTML = '<p style="color: #999; text-align: center;">Processing...</p>' ;
554574
555- const responses = { } ;
575+ // Initialize conversation history if not exists
576+ if ( ! this . comparisonHistory ) {
577+ this . comparisonHistory = [ ] ;
578+ }
556579
557- // Get responses from all bots with individual error handling
558- try {
559- // Rule-based bots (synchronous) - handle individually
560- try {
561- responses . eliza = this . bots . eliza . getResponse ( prompt ) ;
562- } catch ( error ) {
563- console . error ( 'Error from ELIZA:' , error ) ;
564- responses . eliza = 'Error: Unable to get response' ;
565- }
580+ // Add user message to history and display immediately
581+ const userMsgDiv = document . createElement ( 'div' ) ;
582+ userMsgDiv . className = 'comparison-item' ;
583+ userMsgDiv . style . background = 'var(--primary-color)' ;
584+ userMsgDiv . style . color = 'white' ;
585+ userMsgDiv . style . borderLeftColor = 'var(--secondary-color)' ;
586+ const userStrong = document . createElement ( 'strong' ) ;
587+ userStrong . textContent = 'YOU:' ;
588+ userMsgDiv . appendChild ( userStrong ) ;
589+ userMsgDiv . appendChild ( document . createElement ( 'br' ) ) ;
590+ userMsgDiv . appendChild ( document . createTextNode ( prompt ) ) ;
591+ resultsDiv . appendChild ( userMsgDiv ) ;
592+
593+ // Clear input
594+ promptInput . value = '' ;
595+
596+ // Create bot response containers with loading indicators
597+ const botNames = [ 'eliza' , 'parry' , 'alice' , 'seq2seq' , 'gpt' ] ;
598+ const botLabels = {
599+ eliza : 'ELIZA (1966)' ,
600+ parry : 'PARRY (1972)' ,
601+ alice : 'A.L.I.C.E. (1995)' ,
602+ seq2seq : 'BlenderBot (2020)' ,
603+ gpt : 'LaMini-GPT (2023)'
604+ } ;
605+ const botDivs = { } ;
566606
567- try {
568- responses . parry = this . bots . parry . getResponse ( prompt ) ;
569- } catch ( error ) {
570- console . error ( 'Error from PARRY:' , error ) ;
571- responses . parry = 'Error: Unable to get response' ;
607+ // Create containers with typing indicators
608+ for ( const bot of botNames ) {
609+ const item = document . createElement ( 'div' ) ;
610+ item . className = 'comparison-item' ;
611+ item . id = `compare-${ bot } ` ;
612+
613+ const strong = document . createElement ( 'strong' ) ;
614+ strong . textContent = botLabels [ bot ] + ':' ;
615+ item . appendChild ( strong ) ;
616+ item . appendChild ( document . createElement ( 'br' ) ) ;
617+
618+ // Add typing indicator
619+ const typingSpan = document . createElement ( 'span' ) ;
620+ typingSpan . className = 'typing-indicator' ;
621+ typingSpan . style . display = 'inline-flex' ;
622+ for ( let i = 0 ; i < 3 ; i ++ ) {
623+ typingSpan . appendChild ( document . createElement ( 'span' ) ) ;
572624 }
625+ item . appendChild ( typingSpan ) ;
573626
574- try {
575- responses . alice = this . bots . alice . getResponse ( prompt ) ;
576- } catch ( error ) {
577- console . error ( 'Error from ALICE:' , error ) ;
578- responses . alice = 'Error: Unable to get response' ;
579- }
627+ resultsDiv . appendChild ( item ) ;
628+ botDivs [ bot ] = item ;
629+ }
580630
581- // Neural models (asynchronous) - handle individually
582- try {
583- responses . seq2seq = await this . bots . seq2seq . getResponse ( prompt ) ;
584- } catch ( error ) {
585- console . error ( 'Error from Seq2Seq:' , error ) ;
586- responses . seq2seq = 'Error: Unable to get response' ;
587- }
631+ // Scroll to show new content
632+ resultsDiv . scrollTop = resultsDiv . scrollHeight ;
588633
589- try {
590- responses . gpt = await this . bots . gpt . getResponse ( prompt ) ;
591- } catch ( error ) {
592- console . error ( 'Error from GPT:' , error ) ;
593- responses . gpt = 'Error: Unable to get response' ;
594- }
634+ // Get responses - rule-based first (sync), then neural (async)
635+ const updateBotResponse = ( bot , response ) => {
636+ const item = botDivs [ bot ] ;
637+ // Remove typing indicator and add response
638+ const typing = item . querySelector ( '.typing-indicator' ) ;
639+ if ( typing ) typing . remove ( ) ;
640+ item . appendChild ( document . createTextNode ( response ) ) ;
641+ } ;
642+
643+ // Rule-based bots (synchronous)
644+ try {
645+ updateBotResponse ( 'eliza' , this . bots . eliza . getResponse ( prompt ) ) ;
595646 } catch ( error ) {
596- console . error ( 'Unexpected error in comparison:' , error ) ;
647+ console . error ( 'Error from ELIZA:' , error ) ;
648+ updateBotResponse ( 'eliza' , 'Error: Unable to get response' ) ;
597649 }
598650
599- // Display results
600- resultsDiv . innerHTML = '' ;
601- for ( const [ bot , response ] of Object . entries ( responses ) ) {
602- const item = document . createElement ( 'div' ) ;
603- item . className = 'comparison-item' ;
604- item . innerHTML = `
605- <strong>${ bot . toUpperCase ( ) } :</strong><br>
606- ${ response }
607- ` ;
608- resultsDiv . appendChild ( item ) ;
651+ try {
652+ updateBotResponse ( 'parry' , this . bots . parry . getResponse ( prompt ) ) ;
653+ } catch ( error ) {
654+ console . error ( 'Error from PARRY:' , error ) ;
655+ updateBotResponse ( 'parry' , 'Error: Unable to get response' ) ;
656+ }
657+
658+ try {
659+ updateBotResponse ( 'alice' , this . bots . alice . getResponse ( prompt ) ) ;
660+ } catch ( error ) {
661+ console . error ( 'Error from ALICE:' , error ) ;
662+ updateBotResponse ( 'alice' , 'Error: Unable to get response' ) ;
609663 }
664+
665+ // Neural models (asynchronous) - run in parallel
666+ const neuralPromises = [
667+ this . bots . seq2seq . getResponse ( prompt ) . then ( r => updateBotResponse ( 'seq2seq' , r ) )
668+ . catch ( e => { console . error ( 'Error from Seq2Seq:' , e ) ; updateBotResponse ( 'seq2seq' , 'Error: Unable to get response' ) ; } ) ,
669+ this . bots . gpt . getResponse ( prompt ) . then ( r => updateBotResponse ( 'gpt' , r ) )
670+ . catch ( e => { console . error ( 'Error from GPT:' , e ) ; updateBotResponse ( 'gpt' , 'Error: Unable to get response' ) ; } )
671+ ] ;
672+
673+ await Promise . all ( neuralPromises ) ;
674+
675+ // Save to history
676+ this . comparisonHistory . push ( { prompt, timestamp : Date . now ( ) } ) ;
610677 }
611678
612679 displayArchitecture ( ) {
0 commit comments