From bc9346c2a64370e1ad8d33d263a9a2cecbc4fa0c Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 19 May 2026 13:59:01 -0700 Subject: [PATCH 1/9] =?UTF-8?q?feat(demo):=20drawer-mode=20breakpoint=2010?= =?UTF-8?q?24=20=E2=86=92=20768=20(tablet=20widths=20get=20expanded=20side?= =?UTF-8?q?nav)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/angular/src/app/shell/demo-shell.component.css | 2 +- examples/chat/angular/src/app/shell/demo-shell.component.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/chat/angular/src/app/shell/demo-shell.component.css b/examples/chat/angular/src/app/shell/demo-shell.component.css index be95153e..7b7a40f2 100644 --- a/examples/chat/angular/src/app/shell/demo-shell.component.css +++ b/examples/chat/angular/src/app/shell/demo-shell.component.css @@ -46,7 +46,7 @@ .demo-shell__main[data-sidenav-mode="collapsed"] { padding-left: var(--ngaf-chat-sidenav-width-collapsed, 56px); } -@media (max-width: 1023px) { +@media (max-width: 767px) { .demo-shell__main[data-sidenav-mode] { padding-left: 0; } } diff --git a/examples/chat/angular/src/app/shell/demo-shell.component.ts b/examples/chat/angular/src/app/shell/demo-shell.component.ts index 75c703dd..8be5a14e 100644 --- a/examples/chat/angular/src/app/shell/demo-shell.component.ts +++ b/examples/chat/angular/src/app/shell/demo-shell.component.ts @@ -201,15 +201,15 @@ export class DemoShell { /** * User's chosen desktop sidenav mode. Persisted across reloads. - * Below 1024px the shell ignores this and forces drawer mode. + * Below 768px the shell ignores this and forces drawer mode. */ private readonly storedDesktopMode = signal<'expanded' | 'collapsed'>( (this.persistence.read('sidenavMode') as 'expanded' | 'collapsed' | null) ?? 'expanded', ); - /** Computed sidenav mode: viewport forces drawer below 1024px, else user preference. */ + /** Computed sidenav mode: viewport forces drawer below 768px, else user preference. */ protected readonly sidenavMode = computed(() => - this.viewportWidth() >= 1024 ? this.storedDesktopMode() : 'drawer', + this.viewportWidth() >= 768 ? this.storedDesktopMode() : 'drawer', ); /** Active threads filtered by the selected project (or all when none selected). */ From bc1b15efe1caf6b893175cd89eaa3a1af28ce5e1 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 19 May 2026 13:59:18 -0700 Subject: [PATCH 2/9] feat(demo): drawerOpen always starts false; stop persisting drawer state --- examples/chat/angular/src/app/shell/demo-shell.component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/chat/angular/src/app/shell/demo-shell.component.ts b/examples/chat/angular/src/app/shell/demo-shell.component.ts index 8be5a14e..2e69ac65 100644 --- a/examples/chat/angular/src/app/shell/demo-shell.component.ts +++ b/examples/chat/angular/src/app/shell/demo-shell.component.ts @@ -180,8 +180,9 @@ export class DemoShell { (this.persistence.read('colorScheme') as 'light' | 'dark' | null) ?? 'dark', ); - /** Whether the threads drawer is open. Persisted across reloads. */ - protected readonly drawerOpen = signal(this.persistence.read('drawerOpen') ?? false); + /** Whether the threads drawer is open. Always starts closed on a fresh + * load — drawer mode is a transient UI state, not a persisted preference. */ + protected readonly drawerOpen = signal(false); /** Whether the Cmd+K search palette is open. */ protected readonly paletteOpen = signal(false); @@ -399,7 +400,6 @@ export class DemoShell { protected onSidenavOpenChange(next: boolean): void { this.drawerOpen.set(next); - this.persistence.write('drawerOpen', next); } protected toggleSidenav(): void { From f4f2ea45eb86f26135ffa20456ec67efd3556fa4 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 19 May 2026 14:00:01 -0700 Subject: [PATCH 3/9] feat(chat): chat-window body matches input-gap padding-top when header is empty --- libs/chat/src/lib/styles/chat-window.styles.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/chat/src/lib/styles/chat-window.styles.ts b/libs/chat/src/lib/styles/chat-window.styles.ts index bb97c35b..006ac27e 100644 --- a/libs/chat/src/lib/styles/chat-window.styles.ts +++ b/libs/chat/src/lib/styles/chat-window.styles.ts @@ -21,6 +21,11 @@ export const CHAT_WINDOW_STYLES = ` color: var(--ngaf-chat-primary); } .chat-window__header:empty { display: none; } + /* When the consumer doesn't project a header, balance the chat content + * with breathing room at the top equivalent to the input gap at the bottom. */ + .chat-window__header:empty + .chat-window__body { + padding-top: var(--ngaf-chat-input-gap); + } .chat-window__body { flex: 1; min-height: 0; From 6cbeecbcad657cd3ade4cf57132f3d8211f77418 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 19 May 2026 14:00:21 -0700 Subject: [PATCH 4/9] feat(demo): chat-sidebar panel top:0 so close button aligns with toolbar hamburger --- .../chat/angular/src/app/shell/demo-shell.component.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/chat/angular/src/app/shell/demo-shell.component.css b/examples/chat/angular/src/app/shell/demo-shell.component.css index 7b7a40f2..a46ee7d7 100644 --- a/examples/chat/angular/src/app/shell/demo-shell.component.css +++ b/examples/chat/angular/src/app/shell/demo-shell.component.css @@ -185,6 +185,11 @@ * viewport minus the toolbar. */ height: calc(100% - var(--demo-toolbar-height)); } +/* chat-sidebar panel renders top-aligned with the page, NOT under the + * toolbar — so the panel's close button sits at the same viewport-y as + * the hamburger inside the toolbar (both at surface-top + 8 padding). + * The panel's z-index is below the toolbar's so the toolbar still + * renders above it where they overlap on the right edge. */ .demo-shell ::ng-deep .chat-sidebar__panel { - top: var(--demo-toolbar-height); + top: 0; } From 501c0ab840d221daa71d47cfe8ba42abceb42e50 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 19 May 2026 14:01:04 -0700 Subject: [PATCH 5/9] chore(release): bump publishable libs to 0.0.45 --- libs/a2ui/package.json | 2 +- libs/ag-ui/package.json | 2 +- libs/chat/package.json | 2 +- libs/langgraph/package.json | 2 +- libs/licensing/package.json | 2 +- libs/render/package.json | 2 +- libs/telemetry/package.json | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libs/a2ui/package.json b/libs/a2ui/package.json index f4cc6a31..31feb71f 100644 --- a/libs/a2ui/package.json +++ b/libs/a2ui/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/a2ui", - "version": "0.0.44", + "version": "0.0.45", "license": "MIT", "repository": { "type": "git", diff --git a/libs/ag-ui/package.json b/libs/ag-ui/package.json index af761f51..7c89258c 100644 --- a/libs/ag-ui/package.json +++ b/libs/ag-ui/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/ag-ui", - "version": "0.0.44", + "version": "0.0.45", "peerDependencies": { "@ngaf/chat": "*", "@ngaf/licensing": "*", diff --git a/libs/chat/package.json b/libs/chat/package.json index 560b8a3c..ac3b474d 100644 --- a/libs/chat/package.json +++ b/libs/chat/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/chat", - "version": "0.0.44", + "version": "0.0.45", "exports": { "./chat.css": "./chat.css", "./themes/default-dark.css": "./themes/default-dark.css", diff --git a/libs/langgraph/package.json b/libs/langgraph/package.json index 4d401722..73de7b33 100644 --- a/libs/langgraph/package.json +++ b/libs/langgraph/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/langgraph", - "version": "0.0.44", + "version": "0.0.45", "peerDependencies": { "@ngaf/chat": "*", "@ngaf/licensing": "*", diff --git a/libs/licensing/package.json b/libs/licensing/package.json index 2d2f9d43..69b1dcdd 100644 --- a/libs/licensing/package.json +++ b/libs/licensing/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/licensing", - "version": "0.0.44", + "version": "0.0.45", "license": "MIT", "repository": { "type": "git", diff --git a/libs/render/package.json b/libs/render/package.json index 22699762..374ff84b 100644 --- a/libs/render/package.json +++ b/libs/render/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/render", - "version": "0.0.44", + "version": "0.0.45", "peerDependencies": { "@angular/core": "^20.0.0 || ^21.0.0", "@angular/common": "^20.0.0 || ^21.0.0", diff --git a/libs/telemetry/package.json b/libs/telemetry/package.json index 118a9bc9..9e61a460 100644 --- a/libs/telemetry/package.json +++ b/libs/telemetry/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/telemetry", - "version": "0.0.44", + "version": "0.0.45", "license": "MIT", "publishConfig": { "access": "public" From 74edc997f9dd0307bace68c6154b05c3d3b11b6b Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 19 May 2026 14:06:38 -0700 Subject: [PATCH 6/9] fix(chat): collapse chat-sidenav header slot when empty (matches chat-window pattern) --- libs/chat/src/lib/styles/chat-sidenav.styles.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/chat/src/lib/styles/chat-sidenav.styles.ts b/libs/chat/src/lib/styles/chat-sidenav.styles.ts index d56d4def..c3f28deb 100644 --- a/libs/chat/src/lib/styles/chat-sidenav.styles.ts +++ b/libs/chat/src/lib/styles/chat-sidenav.styles.ts @@ -65,6 +65,10 @@ export const CHAT_SIDENAV_STYLES = ` flex-shrink: 0; padding: var(--ngaf-chat-space-3); } + /* Collapse the header slot when the consumer doesn't project content + * — matches the chat-window pattern. Avoids 24px of dead space above + * the New chat button. */ + .chat-sidenav__header:empty { display: none; } .chat-sidenav__topbar { flex-shrink: 0; display: flex; From c5c17a96ed13d1837ccb7a78d1a9c7011192478e Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 19 May 2026 14:12:19 -0700 Subject: [PATCH 7/9] feat(chat): add modal z-index tokens; palette uses var(--ngaf-chat-z-modal*) so it stays above drawer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Search palette was at z 51/50 — below drawer (1001), toolbar (50), interrupt-panel (999), subagents (997). Opening the palette while the drawer was open swallowed every click through to the drawer underneath. Adds --ngaf-chat-z-modal-scrim: 1100 and --ngaf-chat-z-modal: 1101 to LAYER_TOKENS. Palette scrim + dialog use those tokens. Palette now sits cleanly above drawer / toolbar / notifications at all viewport widths. --- libs/a2ui/package.json | 2 +- libs/ag-ui/package.json | 2 +- libs/chat/package.json | 2 +- .../src/lib/styles/chat-history-search-palette.styles.ts | 4 ++-- libs/chat/src/lib/styles/chat-tokens.ts | 6 +++++- libs/langgraph/package.json | 2 +- libs/licensing/package.json | 2 +- libs/render/package.json | 2 +- libs/telemetry/package.json | 2 +- 9 files changed, 14 insertions(+), 10 deletions(-) diff --git a/libs/a2ui/package.json b/libs/a2ui/package.json index 31feb71f..a5bcc9d5 100644 --- a/libs/a2ui/package.json +++ b/libs/a2ui/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/a2ui", - "version": "0.0.45", + "version": "0.0.46", "license": "MIT", "repository": { "type": "git", diff --git a/libs/ag-ui/package.json b/libs/ag-ui/package.json index 7c89258c..839eaccc 100644 --- a/libs/ag-ui/package.json +++ b/libs/ag-ui/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/ag-ui", - "version": "0.0.45", + "version": "0.0.46", "peerDependencies": { "@ngaf/chat": "*", "@ngaf/licensing": "*", diff --git a/libs/chat/package.json b/libs/chat/package.json index ac3b474d..ff0db163 100644 --- a/libs/chat/package.json +++ b/libs/chat/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/chat", - "version": "0.0.45", + "version": "0.0.46", "exports": { "./chat.css": "./chat.css", "./themes/default-dark.css": "./themes/default-dark.css", diff --git a/libs/chat/src/lib/styles/chat-history-search-palette.styles.ts b/libs/chat/src/lib/styles/chat-history-search-palette.styles.ts index 366909f5..3a6cdecd 100644 --- a/libs/chat/src/lib/styles/chat-history-search-palette.styles.ts +++ b/libs/chat/src/lib/styles/chat-history-search-palette.styles.ts @@ -6,7 +6,7 @@ export const CHAT_HISTORY_SEARCH_PALETTE_STYLES = ` position: fixed; inset: 0; background: rgba(0, 0, 0, 0.4); - z-index: 50; + z-index: var(--ngaf-chat-z-modal-scrim, 1100); border: 0; padding: 0; cursor: pointer; @@ -22,7 +22,7 @@ export const CHAT_HISTORY_SEARCH_PALETTE_STYLES = ` border: 1px solid var(--ngaf-chat-separator); border-radius: 12px; box-shadow: 0 16px 48px rgba(0, 0, 0, 0.25); - z-index: 51; + z-index: var(--ngaf-chat-z-modal, 1101); display: flex; flex-direction: column; overflow: hidden; diff --git a/libs/chat/src/lib/styles/chat-tokens.ts b/libs/chat/src/lib/styles/chat-tokens.ts index 6cb8745e..a0f494a3 100644 --- a/libs/chat/src/lib/styles/chat-tokens.ts +++ b/libs/chat/src/lib/styles/chat-tokens.ts @@ -122,10 +122,14 @@ const SPACING_TOKENS = ` const LAYER_TOKENS = ` /* Z-index layers — documented for consumers + future primitives. - * Default values listed; overridable per-app via :root or :host. */ + * Default values listed; overridable per-app via :root or :host. + * Modal layers sit above drawer so palettes/dialogs stay reachable + * when the drawer is open. */ --ngaf-chat-z-overlay-content: 30; /* chat-sidebar panel, chat-popup window */ --ngaf-chat-z-drawer-scrim: 1000; /* chat-sidenav-scrim backdrop */ --ngaf-chat-z-drawer: 1001; /* chat-sidenav drawer mode host */ + --ngaf-chat-z-modal-scrim: 1100; /* chat-history-search-palette backdrop */ + --ngaf-chat-z-modal: 1101; /* chat-history-search-palette dialog */ `; const EDGE_CLAIM_TOKENS = ` diff --git a/libs/langgraph/package.json b/libs/langgraph/package.json index 73de7b33..b85b225a 100644 --- a/libs/langgraph/package.json +++ b/libs/langgraph/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/langgraph", - "version": "0.0.45", + "version": "0.0.46", "peerDependencies": { "@ngaf/chat": "*", "@ngaf/licensing": "*", diff --git a/libs/licensing/package.json b/libs/licensing/package.json index 69b1dcdd..e35502ab 100644 --- a/libs/licensing/package.json +++ b/libs/licensing/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/licensing", - "version": "0.0.45", + "version": "0.0.46", "license": "MIT", "repository": { "type": "git", diff --git a/libs/render/package.json b/libs/render/package.json index 374ff84b..b44fba78 100644 --- a/libs/render/package.json +++ b/libs/render/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/render", - "version": "0.0.45", + "version": "0.0.46", "peerDependencies": { "@angular/core": "^20.0.0 || ^21.0.0", "@angular/common": "^20.0.0 || ^21.0.0", diff --git a/libs/telemetry/package.json b/libs/telemetry/package.json index 9e61a460..ee6071d3 100644 --- a/libs/telemetry/package.json +++ b/libs/telemetry/package.json @@ -1,6 +1,6 @@ { "name": "@ngaf/telemetry", - "version": "0.0.45", + "version": "0.0.46", "license": "MIT", "publishConfig": { "access": "public" From 5fe65139b88ce1b11abcc15bf6117f031bf2c98c Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 19 May 2026 16:50:12 -0700 Subject: [PATCH 8/9] fix(demo): welcome-suggestions chip no longer clips at narrow viewports At <320px viewports the chat-welcome-suggestion host's default display:inline-block sized to button content (~220px wide), overflowing the .welcome-suggestions__featured wrapper that flex-shrank to ~180px. overflow:hidden on the wrapper then clipped the button right edge. Fix demo-side via ::ng-deep: force chat-welcome-suggestion host to display:block + width:100% so it follows the wrapper's flex-shrunk width. Label inside ellipsizes correctly. Also adds 12px horizontal padding on the welcome-suggestions host so the row doesn't overflow viewport edges on the smallest screens. --- .../modes/welcome-suggestions.component.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/examples/chat/angular/src/app/modes/welcome-suggestions.component.ts b/examples/chat/angular/src/app/modes/welcome-suggestions.component.ts index 2e2d10b7..59b2298a 100644 --- a/examples/chat/angular/src/app/modes/welcome-suggestions.component.ts +++ b/examples/chat/angular/src/app/modes/welcome-suggestions.component.ts @@ -43,11 +43,15 @@ import { FEATURED_SUGGESTIONS, MORE_SUGGESTIONS } from './welcome-suggestions'; :host { display: flex; justify-content: center; + width: 100%; + padding: 0 12px; + box-sizing: border-box; } .welcome-suggestions__row { display: flex; align-items: center; gap: 12px; + max-width: 100%; } .welcome-suggestions__featured { flex: 1 1 0; @@ -55,10 +59,25 @@ import { FEATURED_SUGGESTIONS, MORE_SUGGESTIONS } from './welcome-suggestions'; max-width: 380px; overflow: hidden; } + /* chat-welcome-suggestion host is display: inline-block by default + * (sizes to content). At narrow viewports this lets the inner + * button overflow the wrapper and get clipped at the wrapper's + * right edge ("hard right border"). Force block sizing here so + * the host follows the wrapper's flex-shrunk width and the + * label inside ellipsizes. */ + .welcome-suggestions__featured ::ng-deep chat-welcome-suggestion { + display: block; + width: 100%; + } + .welcome-suggestions__featured ::ng-deep .chat-welcome-suggestion { + width: 100%; + } .welcome-suggestions__featured ::ng-deep .chat-welcome-suggestion__label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + min-width: 0; + flex: 1 1 auto; } /* Make the "More prompts" dropdown match the featured chip visually. Scoped to .welcome-suggestions__row so the model picker (also From 10b85ab6dd0c079c16aaf829774703e2225b7e80 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 20 May 2026 08:44:33 -0700 Subject: [PATCH 9/9] =?UTF-8?q?fix(chat):=20rename=20chat-sidenav-scrim=20?= =?UTF-8?q?(close)=20=E2=86=92=20(dismissed)=20=E2=80=94=20lint:=20no-outp?= =?UTF-8?q?ut-native?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/angular/src/app/shell/demo-shell.component.html | 2 +- .../chat-sidenav-scrim.component.spec.ts | 8 ++++---- .../chat-sidenav-scrim/chat-sidenav-scrim.component.ts | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/chat/angular/src/app/shell/demo-shell.component.html b/examples/chat/angular/src/app/shell/demo-shell.component.html index e8402678..ca88accd 100644 --- a/examples/chat/angular/src/app/shell/demo-shell.component.html +++ b/examples/chat/angular/src/app/shell/demo-shell.component.html @@ -60,7 +60,7 @@ { expect(btn.getAttribute('aria-label')).toBe('Close conversations'); }); - it('emits (close) on click', () => { + it('emits (dismissed) on click', () => { fx.componentRef.setInput('open', true); fx.detectChanges(); - let closed = false; - fx.componentInstance.close.subscribe(() => { closed = true; }); + let dismissed = false; + fx.componentInstance.dismissed.subscribe(() => { dismissed = true; }); const btn = fx.nativeElement.querySelector('button.chat-sidenav-scrim__button') as HTMLButtonElement; btn.click(); - expect(closed).toBe(true); + expect(dismissed).toBe(true); }); }); diff --git a/libs/chat/src/lib/primitives/chat-sidenav-scrim/chat-sidenav-scrim.component.ts b/libs/chat/src/lib/primitives/chat-sidenav-scrim/chat-sidenav-scrim.component.ts index 41d107f0..d6a9bd06 100644 --- a/libs/chat/src/lib/primitives/chat-sidenav-scrim/chat-sidenav-scrim.component.ts +++ b/libs/chat/src/lib/primitives/chat-sidenav-scrim/chat-sidenav-scrim.component.ts @@ -8,7 +8,7 @@ import { ChangeDetectionStrategy, Component, input, output } from '@angular/core * and the drawer host (escapes the drawer host's stacking context). * * Usage: - * + * * */ @Component({ @@ -21,7 +21,7 @@ import { ChangeDetectionStrategy, Component, input, output } from '@angular/core type="button" class="chat-sidenav-scrim__button" aria-label="Close conversations" - (click)="close.emit()" + (click)="dismissed.emit()" > } `, @@ -44,5 +44,5 @@ export class ChatSidenavScrimComponent { /** When true, render the backdrop button covering the viewport. */ readonly open = input(false); /** Fires when the user clicks the backdrop. */ - readonly close = output(); + readonly dismissed = output(); }