Skip to content

Commit e3ac24a

Browse files
authored
feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27)
* docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning.
1 parent 2f85d4a commit e3ac24a

52 files changed

Lines changed: 1720 additions & 1233 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.
Lines changed: 5 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,22 @@
1-
import { Component, computed } from '@angular/core';
2-
import { LegacyChatComponent } from '@cacheplane/chat';
1+
import { Component } from '@angular/core';
2+
import { ChatComponent } from '@cacheplane/chat';
33
import { streamResource } from '@cacheplane/stream-resource';
44
import { environment } from '../environments/environment';
55

6-
interface ToolCallEntry {
7-
name: string;
8-
args: string;
9-
result?: string;
10-
}
11-
126
/**
137
* FilesystemComponent demonstrates agent file operations.
148
*
15-
* The agent can read and write files using tool calls. The sidebar
16-
* shows a real-time log of each file operation as it happens.
17-
*
18-
* Key integration points:
19-
* - `stream.messages()` contains all messages including tool call results
20-
* - `computed()` derives tool call entries from AI messages
21-
* - Tool calls update reactively as the agent performs file operations
9+
* The agent can read and write files using tool calls.
2210
*/
2311
@Component({
2412
selector: 'app-filesystem',
2513
standalone: true,
26-
imports: [LegacyChatComponent],
27-
template: `
28-
<cp-chat
29-
[messages]="stream.messages()"
30-
[isLoading]="stream.isLoading()"
31-
[error]="stream.error()"
32-
(sendMessage)="send($event)">
33-
<ng-template #sidebar>
34-
<h3 style="font-size: 0.8rem; font-weight: 600; margin-bottom: 0.75rem; color: #1a1a2e;">File Operations</h3>
35-
@for (entry of toolCallEntries(); track $index) {
36-
<div style="display: flex; align-items: flex-start; gap: 8px; padding: 6px 0; font-size: 0.8rem; border-bottom: 1px solid #e5e7eb;">
37-
<span style="flex-shrink: 0; font-size: 1rem; line-height: 1.2;">
38-
{{ entry.name === 'read_file' ? '📖' : '✏️' }}
39-
</span>
40-
<div style="min-width: 0;">
41-
<div style="font-weight: 500; color: #1a1a2e; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
42-
{{ getFilePath(entry.args) }}
43-
</div>
44-
<div style="color: #8b8fa3; font-size: 0.75rem; margin-top: 2px;">
45-
{{ entry.name === 'read_file' ? 'read' : 'write' }}
46-
{{ entry.result ? ' · done' : ' · running…' }}
47-
</div>
48-
</div>
49-
</div>
50-
}
51-
@empty {
52-
<p style="color: #8b8fa3; font-size: 0.8rem;">Ask the agent to read or write a file.</p>
53-
}
54-
</ng-template>
55-
</cp-chat>
56-
`,
14+
imports: [ChatComponent],
15+
template: `<chat [ref]="stream" class="block h-screen" />`,
5716
})
5817
export class FilesystemComponent {
5918
protected readonly stream = streamResource({
6019
apiUrl: environment.langGraphApiUrl,
6120
assistantId: environment.streamingAssistantId,
6221
});
63-
64-
toolCallEntries = computed(() => {
65-
const msg = this.stream.messages();
66-
const calls: ToolCallEntry[] = [];
67-
for (const m of msg) {
68-
if ((m as any).tool_calls) {
69-
for (const tc of (m as any).tool_calls) {
70-
calls.push({ name: tc.name, args: JSON.stringify(tc.args), result: tc.output });
71-
}
72-
}
73-
}
74-
return calls;
75-
});
76-
77-
getFilePath(args: string): string {
78-
try {
79-
const parsed = JSON.parse(args);
80-
return parsed.path ?? args;
81-
} catch {
82-
return args;
83-
}
84-
}
85-
86-
send(text: string): void {
87-
this.stream.submit({ messages: [{ role: 'human', content: text }] });
88-
}
8922
}

cockpit/deep-agents/filesystem/angular/src/styles.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@import "../../../../../libs/design-tokens/src/lib/tokens.css";
22
@import "tailwindcss";
3+
@source "../../../../../libs/chat/src/";
34

45
@theme {
56
--color-bg: var(--ds-bg);
Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,23 @@
1-
import { Component, computed } from '@angular/core';
2-
import { LegacyChatComponent } from '@cacheplane/chat';
1+
import { Component } from '@angular/core';
2+
import { ChatComponent } from '@cacheplane/chat';
33
import { streamResource } from '@cacheplane/stream-resource';
44
import { environment } from '../environments/environment';
55

66
/**
77
* MemoryComponent demonstrates persistent agent memory across sessions.
88
*
99
* The agent extracts facts about the user from each conversation turn
10-
* and stores them in `agent_memory` state. The sidebar shows all learned
11-
* facts in real time as the agent updates its memory.
12-
*
13-
* Key integration points:
14-
* - `stream.value()` contains the agent state including `agent_memory`
15-
* - `computed()` derives key/value pairs for the sidebar
16-
* - Memory entries update reactively as the agent learns new facts
10+
* and stores them in `agent_memory` state.
1711
*/
1812
@Component({
1913
selector: 'app-da-memory',
2014
standalone: true,
21-
imports: [LegacyChatComponent],
22-
template: `
23-
<cp-chat
24-
[messages]="stream.messages()"
25-
[isLoading]="stream.isLoading()"
26-
[error]="stream.error()"
27-
(sendMessage)="send($event)">
28-
<ng-template #sidebar>
29-
<h3 style="font-size: 0.8rem; font-weight: 600; margin-bottom: 0.75rem; color: #1a1a2e;">Learned Facts</h3>
30-
@for (entry of memoryEntries(); track entry[0]) {
31-
<div style="padding: 6px 0; font-size: 0.8rem; border-bottom: 1px solid #e5e7eb;">
32-
<div style="font-weight: 600; color: #1a1a2e; margin-bottom: 2px;">{{ entry[0] }}</div>
33-
<div style="color: #555770;">{{ entry[1] }}</div>
34-
</div>
35-
}
36-
@empty {
37-
<p style="color: #8b8fa3; font-size: 0.8rem;">Tell the agent something about yourself to see it remember.</p>
38-
}
39-
</ng-template>
40-
</cp-chat>
41-
`,
15+
imports: [ChatComponent],
16+
template: `<chat [ref]="stream" class="block h-screen" />`,
4217
})
4318
export class MemoryComponent {
4419
protected readonly stream = streamResource({
4520
apiUrl: environment.langGraphApiUrl,
4621
assistantId: environment.streamingAssistantId,
4722
});
48-
49-
memoryEntries = computed(() => {
50-
const val = this.stream.value() as { agent_memory?: Record<string, string> } | undefined;
51-
return Object.entries(val?.agent_memory ?? {});
52-
});
53-
54-
send(text: string): void {
55-
this.stream.submit({ messages: [{ role: 'human', content: text }] });
56-
}
5723
}

cockpit/deep-agents/memory/angular/src/styles.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@import "../../../../../libs/design-tokens/src/lib/tokens.css";
22
@import "tailwindcss";
3+
@source "../../../../../libs/chat/src/";
34

45
@theme {
56
--color-bg: var(--ds-bg);
Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,23 @@
1-
import { Component, computed } from '@angular/core';
2-
import { LegacyChatComponent } from '@cacheplane/chat';
1+
import { Component } from '@angular/core';
2+
import { ChatComponent } from '@cacheplane/chat';
33
import { streamResource } from '@cacheplane/stream-resource';
44
import { environment } from '../environments/environment';
55

6-
interface PlanStep {
7-
title: string;
8-
status: 'pending' | 'running' | 'complete';
9-
}
10-
116
/**
127
* PlanningComponent demonstrates agent task decomposition.
138
*
149
* The agent receives a complex task, breaks it into ordered steps,
15-
* and executes them. The sidebar shows each step's status in real time.
16-
*
17-
* Key integration points:
18-
* - `stream.value()` contains the plan state with step list
19-
* - `computed()` derives the plan steps for the sidebar
20-
* - Steps update reactively as the agent works through them
10+
* and executes them sequentially.
2111
*/
2212
@Component({
2313
selector: 'app-planning',
2414
standalone: true,
25-
imports: [LegacyChatComponent],
26-
template: `
27-
<cp-chat
28-
[messages]="stream.messages()"
29-
[isLoading]="stream.isLoading()"
30-
[error]="stream.error()"
31-
(sendMessage)="send($event)">
32-
<ng-template #sidebar>
33-
<h3 style="font-size: 0.8rem; font-weight: 600; margin-bottom: 0.75rem; color: #1a1a2e;">Task Plan</h3>
34-
@for (step of planSteps(); track $index) {
35-
<div style="display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 0.8rem;">
36-
<span style="width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;"
37-
[style.background]="step.status === 'complete' ? '#10b981' : step.status === 'running' ? '#004090' : '#d1d5db'"></span>
38-
<span [style.color]="step.status === 'complete' ? '#555770' : '#1a1a2e'"
39-
[style.textDecoration]="step.status === 'complete' ? 'line-through' : 'none'">
40-
{{ step.title }}
41-
</span>
42-
</div>
43-
}
44-
@empty {
45-
<p style="color: #8b8fa3; font-size: 0.8rem;">Ask a complex question to see the plan.</p>
46-
}
47-
</ng-template>
48-
</cp-chat>
49-
`,
15+
imports: [ChatComponent],
16+
template: `<chat [ref]="stream" class="block h-screen" />`,
5017
})
5118
export class PlanningComponent {
5219
protected readonly stream = streamResource({
5320
apiUrl: environment.langGraphApiUrl,
5421
assistantId: environment.streamingAssistantId,
5522
});
56-
57-
planSteps = computed(() => {
58-
const val = this.stream.value() as { plan?: PlanStep[] } | undefined;
59-
return val?.plan ?? [];
60-
});
61-
62-
send(text: string): void {
63-
this.stream.submit({ messages: [{ role: 'human', content: text }] });
64-
}
6523
}

cockpit/deep-agents/planning/angular/src/styles.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@import "../../../../../libs/design-tokens/src/lib/tokens.css";
22
@import "tailwindcss";
3+
@source "../../../../../libs/chat/src/";
34

45
@theme {
56
--color-bg: var(--ds-bg);
Lines changed: 5 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,23 @@
1-
import { Component, computed } from '@angular/core';
2-
import { LegacyChatComponent } from '@cacheplane/chat';
1+
import { Component } from '@angular/core';
2+
import { ChatComponent } from '@cacheplane/chat';
33
import { streamResource } from '@cacheplane/stream-resource';
44
import { environment } from '../environments/environment';
55

6-
interface ExecutionLog {
7-
code: string;
8-
stdout: string;
9-
exitStatus: number;
10-
}
11-
126
/**
137
* SandboxesComponent demonstrates a coding agent that executes Python code.
148
*
159
* The agent writes and runs code snippets to solve problems using a
16-
* `run_code` tool. The sidebar shows execution logs — code input, stdout
17-
* output, and exit status — for each sandbox execution.
18-
*
19-
* Key integration points:
20-
* - `stream.messages()` contains all messages including tool call results
21-
* - `computed()` derives execution log entries from tool calls in AI messages
22-
* - Logs update reactively as the agent writes and runs code
10+
* `run_code` tool.
2311
*/
2412
@Component({
2513
selector: 'app-sandboxes',
2614
standalone: true,
27-
imports: [LegacyChatComponent],
28-
template: `
29-
<cp-chat
30-
[messages]="stream.messages()"
31-
[isLoading]="stream.isLoading()"
32-
[error]="stream.error()"
33-
(sendMessage)="send($event)">
34-
<ng-template #sidebar>
35-
<h3 style="font-size: 0.8rem; font-weight: 600; margin-bottom: 0.75rem; color: #1a1a2e;">Execution Logs</h3>
36-
@for (log of executionLogs(); track $index) {
37-
<div style="padding: 8px; margin-bottom: 8px; border: 1px solid #e5e7eb; border-radius: 6px; font-size: 0.8rem;">
38-
<div style="display: flex; align-items: center; gap: 6px; margin-bottom: 6px;">
39-
<span style="display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.7rem; font-weight: 600; font-family: monospace;"
40-
[style.background]="log.exitStatus === 0 ? '#d1fae5' : '#fee2e2'"
41-
[style.color]="log.exitStatus === 0 ? '#065f46' : '#991b1b'">
42-
exit {{ log.exitStatus }}
43-
</span>
44-
</div>
45-
<div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 4px; padding: 6px; margin-bottom: 6px; font-family: monospace; font-size: 0.75rem; color: #1a1a2e; white-space: pre-wrap; word-break: break-all;">{{ log.code }}</div>
46-
@if (log.stdout) {
47-
<div style="font-size: 0.7rem; font-weight: 600; color: #6b7280; margin-bottom: 2px; text-transform: uppercase; letter-spacing: 0.04em;">stdout</div>
48-
<div style="background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 4px; padding: 6px; font-family: monospace; font-size: 0.75rem; color: #166534; white-space: pre-wrap; word-break: break-all;">{{ log.stdout }}</div>
49-
}
50-
</div>
51-
}
52-
@empty {
53-
<p style="color: #8b8fa3; font-size: 0.8rem;">Ask the agent to write and run Python code.</p>
54-
}
55-
</ng-template>
56-
</cp-chat>
57-
`,
15+
imports: [ChatComponent],
16+
template: `<chat [ref]="stream" class="block h-screen" />`,
5817
})
5918
export class SandboxesComponent {
6019
protected readonly stream = streamResource({
6120
apiUrl: environment.langGraphApiUrl,
6221
assistantId: environment.streamingAssistantId,
6322
});
64-
65-
executionLogs = computed(() => {
66-
const msgs = this.stream.messages();
67-
const logs: ExecutionLog[] = [];
68-
for (const m of msgs) {
69-
if ((m as any).tool_calls) {
70-
for (const tc of (m as any).tool_calls) {
71-
if (tc.name === 'run_code' && tc.output) {
72-
try {
73-
const parsed = JSON.parse(tc.output);
74-
logs.push({
75-
code: tc.args?.code ?? '',
76-
stdout: parsed.stdout ?? '',
77-
exitStatus: parsed.exit_status ?? 0,
78-
});
79-
} catch {
80-
logs.push({
81-
code: tc.args?.code ?? '',
82-
stdout: tc.output,
83-
exitStatus: 0,
84-
});
85-
}
86-
}
87-
}
88-
}
89-
}
90-
return logs;
91-
});
92-
93-
send(text: string): void {
94-
this.stream.submit({ messages: [{ role: 'human', content: text }] });
95-
}
9623
}

cockpit/deep-agents/sandboxes/angular/src/styles.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@import "../../../../../libs/design-tokens/src/lib/tokens.css";
22
@import "tailwindcss";
3+
@source "../../../../../libs/chat/src/";
34

45
@theme {
56
--color-bg: var(--ds-bg);

0 commit comments

Comments
 (0)