@dataclouder/ngx-agent-cards 0.1.68 → 0.1.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable, inject, signal, computed, RendererFactory2, ApplicationRef, Injector, EnvironmentInjector, createComponent, ChangeDetectionStrategy, Component, output, Input, input, effect, ViewChild, DestroyRef, ElementRef, Pipe, ChangeDetectorRef, EventEmitter, Output } from '@angular/core';
|
|
2
|
+
import { InjectionToken, Injectable, inject, signal, computed, RendererFactory2, ApplicationRef, Injector, EnvironmentInjector, createComponent, ChangeDetectionStrategy, Component, output, Input, input, effect, ViewChild, DestroyRef, HostBinding, ElementRef, Pipe, ChangeDetectorRef, EventEmitter, Output } from '@angular/core';
|
|
3
3
|
import * as i1 from '@dataclouder/ngx-core';
|
|
4
4
|
import { MoodStateOptions, LANGUAGES, EntityCommunicationService, TOAST_ALERTS_TOKEN, APP_CONFIG, AudioSpeed as AudioSpeed$1, LoadingBarService, EModelQuality, getLangDesc, AudioSpeedReverse, formatCamelCaseString, getSupportedLanguageOptions, AudioNotificationService, MoodState, SUPPORTED_LANGUAGES, FormUtilsService, EntityBaseFormComponent, DcManageableFormComponent, DcLearnableFormComponent, EntityBaseListComponent, DCFilterBarComponent, QuickTableComponent, EmptyStateComponent } from '@dataclouder/ngx-core';
|
|
5
5
|
import { UserService } from '@dataclouder/ngx-users';
|
|
6
|
+
import { AiWhisperService, TtsService, NgxAiServicesService, GeneratedAssetsService, VoiceSelectorComponent } from '@dataclouder/ngx-ai-services';
|
|
6
7
|
import * as i2$1 from '@angular/common';
|
|
7
8
|
import { DOCUMENT, CommonModule, DecimalPipe, KeyValuePipe, DatePipe, SlicePipe, JsonPipe } from '@angular/common';
|
|
8
9
|
import { nanoid } from 'nanoid';
|
|
9
|
-
import { TtsService, NgxVertexService, GeneratedAssetsService, VoiceSelectorComponent } from '@dataclouder/ngx-vertex';
|
|
10
10
|
import { DynamicDialogRef, DynamicDialogConfig, DialogService, DynamicDialogModule } from 'primeng/dynamicdialog';
|
|
11
11
|
import * as i2 from 'primeng/button';
|
|
12
12
|
import { ButtonModule } from 'primeng/button';
|
|
@@ -1458,6 +1458,7 @@ class DefaultAgentCardsService extends EntityCommunicationService {
|
|
|
1458
1458
|
this.userService = inject(UserService);
|
|
1459
1459
|
this.toastService = inject(TOAST_ALERTS_TOKEN);
|
|
1460
1460
|
this.appConfig = inject(APP_CONFIG);
|
|
1461
|
+
this.whisperService = inject(AiWhisperService);
|
|
1461
1462
|
this.randomSeed = Math.floor(Math.random() * 100000);
|
|
1462
1463
|
}
|
|
1463
1464
|
partialUpdateAgentCard(agentCard) {
|
|
@@ -1521,7 +1522,8 @@ class DefaultAgentCardsService extends EntityCommunicationService {
|
|
|
1521
1522
|
async getAudioTranscriptions(audio, options) {
|
|
1522
1523
|
// Note: Original service in agent-cards.service.ts used 'node' as host. Using 'primary'.
|
|
1523
1524
|
// 'options' parameter corresponds to 'metadata' in the original service.
|
|
1524
|
-
return this.
|
|
1525
|
+
return this.whisperService.transcribeBytes(audio, options);
|
|
1526
|
+
// return this.httpService.uploadFile<TranscriptionsWhisper>(`${Endpoints.TranscribeAudio}?provider=groq`, audio, options, this.appConfig.aiServicesUrl);
|
|
1525
1527
|
}
|
|
1526
1528
|
async callInstruction(prompt, model) {
|
|
1527
1529
|
const messages = [{ content: prompt, role: ChatRole.User }];
|
|
@@ -2626,13 +2628,10 @@ ${task.task}
|
|
|
2626
2628
|
});
|
|
2627
2629
|
}
|
|
2628
2630
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: EvaluationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2629
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: EvaluationService
|
|
2631
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: EvaluationService }); }
|
|
2630
2632
|
}
|
|
2631
2633
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: EvaluationService, decorators: [{
|
|
2632
|
-
type: Injectable
|
|
2633
|
-
args: [{
|
|
2634
|
-
providedIn: 'root',
|
|
2635
|
-
}]
|
|
2634
|
+
type: Injectable
|
|
2636
2635
|
}] });
|
|
2637
2636
|
|
|
2638
2637
|
class DynamicFlowTaskService {
|
|
@@ -2688,13 +2687,10 @@ class DynamicFlowTaskService {
|
|
|
2688
2687
|
}
|
|
2689
2688
|
}
|
|
2690
2689
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicFlowTaskService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2691
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicFlowTaskService
|
|
2690
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicFlowTaskService }); }
|
|
2692
2691
|
}
|
|
2693
2692
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicFlowTaskService, decorators: [{
|
|
2694
|
-
type: Injectable
|
|
2695
|
-
args: [{
|
|
2696
|
-
providedIn: 'root',
|
|
2697
|
-
}]
|
|
2693
|
+
type: Injectable
|
|
2698
2694
|
}] });
|
|
2699
2695
|
|
|
2700
2696
|
const DEFUALT_USER_AVATAR = 'assets/defaults/avatar_user.jpg';
|
|
@@ -2953,18 +2949,15 @@ class ConversationService {
|
|
|
2953
2949
|
return this.ttsService.getTextAudioFile(ttsRequest);
|
|
2954
2950
|
}
|
|
2955
2951
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ConversationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2956
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ConversationService
|
|
2952
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ConversationService }); }
|
|
2957
2953
|
}
|
|
2958
2954
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ConversationService, decorators: [{
|
|
2959
|
-
type: Injectable
|
|
2960
|
-
args: [{
|
|
2961
|
-
providedIn: 'root',
|
|
2962
|
-
}]
|
|
2955
|
+
type: Injectable
|
|
2963
2956
|
}] });
|
|
2964
2957
|
|
|
2965
2958
|
class AIGenerationService {
|
|
2966
2959
|
constructor() {
|
|
2967
|
-
this.vertexService = inject(
|
|
2960
|
+
this.vertexService = inject(NgxAiServicesService);
|
|
2968
2961
|
this.messagesStateService = inject(MessagesStateService);
|
|
2969
2962
|
this.conversationService = inject(ConversationService);
|
|
2970
2963
|
}
|
|
@@ -3477,10 +3470,14 @@ function extractTags(text) {
|
|
|
3477
3470
|
}
|
|
3478
3471
|
|
|
3479
3472
|
class MessageContentDisplayer {
|
|
3473
|
+
get hostHighlightColor() {
|
|
3474
|
+
return this.highlightColor();
|
|
3475
|
+
}
|
|
3480
3476
|
constructor() {
|
|
3481
3477
|
// Inputs
|
|
3482
3478
|
this.message = input.required(...(ngDevMode ? [{ debugName: "message" }] : []));
|
|
3483
3479
|
this.markWord = input(...(ngDevMode ? [undefined, { debugName: "markWord" }] : []));
|
|
3480
|
+
this.highlightColor = input(...(ngDevMode ? [undefined, { debugName: "highlightColor" }] : []));
|
|
3484
3481
|
// Outputs
|
|
3485
3482
|
this.playAudio = output();
|
|
3486
3483
|
this.audioCompleted = output();
|
|
@@ -3682,13 +3679,25 @@ class MessageContentDisplayer {
|
|
|
3682
3679
|
console.log('debug', this.wordWithMeta());
|
|
3683
3680
|
}
|
|
3684
3681
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: MessageContentDisplayer, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3685
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: MessageContentDisplayer, isStandalone: true, selector: "message-content-displayer", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, markWord: { classPropertyName: "markWord", publicName: "markWord", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted", wordClicked: "wordClicked" }, ngImport: i0, template: "<div class=\"audio-text-sync-container\">\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @switch (iconState()) { @case ('loading') { <i class=\"spin-animation pi pi-spinner-dotted\"></i> } @case ('playing') { <i class=\"pi pi-volume-up\"></i> }\n @case ('paused') { <i class=\"pi pi-pause-circle\"></i> } @case ('playable') { <dc-icon name=\"play\"></dc-icon> } }\n </i>\n\n <!-- Display transcription with highlighting -->\n <p style=\"width: 100%\" [class]=\"'text-content ' + classTag()\">\n @for (word of wordWithMeta(); track trackByIndex($index, word)) {\n <span\n [class]=\"word.tag\"\n [class.highlight]=\"word.isHighlighted\"\n [class.marked]=\"word.marked\"\n (click)=\"onWordClick(word)\"\n [attr.id]=\"'word-' + message().messageId + '-' + word.index\"\n >{{ word.word }}\n </span>\n }\n </p>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:2px;align-items:center}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px;margin-top:4px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color
|
|
3682
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: MessageContentDisplayer, isStandalone: true, selector: "message-content-displayer", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, markWord: { classPropertyName: "markWord", publicName: "markWord", isSignal: true, isRequired: false, transformFunction: null }, highlightColor: { classPropertyName: "highlightColor", publicName: "highlightColor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted", wordClicked: "wordClicked" }, host: { properties: { "style.--highlight-bg-color": "this.hostHighlightColor" } }, ngImport: i0, template: "<div class=\"audio-text-sync-container\">\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @switch (iconState()) { @case ('loading') { <i class=\"spin-animation pi pi-spinner-dotted\"></i> } @case ('playing') { <i class=\"pi pi-volume-up\"></i> }\n @case ('paused') { <i class=\"pi pi-pause-circle\"></i> } @case ('playable') { <dc-icon name=\"play\"></dc-icon> } }\n </i>\n\n <!-- Display transcription with highlighting -->\n <p style=\"width: 100%\" [class]=\"'text-content ' + classTag()\">\n @for (word of wordWithMeta(); track trackByIndex($index, word)) {\n <span\n [class]=\"word.tag\"\n [class.highlight]=\"word.isHighlighted\"\n [class.marked]=\"word.marked\"\n (click)=\"onWordClick(word)\"\n [attr.id]=\"'word-' + message().messageId + '-' + word.index\"\n >{{ word.word }}\n </span>\n }\n </p>\n</div>\n@if (message().imageUrl) {\n<div class=\"tour-image-container\" style=\"display: flex; justify-content: center\">\n <img [src]=\"message().imageUrl\" alt=\"Tour Image\" style=\"max-width: 220px; height: auto\" />\n</div>\n}\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:2px;align-items:center}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px;margin-top:4px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color:var(--highlight-bg-color, rgb(60, 216, 255));border-radius:4px}::ng-deep .marked{position:relative;color:#f8bfbc}::ng-deep .marked:before{content:\"\";z-index:-2;left:-.1em;top:.1em;border-width:2px;border-style:solid;border-color:#f27068;position:absolute;border-right-color:transparent;width:100%;height:1em;transform:rotate(2deg);opacity:.7;border-radius:50%;padding:.1em .25em}::ng-deep .marked:after{content:\"\";z-index:-1;left:-.1em;top:.4em;padding:.1em .25em;border-width:2px;border-style:solid;border-color:#f27068;border-left-color:transparent;border-top-color:transparent;position:absolute;width:100%;height:1em;transform:rotate(-1deg);opacity:.7;border-radius:50%}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.em{font-style:italic;color:#b8d0fc}.strong{font-weight:700;color:inherit}.italic{font-style:italic;color:#0d5cf0}.em_strong{font-weight:700;font-style:italic;color:inherit}\n"], dependencies: [{ kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3686
3683
|
}
|
|
3687
3684
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: MessageContentDisplayer, decorators: [{
|
|
3688
3685
|
type: Component,
|
|
3689
|
-
args: [{ selector: 'message-content-displayer', standalone: true, imports: [IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"audio-text-sync-container\">\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @switch (iconState()) { @case ('loading') { <i class=\"spin-animation pi pi-spinner-dotted\"></i> } @case ('playing') { <i class=\"pi pi-volume-up\"></i> }\n @case ('paused') { <i class=\"pi pi-pause-circle\"></i> } @case ('playable') { <dc-icon name=\"play\"></dc-icon> } }\n </i>\n\n <!-- Display transcription with highlighting -->\n <p style=\"width: 100%\" [class]=\"'text-content ' + classTag()\">\n @for (word of wordWithMeta(); track trackByIndex($index, word)) {\n <span\n [class]=\"word.tag\"\n [class.highlight]=\"word.isHighlighted\"\n [class.marked]=\"word.marked\"\n (click)=\"onWordClick(word)\"\n [attr.id]=\"'word-' + message().messageId + '-' + word.index\"\n >{{ word.word }}\n </span>\n }\n </p>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:2px;align-items:center}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px;margin-top:4px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color
|
|
3690
|
-
}], ctorParameters: () => []
|
|
3686
|
+
args: [{ selector: 'message-content-displayer', standalone: true, imports: [IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"audio-text-sync-container\">\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @switch (iconState()) { @case ('loading') { <i class=\"spin-animation pi pi-spinner-dotted\"></i> } @case ('playing') { <i class=\"pi pi-volume-up\"></i> }\n @case ('paused') { <i class=\"pi pi-pause-circle\"></i> } @case ('playable') { <dc-icon name=\"play\"></dc-icon> } }\n </i>\n\n <!-- Display transcription with highlighting -->\n <p style=\"width: 100%\" [class]=\"'text-content ' + classTag()\">\n @for (word of wordWithMeta(); track trackByIndex($index, word)) {\n <span\n [class]=\"word.tag\"\n [class.highlight]=\"word.isHighlighted\"\n [class.marked]=\"word.marked\"\n (click)=\"onWordClick(word)\"\n [attr.id]=\"'word-' + message().messageId + '-' + word.index\"\n >{{ word.word }}\n </span>\n }\n </p>\n</div>\n@if (message().imageUrl) {\n<div class=\"tour-image-container\" style=\"display: flex; justify-content: center\">\n <img [src]=\"message().imageUrl\" alt=\"Tour Image\" style=\"max-width: 220px; height: auto\" />\n</div>\n}\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:2px;align-items:center}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px;margin-top:4px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color:var(--highlight-bg-color, rgb(60, 216, 255));border-radius:4px}::ng-deep .marked{position:relative;color:#f8bfbc}::ng-deep .marked:before{content:\"\";z-index:-2;left:-.1em;top:.1em;border-width:2px;border-style:solid;border-color:#f27068;position:absolute;border-right-color:transparent;width:100%;height:1em;transform:rotate(2deg);opacity:.7;border-radius:50%;padding:.1em .25em}::ng-deep .marked:after{content:\"\";z-index:-1;left:-.1em;top:.4em;padding:.1em .25em;border-width:2px;border-style:solid;border-color:#f27068;border-left-color:transparent;border-top-color:transparent;position:absolute;width:100%;height:1em;transform:rotate(-1deg);opacity:.7;border-radius:50%}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.em{font-style:italic;color:#b8d0fc}.strong{font-weight:700;color:inherit}.italic{font-style:italic;color:#0d5cf0}.em_strong{font-weight:700;font-style:italic;color:inherit}\n"] }]
|
|
3687
|
+
}], ctorParameters: () => [], propDecorators: { hostHighlightColor: [{
|
|
3688
|
+
type: HostBinding,
|
|
3689
|
+
args: ['style.--highlight-bg-color']
|
|
3690
|
+
}] } });
|
|
3691
3691
|
|
|
3692
|
+
/**
|
|
3693
|
+
* @Injectable
|
|
3694
|
+
*
|
|
3695
|
+
* @description
|
|
3696
|
+
* This service orchestrates the playback of chat messages, ensuring that they are played sequentially.
|
|
3697
|
+
* It manages a queue of messages and handles the generation and playback of audio for each message.
|
|
3698
|
+
*
|
|
3699
|
+
* @see ChatMessageOrchestratorComponent
|
|
3700
|
+
*/
|
|
3692
3701
|
class MessageOrchestrationService {
|
|
3693
3702
|
constructor() {
|
|
3694
3703
|
// Services
|
|
@@ -3709,6 +3718,13 @@ class MessageOrchestrationService {
|
|
|
3709
3718
|
this.preGenerationInProgress = signal(false, ...(ngDevMode ? [{ debugName: "preGenerationInProgress" }] : []));
|
|
3710
3719
|
console.log('MessageOrchestrationService initialized');
|
|
3711
3720
|
}
|
|
3721
|
+
/**
|
|
3722
|
+
* @description
|
|
3723
|
+
* Starts the orchestration process for a list of messages.
|
|
3724
|
+
*
|
|
3725
|
+
* @param messages - An array of `MessageContent` objects to be orchestrated.
|
|
3726
|
+
* @param role - The `ChatRole` of the messages' author.
|
|
3727
|
+
*/
|
|
3712
3728
|
startOrchestration(messages, role) {
|
|
3713
3729
|
this.messages.set([...messages]);
|
|
3714
3730
|
this.messageRole.set(role);
|
|
@@ -3723,6 +3739,12 @@ class MessageOrchestrationService {
|
|
|
3723
3739
|
this.changeStates(0, this.messages()[0]);
|
|
3724
3740
|
}
|
|
3725
3741
|
}
|
|
3742
|
+
/**
|
|
3743
|
+
* @description
|
|
3744
|
+
* Handles the completion of an audio playback.
|
|
3745
|
+
*
|
|
3746
|
+
* @param message - The `MessageContent` object whose audio has finished playing.
|
|
3747
|
+
*/
|
|
3726
3748
|
audioCompleted(message) {
|
|
3727
3749
|
this.conversationService.currentAudioStatus.set({ message, completed: true });
|
|
3728
3750
|
this.currentPlayingIndex.set(null);
|
|
@@ -3731,6 +3753,12 @@ class MessageOrchestrationService {
|
|
|
3731
3753
|
this.processNextInQueue();
|
|
3732
3754
|
}
|
|
3733
3755
|
}
|
|
3756
|
+
/**
|
|
3757
|
+
* @description
|
|
3758
|
+
* Notifies the `ConversationService` when a word is clicked.
|
|
3759
|
+
*
|
|
3760
|
+
* @param wordData - The `WordData` object containing information about the clicked word.
|
|
3761
|
+
*/
|
|
3734
3762
|
wordClicked(wordData) {
|
|
3735
3763
|
this.conversationService.notifyWordClicked(wordData);
|
|
3736
3764
|
}
|
|
@@ -3823,14 +3851,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
|
|
|
3823
3851
|
type: Injectable
|
|
3824
3852
|
}], ctorParameters: () => [] });
|
|
3825
3853
|
|
|
3854
|
+
/**
|
|
3855
|
+
* @Component
|
|
3856
|
+
*
|
|
3857
|
+
* @description
|
|
3858
|
+
* This component orchestrates the display and audio playback of chat messages.
|
|
3859
|
+
* It uses the `MessageOrchestrationService` to manage the message queue and audio playback.
|
|
3860
|
+
*
|
|
3861
|
+
* @see MessageOrchestrationService
|
|
3862
|
+
* @see MessageContentDisplayer
|
|
3863
|
+
*/
|
|
3826
3864
|
class ChatMessageOrchestratorComponent {
|
|
3827
3865
|
constructor() {
|
|
3828
3866
|
// Services
|
|
3829
3867
|
this.orchestrationService = inject(MessageOrchestrationService);
|
|
3830
3868
|
// Inputs
|
|
3869
|
+
/**
|
|
3870
|
+
* An array of `MessageContent` objects to be displayed and orchestrated.
|
|
3871
|
+
*/
|
|
3831
3872
|
this.messages = input.required(...(ngDevMode ? [{ debugName: "messages" }] : []));
|
|
3873
|
+
/**
|
|
3874
|
+
* The `ChatRole` of the messages' author.
|
|
3875
|
+
*/
|
|
3832
3876
|
this.messageRole = input.required(...(ngDevMode ? [{ debugName: "messageRole" }] : []));
|
|
3833
3877
|
// Outputs
|
|
3878
|
+
/**
|
|
3879
|
+
* Emits when the audio for a message has completed playing.
|
|
3880
|
+
*/
|
|
3834
3881
|
this.audioCompleted = output();
|
|
3835
3882
|
// Signals
|
|
3836
3883
|
this.messagesSignal = this.orchestrationService.messagesSignal;
|
|
@@ -3838,15 +3885,27 @@ class ChatMessageOrchestratorComponent {
|
|
|
3838
3885
|
ngOnInit() {
|
|
3839
3886
|
this.orchestrationService.startOrchestration(this.messages(), this.messageRole());
|
|
3840
3887
|
}
|
|
3888
|
+
/**
|
|
3889
|
+
* @description
|
|
3890
|
+
* Handles the `audioCompleted` event from the `MessageContentDisplayer`.
|
|
3891
|
+
*
|
|
3892
|
+
* @param message - The `MessageContent` object whose audio has finished playing.
|
|
3893
|
+
*/
|
|
3841
3894
|
onAudioCompleted(message) {
|
|
3842
3895
|
this.orchestrationService.audioCompleted(message);
|
|
3843
3896
|
this.audioCompleted.emit(message);
|
|
3844
3897
|
}
|
|
3898
|
+
/**
|
|
3899
|
+
* @description
|
|
3900
|
+
* Handles the `wordClicked` event from the `MessageContentDisplayer`.
|
|
3901
|
+
*
|
|
3902
|
+
* @param wordData - The `WordData` object containing information about the clicked word.
|
|
3903
|
+
*/
|
|
3845
3904
|
onWordClicked(wordData) {
|
|
3846
3905
|
this.orchestrationService.wordClicked(wordData);
|
|
3847
3906
|
}
|
|
3848
3907
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ChatMessageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3849
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: ChatMessageOrchestratorComponent, isStandalone: true, selector: "dc-message-orchestrator", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, messageRole: { classPropertyName: "messageRole", publicName: "messageRole", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { audioCompleted: "audioCompleted" }, providers: [MessageOrchestrationService], ngImport: i0, template: "<div class=\"message-orchestrator-container\">\n @for (message of messagesSignal(); track $index) {\n <message-content-displayer [message]=\"message\" (audioCompleted)=\"onAudioCompleted($event)\" (wordClicked)=\"onWordClicked($event)\" />\n }\n</div>\n", styles: [":host{display:block}.word-options-popup{position:absolute;border:1px solid #ccc;background:#fff;padding:5px;z-index:1000}message-content-displayer{text-shadow:1px 1px 4px rgba(255,255,255,.4)}\n"], dependencies: [{ kind: "component", type: MessageContentDisplayer, selector: "message-content-displayer", inputs: ["message", "markWord"], outputs: ["playAudio", "audioCompleted", "wordClicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3908
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: ChatMessageOrchestratorComponent, isStandalone: true, selector: "dc-message-orchestrator", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, messageRole: { classPropertyName: "messageRole", publicName: "messageRole", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { audioCompleted: "audioCompleted" }, providers: [MessageOrchestrationService], ngImport: i0, template: "<div class=\"message-orchestrator-container\">\n @for (message of messagesSignal(); track $index) {\n <message-content-displayer [message]=\"message\" (audioCompleted)=\"onAudioCompleted($event)\" (wordClicked)=\"onWordClicked($event)\" />\n }\n</div>\n", styles: [":host{display:block}.word-options-popup{position:absolute;border:1px solid #ccc;background:#fff;padding:5px;z-index:1000}message-content-displayer{text-shadow:1px 1px 4px rgba(255,255,255,.4)}\n"], dependencies: [{ kind: "component", type: MessageContentDisplayer, selector: "message-content-displayer", inputs: ["message", "markWord", "highlightColor"], outputs: ["playAudio", "audioCompleted", "wordClicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3850
3909
|
}
|
|
3851
3910
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ChatMessageOrchestratorComponent, decorators: [{
|
|
3852
3911
|
type: Component,
|
|
@@ -3910,7 +3969,6 @@ class ChatMessagesListComponent {
|
|
|
3910
3969
|
constructor() {
|
|
3911
3970
|
// Inputs
|
|
3912
3971
|
this.chatUserSettings = input.required(...(ngDevMode ? [{ debugName: "chatUserSettings" }] : []));
|
|
3913
|
-
this.inputMessages = input.required(...(ngDevMode ? [{ debugName: "inputMessages" }] : []));
|
|
3914
3972
|
// Private services
|
|
3915
3973
|
this.conversationService = inject(ConversationService);
|
|
3916
3974
|
this.messagesStateService = inject(MessagesStateService);
|
|
@@ -3940,7 +3998,7 @@ class ChatMessagesListComponent {
|
|
|
3940
3998
|
return `${message.role}-${index}-${message.content.substring(0, 20)}`;
|
|
3941
3999
|
}
|
|
3942
4000
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ChatMessagesListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3943
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: ChatMessagesListComponent, isStandalone: true, selector: "dc-chat-messages-list", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: true, transformFunction: null }
|
|
4001
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: ChatMessagesListComponent, isStandalone: true, selector: "dc-chat-messages-list", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"messages-container\">\n @for (message of messages(); track message.messageId) {\n <dc-chat-message [chatMessage]=\"message\" [chatUserSettings]=\"chatUserSettings()\"> </dc-chat-message>\n } @if (isThinking()) {\n <div class=\"thinking-container\">\n <div class=\"thinking-message\">\n <div class=\"thinking-avatar\">\n <img [src]=\"aiIcon\" alt=\"AI thinking\" class=\"avatar-img\" />\n </div>\n <div class=\"thinking-content\">\n <p-skeleton width=\"80%\" height=\"2rem\"></p-skeleton>\n <p-skeleton width=\"60%\" height=\"2rem\"></p-skeleton>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".messages-container{display:flex;flex-direction:column;gap:1rem;height:100%;padding:3px}@media (min-width: 992px){.messages-container{padding:2rem}}.thinking-container{padding:.5rem 0;border-radius:20px;background:#fff}.thinking-message{display:flex;gap:1rem;align-items:flex-start}.thinking-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.avatar-img{width:100%;height:100%;object-fit:cover}.thinking-content{flex:1;display:flex;flex-direction:column;gap:.5rem}\n"], dependencies: [{ kind: "component", type: ChatMessageComponent, selector: "dc-chat-message", inputs: ["chatMessage", "chatUserSettings"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3944
4002
|
}
|
|
3945
4003
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ChatMessagesListComponent, decorators: [{
|
|
3946
4004
|
type: Component,
|
|
@@ -4276,16 +4334,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
|
|
|
4276
4334
|
|
|
4277
4335
|
class ConversationInspector {
|
|
4278
4336
|
constructor() {
|
|
4279
|
-
this.evaluationService = inject(EvaluationService);
|
|
4280
|
-
this.messageStateService = inject(MessagesStateService);
|
|
4281
|
-
this.dynamicFlowService = inject(DynamicFlowService);
|
|
4282
|
-
this.globalToolsService = inject(GlobalToolsService);
|
|
4283
4337
|
this.config = inject(DynamicDialogConfig);
|
|
4284
4338
|
this.completeEvent = output();
|
|
4285
4339
|
this.value = 0;
|
|
4286
4340
|
this.viewMode = signal('markdown', ...(ngDevMode ? [{ debugName: "viewMode" }] : []));
|
|
4341
|
+
const parentInjector = this.config.data.injector;
|
|
4287
4342
|
this.agentCard = this.config.data.agentCard;
|
|
4288
4343
|
this.chatUserSettings = this.config.data.chatUserSettings;
|
|
4344
|
+
this.evaluationService = parentInjector.get(EvaluationService);
|
|
4345
|
+
this.messageStateService = parentInjector.get(MessagesStateService);
|
|
4346
|
+
this.dynamicFlowService = parentInjector.get(DynamicFlowService);
|
|
4347
|
+
this.globalToolsService = parentInjector.get(GlobalToolsService);
|
|
4289
4348
|
console.log('ConversationInspector data assigned', this.agentCard, this.chatUserSettings);
|
|
4290
4349
|
}
|
|
4291
4350
|
setScore() {
|
|
@@ -4336,29 +4395,32 @@ class ConversationInfoService {
|
|
|
4336
4395
|
modal: true,
|
|
4337
4396
|
draggable: false,
|
|
4338
4397
|
closable: true,
|
|
4339
|
-
data
|
|
4398
|
+
data: {
|
|
4399
|
+
agentCard: data.agentCard,
|
|
4400
|
+
chatUserSettings: data.chatUserSettings,
|
|
4401
|
+
injector: data.injector,
|
|
4402
|
+
},
|
|
4340
4403
|
});
|
|
4341
4404
|
}
|
|
4342
4405
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ConversationInfoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4343
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ConversationInfoService
|
|
4406
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ConversationInfoService }); }
|
|
4344
4407
|
}
|
|
4345
4408
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ConversationInfoService, decorators: [{
|
|
4346
|
-
type: Injectable
|
|
4347
|
-
args: [{
|
|
4348
|
-
providedIn: 'root',
|
|
4349
|
-
}]
|
|
4409
|
+
type: Injectable
|
|
4350
4410
|
}] });
|
|
4351
4411
|
|
|
4352
4412
|
class DCChatComponent {
|
|
4353
4413
|
constructor() {
|
|
4354
4414
|
// Services
|
|
4355
|
-
this.
|
|
4415
|
+
this.injector = inject(Injector);
|
|
4416
|
+
this.userService = inject(UserService);
|
|
4417
|
+
this.dialogService = inject(DialogService);
|
|
4418
|
+
// Local Services Instances
|
|
4356
4419
|
this.conversationService = inject(ConversationService);
|
|
4357
4420
|
this.evaluationService = inject(EvaluationService);
|
|
4358
4421
|
this.messageStateService = inject(MessagesStateService);
|
|
4359
|
-
this.dialogService = inject(DialogService);
|
|
4360
4422
|
this.chatMonitorService = inject(ChatMonitorService);
|
|
4361
|
-
this.
|
|
4423
|
+
this.conversationInfoService = inject(ConversationInfoService);
|
|
4362
4424
|
// 📥 Inputs
|
|
4363
4425
|
this.chatUserSettings = this.userService.user()?.settings?.conversation; // Default to user data exchange
|
|
4364
4426
|
this.conversationFlow = null;
|
|
@@ -4369,7 +4431,7 @@ class DCChatComponent {
|
|
|
4369
4431
|
this.goalCompleted = output(); // notifies when user completes goal (score reaches 100)
|
|
4370
4432
|
// readonly wordClicked = output<WordData>(); // Replaced by sendMessage with ChatEventType.WordClicked
|
|
4371
4433
|
// Signals States
|
|
4372
|
-
|
|
4434
|
+
// public messages = signal<ChatMessage[]>([]);
|
|
4373
4435
|
// States
|
|
4374
4436
|
this.micSettings = { micMode: 'recognition', lang: 'en' };
|
|
4375
4437
|
this.isAdmin = true;
|
|
@@ -4390,6 +4452,20 @@ class DCChatComponent {
|
|
|
4390
4452
|
// this.conversationService.notifyWordClicked(null);
|
|
4391
4453
|
}
|
|
4392
4454
|
});
|
|
4455
|
+
// Effect to watch for audio playback events
|
|
4456
|
+
effect(() => {
|
|
4457
|
+
const message = this.chatMonitorService.messageAudioWillPlay$();
|
|
4458
|
+
if (message) {
|
|
4459
|
+
this.chatEvent.emit({ type: ChatEventType.AudioStarted, payload: message });
|
|
4460
|
+
}
|
|
4461
|
+
});
|
|
4462
|
+
// Effect to watch for mood changes
|
|
4463
|
+
effect(() => {
|
|
4464
|
+
const mood = this.chatMonitorService.currentMood();
|
|
4465
|
+
if (mood) {
|
|
4466
|
+
this.chatEvent.emit({ type: ChatEventType.MoodDetected, payload: mood });
|
|
4467
|
+
}
|
|
4468
|
+
});
|
|
4393
4469
|
}
|
|
4394
4470
|
async ngOnInit() {
|
|
4395
4471
|
this.conversationService.setDestroyed(false);
|
|
@@ -4437,6 +4513,7 @@ class DCChatComponent {
|
|
|
4437
4513
|
this.conversationInfoService.openConversationInfo({
|
|
4438
4514
|
agentCard: this.agentCard,
|
|
4439
4515
|
chatUserSettings: this.chatUserSettings,
|
|
4516
|
+
injector: this.injector,
|
|
4440
4517
|
});
|
|
4441
4518
|
}
|
|
4442
4519
|
/**
|
|
@@ -4452,11 +4529,11 @@ class DCChatComponent {
|
|
|
4452
4529
|
this.goalCompleted.emit();
|
|
4453
4530
|
}
|
|
4454
4531
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4455
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.1.6", type: DCChatComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: false, isRequired: false, transformFunction: null }, conversationSettings: { classPropertyName: "conversationSettings", publicName: "conversationSettings", isSignal: false, isRequired: false, transformFunction: null }, conversationFlow: { classPropertyName: "conversationFlow", publicName: "conversationFlow", isSignal: false, isRequired: false, transformFunction: null }, agentCard: { classPropertyName: "agentCard", publicName: "agentCard", isSignal: false, isRequired: false, transformFunction: null }, parseDict: { classPropertyName: "parseDict", publicName: "parseDict", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { chatEvent: "chatEvent", goalCompleted: "goalCompleted" }, viewQueries: [{ propertyName: "chatFooterComponent", first: true, predicate: ChatFooterComponent, descendants: true }], ngImport: i0, template: "<div class=\"chat-container\">\n <dc-chat-header\n [agentCard]=\"agentCard\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\"\n (completeEvent)=\"complete()\">\n </dc-chat-header>\n\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\"
|
|
4532
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.1.6", type: DCChatComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: false, isRequired: false, transformFunction: null }, conversationSettings: { classPropertyName: "conversationSettings", publicName: "conversationSettings", isSignal: false, isRequired: false, transformFunction: null }, conversationFlow: { classPropertyName: "conversationFlow", publicName: "conversationFlow", isSignal: false, isRequired: false, transformFunction: null }, agentCard: { classPropertyName: "agentCard", publicName: "agentCard", isSignal: false, isRequired: false, transformFunction: null }, parseDict: { classPropertyName: "parseDict", publicName: "parseDict", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { chatEvent: "chatEvent", goalCompleted: "goalCompleted" }, providers: [ConversationService, AIGenerationService, EvaluationService, DynamicFlowTaskService, ConversationInfoService, ChatMonitorService], viewQueries: [{ propertyName: "chatFooterComponent", first: true, predicate: ChatFooterComponent, descendants: true }], ngImport: i0, template: "<div class=\"chat-container\">\n <dc-chat-header\n [agentCard]=\"agentCard\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\"\n (completeEvent)=\"complete()\">\n </dc-chat-header>\n\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\"> </dc-chat-messages-list>\n\n <dc-chat-footer [micSettings]=\"micSettings\"> </dc-chat-footer>\n</div>\n", styles: ["::ng-deep .p-drawer-content{padding:0!important}::ng-deep .p-dialog-content{display:contents}.chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:transparent}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto;padding:1rem}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ChatHeaderComponent, selector: "dc-chat-header", inputs: ["alternativeConversation", "agentCard"], outputs: ["restartConversationEvent", "showInfoEvent", "settingsClickEvent"] }, { kind: "component", type: ChatFooterComponent, selector: "dc-chat-footer", inputs: ["isAIThinking", "micSettings"], outputs: ["sendMessage", "textInputChanged"] }, { kind: "component", type: ChatMessagesListComponent, selector: "dc-chat-messages-list", inputs: ["chatUserSettings"] }, { kind: "ngmodule", type: ProgressBarModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4456
4533
|
}
|
|
4457
4534
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCChatComponent, decorators: [{
|
|
4458
4535
|
type: Component,
|
|
4459
|
-
args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, ChatHeaderComponent, ChatFooterComponent, ChatMessagesListComponent, ProgressBarModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"chat-container\">\n <dc-chat-header\n [agentCard]=\"agentCard\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\"\n (completeEvent)=\"complete()\">\n </dc-chat-header>\n\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\"
|
|
4536
|
+
args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, ChatHeaderComponent, ChatFooterComponent, ChatMessagesListComponent, ProgressBarModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ConversationService, AIGenerationService, EvaluationService, DynamicFlowTaskService, ConversationInfoService, ChatMonitorService], template: "<div class=\"chat-container\">\n <dc-chat-header\n [agentCard]=\"agentCard\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\"\n (completeEvent)=\"complete()\">\n </dc-chat-header>\n\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\"> </dc-chat-messages-list>\n\n <dc-chat-footer [micSettings]=\"micSettings\"> </dc-chat-footer>\n</div>\n", styles: ["::ng-deep .p-drawer-content{padding:0!important}::ng-deep .p-dialog-content{display:contents}.chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:transparent}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto;padding:1rem}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"] }]
|
|
4460
4537
|
}], ctorParameters: () => [], propDecorators: { chatFooterComponent: [{
|
|
4461
4538
|
type: ViewChild,
|
|
4462
4539
|
args: [ChatFooterComponent]
|