From 77b6027aa1062aef6975b6490001f9f588ccb7b8 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 11 Oct 2026 13:48:03 -0700 Subject: [PATCH] Show still thinking for slow responses --- .../app/components/chat/chat/chat.component.html | 6 +++++- .../components/chat/chat/chat.component.spec.ts | 15 +++++++++++++++ .../app/components/chat/chat/chat.component.ts | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src-ui/src/app/components/chat/chat/chat.component.html b/src-ui/src/app/components/chat/chat/chat.component.html index 9432c4af8..51ea30eb2 100644 --- a/src-ui/src/app/components/chat/chat/chat.component.html +++ b/src-ui/src/app/components/chat/chat/chat.component.html @@ -12,7 +12,11 @@ @if (message.isStreaming && !message.content) {
- Thinking... + @if (slowResponse()) { + Still thinking... + } @else { + Thinking... + }
} @else if (message.role === 'assistant') {
diff --git a/src-ui/src/app/components/chat/chat/chat.component.spec.ts b/src-ui/src/app/components/chat/chat/chat.component.spec.ts index a9b76011e..df4d942ac 100644 --- a/src-ui/src/app/components/chat/chat/chat.component.spec.ts +++ b/src-ui/src/app/components/chat/chat/chat.component.spec.ts @@ -170,6 +170,21 @@ describe('ChatComponent', () => { expect(component.loading()).toBe(false) }) + it('should show a slow response message while waiting', () => { + component.input.set('Hello') + component.sendMessage() + fixture.detectChanges() + const messagesEl = fixture.nativeElement.querySelector('.chat-messages') + expect(messagesEl.textContent).not.toContain('Still thinking...') + + jest.advanceTimersByTime(8000) + fixture.detectChanges() + expect(messagesEl.textContent).toContain('Still thinking...') + + mockStream$.next('Hi') + expect(component.slowResponse()).toBe(false) + }) + it('should enqueue typewriter chunks correctly', () => { const message = { content: '', role: 'assistant', isStreaming: true } component.enqueueTypewriter(null, message as any) // coverage for null diff --git a/src-ui/src/app/components/chat/chat/chat.component.ts b/src-ui/src/app/components/chat/chat/chat.component.ts index 3b74a04bc..d58068574 100644 --- a/src-ui/src/app/components/chat/chat/chat.component.ts +++ b/src-ui/src/app/components/chat/chat/chat.component.ts @@ -18,6 +18,8 @@ import { parseChatResponse, } from 'src/app/services/chat.service' +const SLOW_RESPONSE_DELAY_MS = 8000 + @Component({ selector: 'pngx-chat', imports: [ @@ -36,6 +38,7 @@ export class ChatComponent implements OnInit { readonly loading = signal(false) readonly input = signal('') readonly documentId = signal(undefined) + readonly slowResponse = signal(false) private chatService: ChatService = inject(ChatService) private router: Router = inject(Router) @@ -45,6 +48,7 @@ export class ChatComponent implements OnInit { private typewriterBuffer: string[] = [] private typewriterActive = false + private slowResponseTimeout: ReturnType public get placeholder(): string { return this.documentId() @@ -83,11 +87,16 @@ export class ChatComponent implements OnInit { } this.messages.update((messages) => [...messages, assistantMessage]) this.loading.set(true) + this.slowResponseTimeout = setTimeout( + () => this.slowResponse.set(true), + SLOW_RESPONSE_DELAY_MS + ) let lastVisibleContent = '' this.chatService.streamChat(this.documentId(), this.input()).subscribe({ next: (chunk) => { + this.clearSlowResponse() const nextResponse = parseChatResponse(chunk) if (nextResponse.content.length < lastVisibleContent.length) { @@ -105,12 +114,14 @@ export class ChatComponent implements OnInit { this.notifyMessagesChanged() }, error: () => { + this.clearSlowResponse() assistantMessage.content += '\n\n⚠️ Error receiving response.' assistantMessage.isStreaming = false this.notifyMessagesChanged() this.loading.set(false) }, complete: () => { + this.clearSlowResponse() assistantMessage.isStreaming = false this.notifyMessagesChanged() this.loading.set(false) @@ -121,6 +132,11 @@ export class ChatComponent implements OnInit { this.input.set('') } + private clearSlowResponse(): void { + clearTimeout(this.slowResponseTimeout) + this.slowResponse.set(false) + } + private resetTypewriter(message: ChatMessage, content: string): void { this.typewriterBuffer = [] this.typewriterActive = false