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