Angular chat UI components for coding agents

Angular components that render event streams as messages, tool activity, waits, and decisions. Compatible with coding-agent-runner and any backend that emits the same events.

npm install coding-agent-chat

Demo

Conversation replay

Two ConversationEvent[] transcripts rendered with <cac-conversation-view>. Each replay includes a composer and an independent theme control.

1

Happy path

Planning, tool calls, a code response, and orchestrator approval. The composer includes model, reasoning, permission, attachment, and context controls.

Feature run · replay scroll or press play
2

Unhappy path

A failed command, a watchdog timeout, and an orchestrator retry. Failure and success use the same event model.

Bugfix session · replay failing command → watchdog → retry → green
GroupingNine tool calls render as one burst with counts, families, failures, and tests.
FoldingLong plans truncate behind expand, so transcripts stay skimmable.
Progressive disclosureOutput, traces and raw log ranges live one click behind each row.
Live continuationComposer submissions append real turns. Connect them to your backend.

Rendering

Code and images

Syntax highlighting supports 23 grammars. Inline images and artifact.image events open in a host-provided lightbox. Select an image to open the gallery.

Rendering · static transcripthighlighted code · artifact row · inline image

Syntax highlighting uses lowlight with a size guard and per-block memoization. CHAT_MEDIA_LIGHTBOX handles image clicks; the host provides the overlay.

Overview

What's in the library

23+ event kinds · 7 entry points · 379 green specs · zero Angular imports in core · Apache-2.0

Event kinds

One append-only ConversationEvent union covers messages, tool bursts, waits, and orchestrator decisions. Unknown kinds render as fallback rows.

Framework-free core

coding-agent-chat/core is plain TypeScript: the ConversationEvent contract plus projectConversation(). Backends, SSR code and tests can import it without Angular.

Entry points

Seven import paths keep optional features separate. core is plain TypeScript with no Angular dependency. View samples.

Host integration points

Four injection tokens have safe defaults. Provide CHAT_MEDIA_LIGHTBOX to enable image enlargement. See the rendering demo.

Theme

Import the optional stylesheet for the default theme. Set data-studio-theme="light" on any parent for the light palette.

Zoneless & OnPush

Signal-based components with strict templates, shipped as a partial-Ivy package. This page runs without Zone.js.

Tests

246 component specs run against the built package. Projection and markdown utilities have 133 additional specs.

Documentation

Getting started

1Install

terminal
npm install coding-agent-chat
# peer deps: @angular/core, @angular/common, @angular/forms (>=21 <22), rxjs ~7.8

2Provide

app.config.ts
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideCodingAgentChat } from 'coding-agent-chat';

export const appConfig: ApplicationConfig = {
  providers: [
    // Every integration point has a safe default.
    provideCodingAgentChat(),
  ],
};

3Render

run-view.ts
// any component
import { ConversationViewComponent } from 'coding-agent-chat/conversation';
import type { ConversationEvent } from 'coding-agent-chat/core';

@Component({
  imports: [ConversationViewComponent],
  template: '<cac-conversation-view [events]="events()" />',
})
export class RunView {
  readonly events = signal<readonly ConversationEvent[]>([]);
}

Entry point samples

Seven import paths keep the wire contract, renderer, and optional features separate. Each tab shows a component sample and its code. The root import coding-agent-chat re-exports all entry points and provideCodingAgentChat().

ConversationEvent and projectConversation() are plain TypeScript with no Angular dependency.

live
ConversationNext-gen chat preview
  1. You
    1. The date formatter breaks on 29 Feb — fix it and add a spec.

  2. Agent
    1. Fixed — the formatter used getYear() % 4 for leap years; it now delegates to Intl:

      const fmt = new Intl.DateTimeFormat(locale, { dateStyle: "medium" });
      return fmt.format(date); // 29 Feb 2024 ✓

      Two new specs cover the leap-day and the 1900 century case — 7/7 pass.

Host integration points

All four integration points have defaults. Configure task links, image enlargement, history data, or history confirmation as needed.

CHAT_TASK_REFERENCE_PROVIDERmarkdown

Supplies task references for markdown auto-linking and handles navigation when one is clicked. Default: task keys render as plain text.

CHAT_MEDIA_LIGHTBOXshared

Owns the click-to-enlarge image overlay (modal stack, focus trap). Default: images do not zoom.

PROJECT_CHAT_DATA_SOURCEhistory

The scroll/search/stats/turn transport behind <cac-project-chat-list>. Default: an empty history.

CHAT_HISTORY_CONFIRMhistory

Confirmation prompt before loading an entire deep history. Default: auto-confirm.

app.config.ts: root tokens
// Both services bind via useExisting and retain their root instances.
provideCodingAgentChat({
  taskReferences: TaskReferenceNavigationService, // implements ChatTaskReferenceProvider
  mediaLightbox: MediaLightboxService,            // implements ChatMediaLightbox
});
app.config.ts: history tokens
// The history entry point adds two more seams, provided directly:
import { CHAT_HISTORY_CONFIRM, PROJECT_CHAT_DATA_SOURCE } from 'coding-agent-chat/history';

providers: [
  // scroll/search/stats/turn transport behind <cac-project-chat-list>
  { provide: PROJECT_CHAT_DATA_SOURCE, useClass: MyProjectChatDataSource },
  // guard prompt before loading an entire deep history (defaults to auto-confirm)
  { provide: CHAT_HISTORY_CONFIRM, useClass: MyHistoryConfirm },
];
the data-source contract
// Four read methods. This page implements them in memory.
export interface ProjectChatDataSource {
  scroll(project: string, request: ProjectChatScrollRequest): Observable<ProjectChatScrollResponse>;
  search(project: string, query: string, limit: number): Observable<ProjectChatSearchResponse>;
  stats(project: string): Observable<ProjectChatStatsResponse>;
  turn(project: string, turnId: string): Observable<ProjectChatTurnResponse>;
}
core only: no renderer
// Angular-free core for backends, SSR, and tests.
// contract + projection without pulling in the renderer.
import { projectConversation } from 'coding-agent-chat/core';
import type { ConversationEvent } from 'coding-agent-chat/core';

// Convert CLI lines, timeline data, tokens, screenshots, and commits
// into an ordered ConversationEvent[].
const events: ConversationEvent[] = projectConversation({ source: jobId, lines });

Theming

Import the optional stylesheet. Set data-studio-theme on <html>. Override CSS variables after the import to change colors.

styles.scss: import
/* styles.scss */
@import 'coding-agent-chat/theme/cac-theme.css';
template.html: theme scope
<!-- Dark is the default. Use "dark" to force it. -->
<html lang="en" data-studio-theme="light">
  ...
</html>
styles.scss: token overrides
/* styles.scss, after the theme import */
:root {
  --studio-accent: #7c3aed;
  --studio-on-accent: #ffffff;
  --studio-accent-2: #0f766e;
}