1import {
2 ChangeDetectionStrategy,
3 Component,
4 computed,
5 inject,
6 Injector,
7 signal,
8} from "@angular/core";
9import { ActivatedRoute } from "@angular/router";
10import { connectAgentContext, CopilotKit } from "@copilotkit/angular";
11
12import { agentIdForRoute } from "../../feature-agent";
13import { FeatureHeaderComponent } from "../feature-header.component";
14import { ShowcaseChatHostComponent } from "../showcase-chat-host.component";
15import {
16 AgentConfigCardComponent,
17 AuthCardComponent,
18 DEFAULT_AGENT_CONFIG,
19} from "./app-settings-cards";
20import type {
21 AgentConfig,
22 Expertise,
23 ResponseLength,
24 Tone,
25} from "./app-settings-cards";
26
27const DEMO_AUTH_HEADERS: Readonly<Record<string, string>> = {
28 Authorization: "Bearer demo-token-123",
29};
30
31@Component({
32 selector: "showcase-app-settings-feature",
33 imports: [
34 AgentConfigCardComponent,
35 AuthCardComponent,
36 FeatureHeaderComponent,
37 ShowcaseChatHostComponent,
38 ],
39 changeDetection: ChangeDetectionStrategy.OnPush,
40 host: { class: "feature-page" },
41 template: `
42 <showcase-feature-header />
43 @if (feature === "auth" && !signedIn()) {
44 <main class="sign-in-page">
45 <showcase-auth-card [authenticated]="false" (signIn)="signIn()" />
46 </main>
47 } @else {
48 <main class="settings-page">
49 <section class="settings-panel">
50 @if (feature === "auth") {
51 <showcase-auth-card [authenticated]="true" (signOut)="signOut()" />
52 } @else {
53 <showcase-agent-config-card
54 [config]="config()"
55 (toneChange)="setTone($event)"
56 (expertiseChange)="setExpertise($event)"
57 (responseLengthChange)="setResponseLength($event)"
58 />
59 }
60 </section>
61 <section class="chat-surface" aria-label="CopilotKit assistant">
62 <showcase-chat-host
63 [agentId]="agentId"
64 [headers]="feature === 'auth' ? authHeaders : undefined"
65 />
66 </section>
67 </main>
68 }
69 `,
70 styles: `
71 .sign-in-page {
72 display: grid;
73 min-height: 0;
74 place-items: center;
75 padding: 1.5rem;
76 background: radial-gradient(circle at 50% 20%, #eef2ff, #eef3f7 55%);
77 }
78 .settings-page {
79 display: grid;
80 min-height: 0;
81 grid-template-rows: auto minmax(0, 1fr);
82 gap: 1rem;
83 padding: 1rem;
84 background: #eef3f7;
85 }
86 .settings-panel {
87 min-width: 0;
88 }
89 .chat-surface {
90 min-height: 0;
91 overflow: hidden;
92 border: 1px solid #d8e0ea;
93 border-radius: 1rem;
94 background: #fff;
95 }
96 `,
97})
98export class AppSettingsFeatureComponent {
99 private readonly route = inject(ActivatedRoute);
100 private readonly injector = inject(Injector);
101 protected readonly feature =
102 (this.route.snapshot.data["feature"] as string | undefined) ??
103 "agent-config";
104 protected readonly signedIn = signal(false);
105 protected readonly config = signal<AgentConfig>({ ...DEFAULT_AGENT_CONFIG });
106 protected readonly authHeaders = DEMO_AUTH_HEADERS;
107 protected readonly agentId = agentIdForRoute(this.feature, this.route);
108 private readonly configContext = computed(() => ({
109 description:
110 "Agent response preferences. Apply tone, expertise level, and response length to every reply.",
111 value: JSON.stringify(this.config()),
112 }));
113
114 constructor() {
115 if (this.feature === "agent-config") {
116 connectAgentContext(this.configContext);
117 }
118 }
119
120 protected signIn(): void {
121 this.signedIn.set(true);
122 }
123
124 protected signOut(): void {
125 this.injector.get(CopilotKit).updateRuntime({ headers: {} });
126 this.signedIn.set(false);
127 }
128
129 protected setTone(tone: Tone): void {
130 this.updateConfig({ tone });
131 }
132
133 protected setExpertise(expertise: Expertise): void {
134 this.updateConfig({ expertise });
135 }
136
137 protected setResponseLength(responseLength: ResponseLength): void {
138 this.updateConfig({ responseLength });
139 }
140
141 private updateConfig(patch: Partial<AgentConfig>): void {
142 this.config.update((current) => ({ ...current, ...patch }));
143 }
144}
145