From f896e207a5e8faa8d79db0adf35477fa61ce7dc4 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Mon, 11 May 2026 16:58:38 -0700 Subject: [PATCH] feat(chat): drop bubble-level GenUI skeleton branches Remove the two @if branches in chat.component.ts that mounted from inside the assistant message bubble. The skeleton/coalescing belongs to a dedicated outer slot, not the bubble. Keep the markdown branch as the sole content path; the ChatGenuiSkeletonComponent stays exported for downstream use. Add a regression test asserting no tag or ChatGenuiSkeletonComponent import remains in the composition source. --- .../lib/compositions/chat/chat.component.spec.ts | 16 ++++++++++++++++ .../src/lib/compositions/chat/chat.component.ts | 15 ++------------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/libs/chat/src/lib/compositions/chat/chat.component.spec.ts b/libs/chat/src/lib/compositions/chat/chat.component.spec.ts index 82ca11839..91cc9c30a 100644 --- a/libs/chat/src/lib/compositions/chat/chat.component.spec.ts +++ b/libs/chat/src/lib/compositions/chat/chat.component.spec.ts @@ -404,3 +404,19 @@ describe('ChatComponent — isGenuiTurn', () => { expect(isGenuiTurn(null, undefined)).toBe(false); }); }); + +describe('ChatComponent — no bubble-level GenUI skeleton', () => { + // Regression for the progressive-GenUI cleanup: the chat composition + // template must NOT render from within the + // assistant message bubble. The skeleton/coalescing belongs to a + // dedicated outer slot (chat-message-list / coalescer), not the bubble. + it('chat.component.ts source contains no tag', async () => { + const fs = await import('node:fs'); + const path = await import('node:path'); + const url = await import('node:url'); + const here = path.dirname(url.fileURLToPath(import.meta.url)); + const src = fs.readFileSync(path.join(here, 'chat.component.ts'), 'utf8'); + expect(src.includes('chat-genui-skeleton')).toBe(false); + expect(src.includes('ChatGenuiSkeletonComponent')).toBe(false); + }); +}); diff --git a/libs/chat/src/lib/compositions/chat/chat.component.ts b/libs/chat/src/lib/compositions/chat/chat.component.ts index 92e1eab2c..abd744b4e 100644 --- a/libs/chat/src/lib/compositions/chat/chat.component.ts +++ b/libs/chat/src/lib/compositions/chat/chat.component.ts @@ -29,7 +29,6 @@ import { ChatMessageActionsComponent } from '../../primitives/chat-message-actio import { ChatWelcomeComponent } from '../../primitives/chat-welcome/chat-welcome.component'; import { ChatSelectComponent, type ChatSelectOption } from '../../primitives/chat-select/chat-select.component'; import { A2uiSurfaceComponent } from '../../a2ui/surface.component'; -import { ChatGenuiSkeletonComponent } from '../../primitives/chat-genui-skeleton/chat-genui-skeleton.component'; import { ChatScrollBubbleComponent } from '../../primitives/chat-scroll-bubble/chat-scroll-bubble.component'; import { createContentClassifier, type ContentClassifier } from '../../streaming/content-classifier'; import { messageContent } from '../shared/message-utils'; @@ -59,7 +58,7 @@ export function isPinned( ChatThreadListComponent, ChatGenerativeUiComponent, ChatStreamingMdComponent, ChatToolCallsComponent, ChatSubagentsComponent, A2uiSurfaceComponent, ChatMessageActionsComponent, ChatWelcomeComponent, ChatSelectComponent, ChatReasoningComponent, - ChatGenuiSkeletonComponent, ChatScrollBubbleComponent, + ChatScrollBubbleComponent, ], changeDetection: ChangeDetectionStrategy.OnPush, styles: [CHAT_HOST_TOKENS, ` @@ -155,7 +154,6 @@ export function isPinned( @let content = messageContent(message); @let classified = classifyMessage(content, message); @let pending = classified.type() === 'pending'; - @let genuiTurn = isGenuiTurn(message, prevMessage(i), i); - @if (genuiTurn && classified.type() !== 'a2ui' && classified.type() !== 'json-render') { - - - } @else if (classified.type() === 'a2ui' && classified.a2uiSurfaces().size === 0 && genuiTurn) { - - - } @else if (classified.markdown(); as md) { + @if (classified.markdown(); as md) { } @if (classified.spec(); as spec) {