Render · API Reference

views()

Creates an immutable view registry mapping names to Angular components. Views are rendered inline in the chat when the agent produces a JSON spec.

Usage

import { views } from '@threadplane/render';
 
const ui = views({
  'plan-checklist': PlanChecklistComponent,
  'file-preview': FilePreviewComponent,
  'code-output': CodeOutputComponent,
});

Pass to the chat component:

<chat [agent]="stream" [views]="ui" />

Or provide globally via DI:

import { provideViews } from '@threadplane/render';
 
// app.config.ts
providers: [provideViews(ui)]

Composition

Compose registries via object spread. Last key wins for overrides:

const all = views({
  ...thirdPartyViews,
  ...myViews,
  'chart': MyCustomChart, // overrides thirdPartyViews['chart']
});

Per-View Fallbacks

Each entry can be a bare component or a { component, fallback } object. The fallback mounts while a state-bound prop on the view is still unresolved -- the same shape defineAngularRegistry() accepts:

const ui = views({
  'plan-checklist': {
    component: PlanChecklistComponent,
    fallback: PlanSkeletonComponent,
  },
  'file-preview': FilePreviewComponent, // bare form uses the default fallback
});

withViews() and overrideViews() accept the same object form in their addition and override maps.

API

views(map)

Creates a frozen ViewRegistry from a Record<string, Type<unknown>>.

ParameterTypeDescription
mapRecord<string, Type<unknown>>Name → Angular component mapping
ReturnsViewRegistryFrozen immutable registry

withViews(base, additions)

Adds new views without overwriting existing entries. Existing keys in base are preserved.

const extended = withViews(base, {
  'new-widget': NewWidget,    // added
  'existing': Other,          // IGNORED — base already has 'existing'
});

overrideViews(base, overrides)

Replaces entries in a registry. Keys in overrides win over base. Use this when you want to swap an existing renderer; use withViews when you want to add new node types without touching existing ones.

import { overrideViews } from '@threadplane/render';
 
const myRegistry = overrideViews(baseRegistry, {
  'code-block': MyCodeBlockComponent,
});

Returns a new frozen ViewRegistry. The base argument is not mutated.

withoutViews(base, ...names)

Removes views by name:

const restricted = withoutViews(base, 'dangerous-widget', 'internal-tool');

provideViews(registry)

Angular DI provider. Works at app level or route level:

// Global
providers: [provideViews(ui)]
 
// Route-scoped
{ path: 'planning', providers: [provideViews(planningViews)] }

<render-spec> and <render-element> consume VIEW_REGISTRY as a third-priority fallback in registry resolution. The full priority order is: the [registry] template input → RENDER_CONFIG.registry (from provideRender(...)) → VIEW_REGISTRY (from provideViews(...)) → the existing empty fallback. So provideViews(myRegistry) drives rendering when no provideRender({ registry }) is wired and no [registry] input is bound.

toRenderRegistry(registry)

Converts a ViewRegistry to the low-level AngularRegistry type used by <render-spec>. Called internally by the chat component — most developers won't need this.

View Components

View components are standard Angular standalone components. They receive props as input() signals:

@Component({
  selector: 'plan-checklist',
  standalone: true,
  template: `
    <div class="border rounded-xl p-4">
      <h4>{{ title() }}</h4>
      <ng-content />
    </div>
  `,
})
export class PlanChecklistComponent {
  readonly title = input<string>('Plan');
}

How Specs Are Detected

The chat component checks each message for a ui field containing a valid spec:

{
  "type": "tool",
  "content": "...",
  "ui": {
    "root": "plan",
    "elements": {
      "plan": {
        "type": "plan-checklist",
        "props": { "title": "My Plan" }
      }
    }
  }
}

The type in the spec is matched against the view registry to resolve the Angular component.

viewsfunction

Creates a view registry from a name → component map.

views(map: Record<string, Type<unknown> | RenderViewEntry>): ViewRegistry

Parameters

ParameterTypeDescription
mapRecord<string, Type<unknown> | RenderViewEntry>

Returns

ViewRegistry

Examples

const registry = views({ metric: MetricCardComponent, chart: ChartComponent });
// providers: [provideViews(registry)]