@@ -400,6 +400,8 @@ class AIAssistant {
400400 const sendBtn = app . querySelector ( ".ai-send-btn" ) ;
401401 if ( sendBtn ) sendBtn . innerHTML = stopIconSvg ;
402402
403+ let aiMessageElement = null ;
404+
403405 try {
404406 const settings = await this . getSettings ( ) ;
405407 if ( ! settings . apiKey || ! settings . baseUrl || ! settings . model ) {
@@ -408,11 +410,11 @@ class AIAssistant {
408410 return ;
409411 }
410412
411- const aiMessageElement = this . addMessageToChat ( "assistant" , "" , app ) ;
413+ aiMessageElement = this . addMessageToChat ( "assistant" , "" , app ) ;
412414
413415 let response = await this . callAI ( message , settings ) ;
414416
415- this . updateMessage ( aiMessageElement , response , app ) ;
417+ await this . streamMessage ( aiMessageElement , response , app , false ) ;
416418
417419 const chatAreaAfter = app . querySelector ( "#ai-chat-area" ) ;
418420 if ( chatAreaAfter ) {
@@ -434,8 +436,12 @@ class AIAssistant {
434436
435437 this . saveSession ( ) . catch ( ( e ) => console . error ( "saveSession error:" , e ) ) ;
436438 } catch ( error ) {
439+ const errMsg = "Error: " + ( error && error . message ? error . message : String ( error ) ) ;
440+ if ( ! aiMessageElement ) {
441+ aiMessageElement = this . addMessageToChat ( "assistant" , "" , app ) ;
442+ }
443+ await this . streamMessage ( aiMessageElement , errMsg , app , true ) ;
437444 console . error ( "Error generating response:" , error ) ;
438- window . toast ( "Error generating response: " + ( error && error . message ? error . message : error ) , 3000 ) ;
439445 } finally {
440446 this . isGenerating = false ;
441447 if ( sendBtn ) sendBtn . innerHTML = sendIconSvg ;
@@ -472,6 +478,44 @@ class AIAssistant {
472478 return messageContent ;
473479 }
474480
481+ async streamMessage ( element , content , app , isError ) {
482+ if ( ! element ) return ;
483+ const text = String ( content || "" ) ;
484+ element . classList . remove ( "ai-error" ) ;
485+ element . innerHTML = "" ;
486+ element . textContent = "" ;
487+ if ( isError ) element . classList . add ( "ai-error" ) ;
488+ const total = text . length ;
489+ let i = 0 ;
490+ const large = total > 2000 ;
491+ const chunk = large ? 8 : 1 ;
492+ const delay = large ? 20 : 18 ;
493+ while ( i < total ) {
494+ const part = text . slice ( i , i + chunk ) ;
495+ element . textContent = element . textContent + part ;
496+ i += chunk ;
497+ await new Promise ( ( r ) => setTimeout ( r , delay ) ) ;
498+ const chatArea = app && app . querySelector ? app . querySelector ( "#ai-chat-area" ) : null ;
499+ if ( chatArea ) {
500+ requestAnimationFrame ( ( ) => {
501+ chatArea . scrollTop = chatArea . scrollHeight ;
502+ } ) ;
503+ }
504+ }
505+ if ( ! isError && window . markdownit ) {
506+ element . innerHTML = this . formatAIResponse ( element . textContent ) ;
507+ this . highlightCode ( app ) ;
508+ } else {
509+ element . innerHTML = String ( element . textContent ) . replace ( / < / g, "<" ) . replace ( / > / g, ">" ) . replace ( / \n / g, "<br>" ) ;
510+ }
511+ const chatAreaFinal = app && app . querySelector ? app . querySelector ( "#ai-chat-area" ) : null ;
512+ if ( chatAreaFinal ) {
513+ requestAnimationFrame ( ( ) => {
514+ chatAreaFinal . scrollTop = chatAreaFinal . scrollHeight ;
515+ } ) ;
516+ }
517+ }
518+
475519 updateMessage ( element , content , app ) {
476520 if ( ! element ) return ;
477521 element . innerHTML = this . formatAIResponse ( content ) ;
0 commit comments