diff --git a/libs/chat/package.json b/libs/chat/package.json index 639803cad..38afa54bc 100644 --- a/libs/chat/package.json +++ b/libs/chat/package.json @@ -45,6 +45,7 @@ }, "sideEffects": [ "**/chat-tokens.ts", + "./fesm2022/ngaf-chat.mjs", "**/*.css" ] } diff --git a/libs/chat/src/lib/compositions/chat-debug/chat-debug.component.ts b/libs/chat/src/lib/compositions/chat-debug/chat-debug.component.ts index e54960198..7b30c3acc 100644 --- a/libs/chat/src/lib/compositions/chat-debug/chat-debug.component.ts +++ b/libs/chat/src/lib/compositions/chat-debug/chat-debug.component.ts @@ -16,6 +16,7 @@ import { import { NgTemplateOutlet } from '@angular/common'; import type { AgentWithHistory } from '../../agent'; import { CHAT_DEBUG_TOKENS } from './chat-debug-tokens'; +import { ensureChatRootStyles } from '../../styles/chat-tokens'; import { ChatDebugControlsDirective } from './chat-debug-controls.directive'; import { ChatDebugInspectorDirective } from './chat-debug-inspector.directive'; import { TimelineInspectorComponent } from './inspectors/timeline-inspector.component'; @@ -399,6 +400,10 @@ export class ChatDebugComponent { private readonly hostEl: ElementRef = inject(ElementRef); constructor() { + // Inject chat lib root CSS custom properties so the theme-attribute + // mappings + edge-claim primitive are in the document, even when + // chat-debug is mounted without a sibling chat composition. + ensureChatRootStyles(); // Restore once from storage on construction; inputs seed the fallback. // `storageKey` is read-once: rebinding it at runtime is not supported. const restore = createPersistence(this.storageKey()); diff --git a/libs/chat/src/lib/compositions/chat-popup/chat-popup.component.ts b/libs/chat/src/lib/compositions/chat-popup/chat-popup.component.ts index c4970b3ec..06e9d3fb9 100644 --- a/libs/chat/src/lib/compositions/chat-popup/chat-popup.component.ts +++ b/libs/chat/src/lib/compositions/chat-popup/chat-popup.component.ts @@ -6,7 +6,7 @@ import type { ViewRegistry } from '@ngaf/render'; import { ChatComponent } from '../chat/chat.component'; import type { ChatSelectOption } from '../../primitives/chat-select/chat-select.component'; import { ChatLauncherButtonComponent } from '../../primitives/chat-launcher-button/chat-launcher-button.component'; -import { CHAT_HOST_TOKENS } from '../../styles/chat-tokens'; +import { CHAT_HOST_TOKENS, ensureChatRootStyles } from '../../styles/chat-tokens'; @Component({ selector: 'chat-popup', @@ -106,6 +106,9 @@ export class ChatPopupComponent { private readonly document = inject(DOCUMENT); constructor() { + // Inject chat lib root CSS custom properties — see ChatComponent + // for the full rationale. Idempotent + lifecycle-guaranteed. + ensureChatRootStyles(); effect(() => { // Re-bind whenever shortcut/closeOnEscape change. const shortcut = this.shortcut(); diff --git a/libs/chat/src/lib/compositions/chat-sidebar/chat-sidebar.component.ts b/libs/chat/src/lib/compositions/chat-sidebar/chat-sidebar.component.ts index 6fdeed391..a791b9fc2 100644 --- a/libs/chat/src/lib/compositions/chat-sidebar/chat-sidebar.component.ts +++ b/libs/chat/src/lib/compositions/chat-sidebar/chat-sidebar.component.ts @@ -6,7 +6,7 @@ import type { ViewRegistry } from '@ngaf/render'; import { ChatComponent } from '../chat/chat.component'; import { ChatLauncherButtonComponent } from '../../primitives/chat-launcher-button/chat-launcher-button.component'; import type { ChatSelectOption } from '../../primitives/chat-select/chat-select.component'; -import { CHAT_HOST_TOKENS } from '../../styles/chat-tokens'; +import { CHAT_HOST_TOKENS, ensureChatRootStyles } from '../../styles/chat-tokens'; @Component({ selector: 'chat-sidebar', @@ -104,6 +104,9 @@ export class ChatSidebarComponent { readonly forkRequested = output(); constructor() { + // Inject chat lib root CSS custom properties — see ChatComponent + // for the full rationale. Idempotent + lifecycle-guaranteed. + ensureChatRootStyles(); // Publish the right-edge claim while the panel is open. Peer panels // (e.g. chat-debug) read --ngaf-chat-occupy-right to leave room. effect(() => { diff --git a/libs/chat/src/lib/compositions/chat/chat.component.ts b/libs/chat/src/lib/compositions/chat/chat.component.ts index 4af9adfc9..c87452a16 100644 --- a/libs/chat/src/lib/compositions/chat/chat.component.ts +++ b/libs/chat/src/lib/compositions/chat/chat.component.ts @@ -33,7 +33,7 @@ import { createContentClassifier, type ContentClassifier } from '../../streaming import { createPartialArgsBridge, type PartialArgsBridge } from '../../a2ui/partial-args-bridge'; import { createA2uiSurfaceStore, type A2uiSurfaceStore } from '../../a2ui/surface-store'; import { messageContent } from '../shared/message-utils'; -import { CHAT_HOST_TOKENS } from '../../styles/chat-tokens'; +import { CHAT_HOST_TOKENS, ensureChatRootStyles } from '../../styles/chat-tokens'; import type { ChatRenderEvent } from './chat-render-event'; import { CHAT_LIFECYCLE, type ChatLifecycle } from '../../lifecycle'; @@ -395,6 +395,19 @@ export class ChatComponent { private static readonly PIN_TOLERANCE_PX = 150; constructor() { + // Inject the chat lib's root CSS custom properties (--ngaf-chat-bg, + // --ngaf-chat-surface, --ngaf-chat-radius-input, etc.) the first + // time any chat composition is constructed. The module-eval side + // effect that previously handled this is unreliable under + // aggressive production tree-shaking — bundlers that don't see + // the source `chat-tokens.ts` path in the published artifact's + // `sideEffects` glob drop the call entirely, leaving consumers + // with zero token defaults (sidenav has no width, input has no + // border, chips have no chrome — everything renders as plain + // text on the page background). Calling from a constructor that + // is unconditionally reachable from user code defeats that + // tree-shaking and is idempotent. */ + ensureChatRootStyles(); effect(() => { if (this.eventsSubscribed) return; let agent: ReturnType;