From 4bca3da373588a0d929ceb8053f6eb89f9caad0d Mon Sep 17 00:00:00 2001 From: Brian Love Date: Fri, 1 May 2026 22:06:47 -0700 Subject: [PATCH] =?UTF-8?q?fix(chat,langgraph):=20jank=20=E2=80=94=20stabl?= =?UTF-8?q?e=20@for=20tracking=20+=20empty-array=20guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - chat-message-list: `track message.id` (was `track $index`) so the @for doesn't tear down DOM when the messages array briefly shrinks/refills during streaming. Loses streaming-md renderer state on every teardown → visible flash + missed tokens. - stream-manager: skip publishing an empty `messages` array during streaming (both messages-event and values-event paths). An empty payload mid-stream shouldn't wipe the UI; this was the upstream cause of the transient empty-array we saw via MutationObserver instrumentation. - Bumps: @ngaf/chat 0.0.8 → 0.0.9, @ngaf/langgraph 0.0.2 → 0.0.3. Co-Authored-By: Claude Opus 4.7 (1M context) --- libs/chat/package.json | 2 +- .../chat-message-list/chat-message-list.component.ts | 2 +- libs/langgraph/package.json | 2 +- libs/langgraph/src/lib/internals/stream-manager.bridge.ts | 8 +++++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libs/chat/package.json b/libs/chat/package.json index edae9af47..ff0d83d69 100644 --- a/libs/chat/package.json +++ b/libs/chat/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/chat", - "version": "0.0.8", + "version": "0.0.9", "exports": { ".": { "types": "./index.d.ts", diff --git a/libs/chat/src/lib/primitives/chat-message-list/chat-message-list.component.ts b/libs/chat/src/lib/primitives/chat-message-list/chat-message-list.component.ts index 6de3bf5f2..1b26cbfba 100644 --- a/libs/chat/src/lib/primitives/chat-message-list/chat-message-list.component.ts +++ b/libs/chat/src/lib/primitives/chat-message-list/chat-message-list.component.ts @@ -39,7 +39,7 @@ export function getMessageType(message: Message): MessageTemplateType { changeDetection: ChangeDetectionStrategy.OnPush, styles: [CHAT_HOST_TOKENS, CHAT_MESSAGE_LIST_STYLES], template: ` - @for (message of messages(); track $index) { + @for (message of messages(); track message.id) { @let template = findTemplate(getMessageType(message)); @if (template) { )['messages']; - if (Array.isArray(stateMessages)) { + if (Array.isArray(stateMessages) && stateMessages.length > 0) { + // Defensive: only sync when state carries messages. An empty + // values payload shouldn't wipe the UI mid-stream. if (options.toMessage) { subjects.messages$.next(stateMessages.map(options.toMessage)); } else {