Skip to content

Commit b054ff9

Browse files
bloveclaude
andauthored
feat(chat): production-ready visual redesign (0.0.3) (#157)
* chore(workflow): upgrade npm CLI for trusted publishing OIDC support The 0.0.2 publish workflow run failed with 'error retrieving identity token' on @ngaf/licensing and @ngaf/partial-json, and a 404 on @ngaf/a2ui. Root cause: actions/setup-node@v6.3.0 with node-version: 22 ships npm 10.9.2, which has partial OIDC code paths but doesn't fully implement the trusted-publishing flow against npm registry's OIDC endpoint. npm 11.5.1+ is required for trusted publishing. Adding 'npm install -g npm@latest' before the publish step bumps the runner to a current release. Sources: - https://philna.sh/blog/2026/01/28/trusted-publishing-npm/ - npm/cli#8730 - https://docs.npmjs.com/trusted-publishers/ Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(chat): document Tailwind v4 requirement for compositions Verified against a fresh-install consumer of @ngaf/chat@0.0.2: without Tailwind configured (and without `@source "../node_modules/@ngaf/chat"`), ChatComponent's utility classes (flex, gap-3, max-w-[75%], md:flex, ...) are tree-shaken away and the chat collapses to a column of unstyled full-width blocks. The library does not ship a precompiled stylesheet, so this is a hard consumer-side requirement. Surface it explicitly: - Quickstart gets a Tailwind setup step between install and provider config. - Installation Requirements step calls out Tailwind v4 alongside Angular 20+ and Node 18+. - Tailwind CSS section is rewritten with concrete steps (postcss config, @import, @source) rather than just an `npm install` line. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(specs): add chat library redesign design doc Design for production-ready chat UI overhaul: asymmetric message pattern (user bubble + assistant inline), three layout modes (embedded, popup, sidebar), shared chat-trace primitive driving tool calls / subagents / timeline, complete Tailwind removal with encapsulated component styles + optional global chat.css. Settled architecture decisions: - Three separate compositions over a unified mode-switching one. - Hybrid styling: component-encapsulated + CSS vars + optional global stylesheet for deep overrides. - In-place rewrite of <chat>; coordinated breaking-change PR updates all 19 cockpit demos + libs/example-layouts + website docs in one shot. Ships as 0.0.3 (patch-only policy). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(plans): add chat redesign implementation plan 56-task plan covering tokens (Phase 1), new primitives (Phase 2), existing primitive rewrites (Phase 3), trace-based card rewrites (Phase 4), top-level compositions (Phase 5), debug + interrupt panel (Phase 6), library cleanup + 0.0.3 bump (Phase 7), example-layouts (Phase 8), 19 cockpit demos (Phase 9), website docs (Phase 10), and verification + PR (Phase 11). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(chat): add CHAT_HOST_TOKENS design-token constant * feat(chat): ship optional chat.css global stylesheet * feat(chat): add chat-trace primitive Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(chat): add chat-window primitive * feat(chat): add chat-launcher-button primitive * feat(chat): add chat-suggestions primitive * feat(chat): add chat-message primitive (asymmetric user/assistant) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): rename chat-messages -> chat-message-list Renames the ChatMessagesComponent primitive to ChatMessageListComponent with selector chat-message-list, adds dedicated host styles, and updates all consumers (chat composition, chat-debug composition, public-api). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): rewrite chat-input with new pill design * refactor(chat): restyle chat-typing-indicator as 3-dot animation * refactor(chat): restyle chat-error * refactor(chat): restyle chat-interrupt * refactor(chat): restyle chat-thread-list, add optional new-thread button * refactor(chat): restyle chat-generative-ui wrapper * refactor(chat): rewrite chat-tool-call-card on chat-trace * refactor(chat): rewrite chat-subagent-card on chat-trace * refactor(chat): rewrite chat-timeline-slider as vertical history walk * feat(chat): default-render trace-based cards for tool-calls and subagents * refactor(chat): rewrite <chat> composition for new asymmetric design Replaces Tailwind-based layout + inline avatar markup with CSS-custom-property shell layout; wraps each message role in <chat-message> inside the per-role templates; adds <chat-tool-calls> and <chat-subagents> inside the assistant wrapper; introduces prevRole() helper for spacing context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(chat): add chat-popup floating composition * feat(chat): add chat-sidebar slide-in composition * refactor(chat): restyle chat-interrupt-panel Replace Tailwind utility classes with hand-written BEM CSS, rename all --chat-* tokens to --ngaf-chat-*, and add CHAT_HOST_TOKENS to styles array. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): restyle chat-debug + helpers (no Tailwind, new tokens) Replace all Tailwind utility classes with hand-written BEM CSS, rename --chat-* tokens to --ngaf-chat-* across all 8 debug component files, and add CHAT_HOST_TOKENS to every styles array. Inline markdown styles in chat-debug updated to use new token names. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): drop legacy chat-theme/chat-markdown style modules * refactor(chat): update public-api for new components * chore(release): bump @ngaf/chat to 0.0.3 * refactor(example-layouts): rewrite example-chat-layout without Tailwind * refactor(example-layouts): rewrite example-split-layout without Tailwind * chore(cockpit): update demos for @ngaf/chat 0.0.3 (rename + token migration) - cockpit-chat-input: ChatMessagesComponent → ChatMessageListComponent, <chat-messages> → <chat-message-list>, --chat-* → --ngaf-chat-* - cockpit-chat-messages: same rename + token migration; e2e selector chat-messages → chat-message-list - cockpit-chat-theming: THEMES map and template updated to --ngaf-chat-* token names Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(chat): rewrite install/quickstart/theming for 0.0.3 Remove Tailwind requirement, update peer deps, add optional chat.css import, rewrite quickstart to 3-step no-setup flow, and rewrite theming guide with full --ngaf-chat-* token table, light/dark switching, three override paths, and migration note for old --chat-* tokens. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(chat): add chat-popup/chat-sidebar/chat-trace pages + layout-modes guide - Rename chat-messages.mdx → chat-message-list.mdx; update selector and class name throughout - Add chat-popup.mdx, chat-sidebar.mdx, chat-trace.mdx component pages - Add guides/layout-modes.mdx comparing embedded/popup/sidebar modes - Update --chat-* token references to --ngaf-chat-* in all existing component pages (chat-input, chat-interrupt-panel, chat-subagent-card, chat-tool-call-card) - Drop CHAT_THEME_STYLES/CHAT_MARKDOWN_STYLES from chat.mdx; note asymmetric message pattern - Add new pages and layout-modes to docs-config.ts navigation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(chat): drop click-to-focus on input pill (a11y lint) The click handler on the pill div violated angular-eslint's template/click-events-have-key-events and template/interactive-supports-focus rules. The textarea inside the pill receives focus natively when its visible area is clicked, so the handler was a redundant convenience. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent e17e6d6 commit b054ff9

86 files changed

Lines changed: 7366 additions & 1310 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/website/content/docs/chat/components/chat-input.mdx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,14 @@ The component renders a `<form>` containing a `<textarea>` and a `<button>`. It
102102

103103
| Variable | Applied To |
104104
|----------|-----------|
105-
| `--chat-input-bg` | Form background |
106-
| `--chat-input-border` | Form border (unfocused) |
107-
| `--chat-input-focus-border` | Form border (focused) |
108-
| `--chat-radius-input` | Form border radius |
109-
| `--chat-text` | Textarea text color |
110-
| `--chat-font-family` | Textarea font |
111-
| `--chat-send-bg` | Send button background |
112-
| `--chat-send-text` | Send button icon color |
105+
| `--ngaf-chat-surface-alt` | Form background |
106+
| `--ngaf-chat-separator` | Form border (unfocused) |
107+
| `--ngaf-chat-muted` | Form border (focused) |
108+
| `--ngaf-chat-radius-input` | Form border radius |
109+
| `--ngaf-chat-text` | Textarea text color |
110+
| `--ngaf-chat-font-family` | Textarea font |
111+
| `--ngaf-chat-primary` | Send button background |
112+
| `--ngaf-chat-on-primary` | Send button icon color |
113113

114114
## ARIA
115115

apps/website/content/docs/chat/components/chat-interrupt-panel.mdx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,13 @@ The panel uses the chat theme's warning variables:
8787

8888
| Variable | Applied To |
8989
|----------|-----------|
90-
| `--chat-warning-bg` | Panel background |
91-
| `--chat-warning-text` | Header and message text |
92-
| `--chat-border` | Panel border |
93-
| `--chat-radius-card` | Panel border radius |
94-
| `--chat-bg-alt` | Action button backgrounds |
95-
| `--chat-text` | Action button text |
96-
| `--chat-text-muted` | Ignore button text |
90+
| `--ngaf-chat-warning-bg` | Panel background |
91+
| `--ngaf-chat-warning-text` | Header and message text |
92+
| `--ngaf-chat-separator` | Panel border |
93+
| `--ngaf-chat-radius-card` | Panel border radius |
94+
| `--ngaf-chat-surface-alt` | Action button backgrounds |
95+
| `--ngaf-chat-text` | Action button text |
96+
| `--ngaf-chat-text-muted` | Ignore button text |
9797

9898
## Primitive Alternative: ChatInterruptComponent
9999

apps/website/content/docs/chat/components/chat-messages.mdx renamed to apps/website/content/docs/chat/components/chat-message-list.mdx

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
1-
# ChatMessagesComponent
1+
# ChatMessageListComponent
22

3-
`ChatMessagesComponent` is the core primitive for rendering chat messages. It iterates over the messages from a `LangGraphAgent` and renders each one using a matching `MessageTemplateDirective`. This gives you full control over how each message type is displayed.
3+
`ChatMessageListComponent` is the core primitive for rendering chat messages. It iterates over the messages from a `LangGraphAgent` and renders each one using a matching `MessageTemplateDirective`. This gives you full control over how each message type is displayed.
44

5-
**Selector:** `chat-messages`
5+
**Selector:** `chat-message-list`
66

77
**Import:**
88

99
```typescript
1010
import {
11-
ChatMessagesComponent,
11+
ChatMessageListComponent,
1212
MessageTemplateDirective,
1313
getMessageType,
1414
} from '@ngaf/chat';
1515
```
1616

1717
## How It Works
1818

19-
1. The component reads `ref().messages()` to get the current message list
19+
1. The component reads `agent().messages()` to get the current message list
2020
2. For each message, it calls `getMessageType()` to determine the template type
2121
3. It finds the matching `MessageTemplateDirective` among its content children
2222
4. It renders the message using `ngTemplateOutlet` with the message as the implicit context
2323

2424
## Basic Usage
2525

2626
```html
27-
<chat-messages [ref]="chatRef">
27+
<chat-message-list [agent]="chatAgent">
2828
<ng-template chatMessageTemplate="human" let-message>
2929
<div class="user-bubble">{{ message.content }}</div>
3030
</ng-template>
@@ -40,7 +40,7 @@ import {
4040
<ng-template chatMessageTemplate="system" let-message>
4141
<em>{{ message.content }}</em>
4242
</ng-template>
43-
</chat-messages>
43+
</chat-message-list>
4444
```
4545

4646
## API
@@ -49,7 +49,7 @@ import {
4949

5050
| Input | Type | Default | Description |
5151
|-------|------|---------|-------------|
52-
| `ref` | `LangGraphAgent<any, any>` | **Required** | The agent providing streaming state |
52+
| `agent` | `LangGraphAgent<any, any>` | **Required** | The agent providing streaming state |
5353

5454
### Content Children
5555

@@ -144,55 +144,50 @@ import { DomSanitizer } from '@angular/platform-browser';
144144
import { agent } from '@ngaf/langgraph';
145145
import type { BaseMessage } from '@langchain/core/messages';
146146
import {
147-
ChatMessagesComponent,
147+
ChatMessageListComponent,
148148
MessageTemplateDirective,
149-
CHAT_MARKDOWN_STYLES,
150149
renderMarkdown,
151150
} from '@ngaf/chat';
152151
153152
@Component({
154153
selector: 'app-messages-demo',
155154
standalone: true,
156-
imports: [ChatMessagesComponent, MessageTemplateDirective],
157-
styles: [CHAT_MARKDOWN_STYLES],
155+
imports: [ChatMessageListComponent, MessageTemplateDirective],
158156
changeDetection: ChangeDetectionStrategy.OnPush,
159157
template: `
160-
<chat-messages [ref]="chatRef">
158+
<chat-message-list [agent]="chatAgent">
161159
<ng-template chatMessageTemplate="human" let-message>
162-
<div class="flex justify-end mb-4">
163-
<div class="bg-blue-600 text-white rounded-2xl px-4 py-2 max-w-[70%]">
160+
<div style="display: flex; justify-content: flex-end; margin-bottom: 1rem;">
161+
<div style="background: var(--ngaf-chat-primary); color: var(--ngaf-chat-on-primary); border-radius: var(--ngaf-chat-radius-bubble); padding: 0.5rem 1rem; max-width: 70%;">
164162
{{ message.content }}
165163
</div>
166164
</div>
167165
</ng-template>
168166

169167
<ng-template chatMessageTemplate="ai" let-message>
170-
<div class="flex gap-3 mb-4">
171-
<div class="w-8 h-8 bg-gray-200 rounded-lg flex items-center justify-center text-xs font-bold">
172-
AI
173-
</div>
174-
<div class="chat-md flex-1" [innerHTML]="renderMd(message.content)"></div>
168+
<div style="display: flex; gap: 0.75rem; margin-bottom: 1rem;">
169+
<div style="flex: 1;" [innerHTML]="renderMd(message.content)"></div>
175170
</div>
176171
</ng-template>
177172

178173
<ng-template chatMessageTemplate="tool" let-message>
179-
<div class="bg-gray-100 rounded-lg p-3 font-mono text-sm mb-4">
174+
<div style="background: var(--ngaf-chat-surface-alt); border-radius: var(--ngaf-chat-radius-card); padding: 0.75rem; font-family: var(--ngaf-chat-font-mono); font-size: var(--ngaf-chat-font-size-sm); margin-bottom: 1rem;">
180175
{{ message.content }}
181176
</div>
182177
</ng-template>
183178

184179
<ng-template chatMessageTemplate="system" let-message>
185-
<div class="text-center text-gray-500 text-xs italic mb-4">
180+
<div style="text-align: center; color: var(--ngaf-chat-text-muted); font-size: var(--ngaf-chat-font-size-xs); font-style: italic; margin-bottom: 1rem;">
186181
{{ message.content }}
187182
</div>
188183
</ng-template>
189-
</chat-messages>
184+
</chat-message-list>
190185
`,
191186
})
192187
export class MessagesDemoComponent {
193188
private sanitizer = inject(DomSanitizer);
194189
195-
chatRef = agent<{ messages: BaseMessage[] }>({
190+
chatAgent = agent<{ messages: BaseMessage[] }>({
196191
assistantId: 'chat_agent',
197192
threadId: signal(null),
198193
});
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# ChatPopupComponent
2+
3+
`ChatPopupComponent` is a composition that renders a floating launcher button in the bottom-right corner of the screen. Clicking the launcher opens a chat window as an overlay. The window can be toggled open and closed without destroying conversation state.
4+
5+
**Selector:** `chat-popup`
6+
7+
**Import:**
8+
9+
```typescript
10+
import { ChatPopupComponent } from '@ngaf/chat';
11+
```
12+
13+
## When to Use It
14+
15+
Use `ChatPopupComponent` when you want an unobtrusive chat entry point that does not occupy permanent screen real estate. The popup stays out of the way until the user chooses to engage, making it ideal for support chat, contextual help, and assistant features embedded in existing applications.
16+
17+
If you need the chat to always be visible, use [`<chat>`](/docs/chat/components/chat) (embedded) instead. If you need a slide-in panel alongside your app content, use [`<chat-sidebar>`](/docs/chat/components/chat-sidebar).
18+
19+
## Basic Usage
20+
21+
```typescript
22+
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
23+
import { agent } from '@ngaf/langgraph';
24+
import { ChatPopupComponent } from '@ngaf/chat';
25+
26+
@Component({
27+
selector: 'app-root',
28+
standalone: true,
29+
imports: [ChatPopupComponent],
30+
changeDetection: ChangeDetectionStrategy.OnPush,
31+
template: `
32+
<!-- your app content here -->
33+
<chat-popup [agent]="chatAgent" />
34+
`,
35+
})
36+
export class AppComponent {
37+
protected readonly chatAgent = agent({
38+
assistantId: 'chat_agent',
39+
threadId: signal(null),
40+
});
41+
}
42+
```
43+
44+
<Callout type="tip" title="Place at the root level">
45+
`ChatPopupComponent` uses `position: fixed` internally. Place it as a direct child of your root component or app shell so it is not clipped by a parent with `overflow: hidden`.
46+
</Callout>
47+
48+
## API
49+
50+
### Inputs
51+
52+
| Input | Type | Default | Description |
53+
|-------|------|---------|-------------|
54+
| `agent` | `LangGraphAgent<any, any>` | **Required** | The agent providing streaming state |
55+
| `open` | `boolean` | `false` | Two-way bindable. Controls whether the chat window is open |
56+
57+
### Outputs
58+
59+
| Output | Type | Description |
60+
|--------|------|-------------|
61+
| `openChange` | `boolean` | Emits when the window opens or closes. Use with `[(open)]` for two-way binding |
62+
63+
### Slots
64+
65+
| Slot | Selector | Description |
66+
|------|----------|-------------|
67+
| Header content | `[chatHeader]` | Projected into the chat window header bar. Replaces the default title |
68+
69+
### Methods
70+
71+
| Method | Description |
72+
|--------|-------------|
73+
| `toggle()` | Toggles the window between open and closed |
74+
| `openWindow()` | Opens the chat window |
75+
| `closeWindow()` | Closes the chat window |
76+
77+
## Two-Way Binding
78+
79+
Control the open state from your component:
80+
81+
```typescript
82+
@Component({
83+
template: `
84+
<button (click)="popup.openWindow()">Open Chat</button>
85+
<chat-popup [(open)]="chatOpen" [agent]="chatAgent" #popup />
86+
`,
87+
})
88+
export class AppComponent {
89+
chatOpen = false;
90+
chatAgent = agent({ assistantId: 'chat_agent', threadId: signal(null) });
91+
}
92+
```
93+
94+
## Custom Header
95+
96+
Project content into the window header with the `[chatHeader]` slot:
97+
98+
```html
99+
<chat-popup [agent]="chatAgent">
100+
<span chatHeader>Support Chat</span>
101+
</chat-popup>
102+
```
103+
104+
## Styling
105+
106+
The popup uses the standard `--ngaf-chat-*` token system. To adjust the launcher position:
107+
108+
```css
109+
chat-popup {
110+
--ngaf-chat-launcher-offset-x: 1.5rem;
111+
--ngaf-chat-launcher-offset-y: 1.5rem;
112+
}
113+
```
114+
115+
See [Theming](/docs/chat/guides/theming) for the full token reference.

0 commit comments

Comments
 (0)