import type { Meta, StoryObj } from "@storybook/angular"; import { moduleMetadata } from "@storybook/angular"; import { CommonModule } from "@angular/common"; import { Component, Injectable, Input, signal } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { CopilotChatView, CopilotChatMessageView, CopilotChatInput, ChatState, provideCopilotChatLabels, provideCopilotKit, } from "@copilotkit/angular"; import type { Message } from "@ag-ui/client"; @Injectable() class StoryChatState extends ChatState { readonly inputValue = signal(""); submitInput(value: string): void { const trimmed = value.trim(); if (!trimmed) return; console.log("[Storybook] submitInput", trimmed); this.inputValue.set(""); } changeInput(value: string): void { this.inputValue.set(value); } } // Custom input components defined after imports const meta: Meta = { title: "UI/CopilotChatView/Customized with Templates", component: CopilotChatView, decorators: [ moduleMetadata({ imports: [ CommonModule, FormsModule, CopilotChatView, CopilotChatMessageView, CopilotChatInput, ], providers: [ provideCopilotKit({}), provideCopilotChatLabels({ chatInputPlaceholder: "Type a message...", chatDisclaimerText: "AI can make mistakes. Please verify important information.", }), { provide: ChatState, useClass: StoryChatState }, ], }), ], parameters: { layout: "fullscreen", }, }; export default meta; type Story = StoryObj; export const CustomDisclaimerTemplate: Story = { render: () => { const messages: Message[] = [ { id: "user-1", content: "How do I use templates for customization?", role: "user" as const, }, { id: "assistant-1", content: "Templates provide a powerful way to customize components! You can use ng-template with template references to inject custom HTML directly into the component slots.", role: "assistant" as const, }, ]; return { template: `
⚡ Template-based customization - AI assistance at your fingertips!
`, props: { messages, }, }; }, }; // Custom input component for template story @Component({ selector: "template-custom-input", standalone: true, imports: [CommonModule, FormsModule], template: `
Press Enter to send • Powered by Templates
`, }) class TemplateCustomInputComponent { inputValue = ""; constructor(private chat: ChatState) {} sendMessage() { const value = this.inputValue.trim(); if (value) { this.chat.submitInput(value); this.inputValue = ""; } } } export const CustomInputTemplate: Story = { render: () => { const messages: Message[] = [ { id: "user-1", content: "This input is created with a component!", role: "user" as const, }, { id: "assistant-1", content: "Yes! Components with service injection provide complete control over the input area, including custom styling and behavior.", role: "assistant" as const, }, ]; return { template: `
`, props: { messages, customInputComponent: TemplateCustomInputComponent, }, }; }, }; export const CustomScrollButtonTemplate: Story = { render: () => { // Generate many messages to show scroll behavior const messages: Message[] = []; for (let i = 0; i < 25; i++) { messages.push({ id: `msg-${i}`, content: i % 2 === 0 ? `User message ${i}: Template-based scroll button demonstration!` : `Assistant response ${i}: Templates provide maximum flexibility for UI customization, allowing you to create exactly the experience you want.`, role: i % 2 === 0 ? "user" : "assistant", } as Message); } // Simple click handler without DOM manipulation const handleScroll = (onClick: () => void) => { onClick(); }; return { template: `
`, props: { messages, handleScroll, isHovered: false, }, }; }, }; // Custom input component for combined story @Component({ selector: "combined-custom-input", standalone: true, imports: [CommonModule, FormsModule], template: `
`, }) class CombinedCustomInputComponent { inputValue = ""; constructor(private chat: ChatState) {} sendMessage() { const value = this.inputValue.trim(); if (value) { this.chat.submitInput(value); this.inputValue = ""; } } } export const AllTemplatesCombined: Story = { render: () => { const messages: Message[] = [ { id: "user-1", content: "Show me all templates working together!", role: "user" as const, }, { id: "assistant-1", content: "Here you can see custom disclaimer, input, and scroll button templates all working in harmony!", role: "assistant" as const, }, { id: "user-2", content: "This is amazing flexibility!", role: "user" as const, }, { id: "assistant-2", content: "Templates give you complete control over every aspect of the chat interface while maintaining the core functionality.", role: "assistant" as const, }, ]; return { template: `
🚀 All custom templates active!
`, props: { messages, customInputComponent: CombinedCustomInputComponent, }, }; }, };