1import {
2  ChangeDetectionStrategy,
3  Component,
4  inject,
5  input,
6} from "@angular/core";
7import { ActivatedRoute } from "@angular/router";
8
9import { FeatureHeaderComponent } from "./feature-header.component";
10import { ShowcaseChatHostComponent } from "./showcase-chat-host.component";
11
12interface ShowcaseReasoningMessage {
13  content?: string;
14}
15
16@Component({
17  selector: "showcase-custom-reasoning-message",
18  changeDetection: ChangeDetectionStrategy.OnPush,
19  host: {
20    "data-testid": "reasoning-block",
21    "data-message-role": "reasoning",
22    class: "custom-reasoning",
23  },
24  template: `
25    <header><span>Reasoning</span> Agent reasoning</header>
26    @if (message().content) {
27      <p>{{ message().content }}</p>
28    }
29  `,
30  styles: `
31    :host {
32      display: block;
33      margin: 0.5rem 0;
34      padding: 0.75rem;
35      border: 1px solid #f0c94a;
36      border-radius: 0.6rem;
37      color: #713f12;
38      background: #fffbeb;
39    }
40    header {
41      display: flex;
42      align-items: center;
43      gap: 0.55rem;
44      font-size: 0.82rem;
45      font-weight: 600;
46    }
47    header span {
48      padding: 0.2rem 0.4rem;
49      border: 1px solid #d6a72a;
50      border-radius: 999px;
51      background: #fff;
52      font-size: 0.62rem;
53      letter-spacing: 0.1em;
54      text-transform: uppercase;
55    }
56    p {
57      margin: 0.5rem 0 0;
58      white-space: pre-wrap;
59      color: #854d0e;
60      font-style: italic;
61      line-height: 1.5;
62    }
63  `,
64})
65export class CustomReasoningMessageComponent {
66  readonly message = input.required<ShowcaseReasoningMessage>();
67}
68
69@Component({
70  selector: "showcase-reasoning-feature",
71  imports: [FeatureHeaderComponent, ShowcaseChatHostComponent],
72  changeDetection: ChangeDetectionStrategy.OnPush,
73  host: { class: "feature-page" },
74  template: `
75    <showcase-feature-header />
76    <main class="reasoning-page">
77      <section>
78        <p class="feature-eyebrow">Reasoning message slot</p>
79        <h1>{{ custom ? "Custom reasoning" : "Default reasoning" }}</h1>
80        <p>
81          {{
82            custom
83              ? "A native Angular component renders first-class reasoning messages."
84              : "The built-in expandable renderer handles reasoning with no configuration."
85          }}
86        </p>
87      </section>
88      <div class="chat-surface">
89        <showcase-chat-host
90          [reasoningMessageComponent]="custom ? customRenderer : undefined"
91        />
92      </div>
93    </main>
94  `,
95  styles: `
96    .reasoning-page {
97      display: grid;
98      min-height: 0;
99      grid-template-rows: auto minmax(0, 1fr);
100      padding: 1rem;
101      background: #eef3f7;
102    }
103    .reasoning-page > section {
104      width: min(52rem, 100%);
105      margin: 0 auto;
106      padding: 0.75rem 1rem 0;
107    }
108    h1 {
109      margin: 0.25rem 0;
110      font-size: 1.5rem;
111    }
112    section > p:last-child {
113      margin: 0;
114      color: #52637a;
115    }
116    .chat-surface {
117      width: min(56rem, calc(100% - 2rem));
118      min-height: 0;
119      margin: 1rem auto;
120    }
121  `,
122})
123export class ReasoningFeatureComponent {
124  private readonly route = inject(ActivatedRoute);
125  protected readonly feature =
126    (this.route.snapshot.data["feature"] as string | undefined) ??
127    "reasoning-default";
128  protected readonly custom = this.feature === "reasoning-custom";
129  protected readonly customRenderer = CustomReasoningMessageComponent;
130}
131
4a65196ee
CopilotKit Showcase