From 3d437debab0365474ec00fe95897ab8bdb229927 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Sat, 2 May 2026 09:13:51 -0700 Subject: [PATCH] fix(chat): typing indicator in centered column + caret hidden for short streams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two embed-mode polish fixes from live smoke-testing: 1. chat-typing-indicator was rendering at the chat-scroll's left edge (x=0 + 24px padding) while messages live in a centered column (max-width: var(--ngaf-chat-max-width); margin: 0 auto). The dots would flash at the far-left position, then content would suddenly appear in the centered column — visible horizontal shift. Adding the same max-width + margin auto to chat-typing-indicator locks the dots into the same column as messages. 2. Caret animation now has a 300ms fade-in delay before its pulse loop starts. Quick LLM responses ("hello" → "Hello! How can I assist you today?" finishes in ~140ms) never reach the delay threshold, so the caret stays invisible. Longer streams still get the smooth pulse from copilotkit's 2s cubic-bezier curve. Bumps @ngaf/chat 0.0.12 → 0.0.13. Co-Authored-By: Claude Opus 4.7 (1M context) --- libs/chat/package.json | 2 +- libs/chat/src/lib/styles/chat-message.styles.ts | 12 +++++++++--- libs/chat/src/lib/styles/chat-tokens.ts | 4 ++++ .../src/lib/styles/chat-typing-indicator.styles.ts | 12 +++++++++++- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/libs/chat/package.json b/libs/chat/package.json index 0153f1439..a28577eb7 100644 --- a/libs/chat/package.json +++ b/libs/chat/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/chat", - "version": "0.0.12", + "version": "0.0.13", "exports": { ".": { "types": "./index.d.ts", diff --git a/libs/chat/src/lib/styles/chat-message.styles.ts b/libs/chat/src/lib/styles/chat-message.styles.ts index b0a54039c..1f9f556ad 100644 --- a/libs/chat/src/lib/styles/chat-message.styles.ts +++ b/libs/chat/src/lib/styles/chat-message.styles.ts @@ -41,13 +41,19 @@ export const CHAT_MESSAGE_STYLES = ` margin-left: 2px; margin-top: 0.25rem; color: var(--ngaf-chat-text-muted); - /* Smooth pulse curve (copilotkit-style) — easier on the eyes than a - hard step-end blink, especially during long streams. */ - animation: ngaf-chat-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; vertical-align: text-bottom; } :host([data-role="assistant"][data-current="true"][data-streaming="true"]) .chat-message__caret { display: inline-block; + /* The caret is suppressed for the first 300ms of streaming so quick + responses (one-or-two-token "hello"-style replies) never flash the + cursor. Past 300ms the smooth pulse takes over (copilotkit-style) + — easier on the eyes than a hard blink during long streams. + Note: animations restart whenever the element is created/inserted, + so this delay re-applies on every new streaming message. */ + opacity: 0; + animation: ngaf-chat-caret-fade-in 200ms ease-out 300ms forwards, + ngaf-chat-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) 500ms infinite; } .chat-message__plain { /* system / tool fallback */ } diff --git a/libs/chat/src/lib/styles/chat-tokens.ts b/libs/chat/src/lib/styles/chat-tokens.ts index a78191215..1dae6e800 100644 --- a/libs/chat/src/lib/styles/chat-tokens.ts +++ b/libs/chat/src/lib/styles/chat-tokens.ts @@ -86,6 +86,10 @@ const KEYFRAMES = ` 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } } + @keyframes ngaf-chat-caret-fade-in { + from { opacity: 0; } + to { opacity: 1; } + } `; /** diff --git a/libs/chat/src/lib/styles/chat-typing-indicator.styles.ts b/libs/chat/src/lib/styles/chat-typing-indicator.styles.ts index f5d5ee407..3a934b055 100644 --- a/libs/chat/src/lib/styles/chat-typing-indicator.styles.ts +++ b/libs/chat/src/lib/styles/chat-typing-indicator.styles.ts @@ -1,6 +1,16 @@ // SPDX-License-Identifier: MIT export const CHAT_TYPING_INDICATOR_STYLES = ` - :host { display: block; padding: 0 var(--ngaf-chat-space-6) var(--ngaf-chat-space-3); } + /* Sit in the same centered column as chat-message-list so the dots + don't flash at the scroll container's left edge before the assistant + message renders. */ + :host { + display: block; + padding: 0 var(--ngaf-chat-space-6) var(--ngaf-chat-space-3); + max-width: var(--ngaf-chat-max-width); + margin: 0 auto; + width: 100%; + box-sizing: border-box; + } .chat-typing__dots { display: inline-flex; gap: 4px; align-items: center; } .chat-typing__dot { width: 6px;