sf-aiembedded 0.1.2 → 0.2.4
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.
- package/fesm2022/sf-aiembedded.mjs +496 -216
- package/fesm2022/sf-aiembedded.mjs.map +1 -1
- package/lib/agents-backend.port.d.ts +2 -0
- package/lib/general-agent/agents/bubble-chat/bubble-agent.component.d.ts +21 -0
- package/lib/general-agent/components/agent-avatar/agent-avatar.component.d.ts +6 -0
- package/lib/general-agent/components/bubble-chat/bubble-chat.component.d.ts +28 -0
- package/lib/general-agent/components/input/input.component.d.ts +20 -0
- package/lib/general-agent/components/messages/messages.component.d.ts +14 -0
- package/lib/general-agent/components/robot-icon/robot-icon.component.d.ts +5 -0
- package/lib/general-agent/general-agent.component.d.ts +22 -60
- package/lib/general-agent/models/agent.model.d.ts +21 -0
- package/lib/general-agent/services/agent.service.d.ts +68 -0
- package/lib/general-agent/types/markdown/markdown.component.d.ts +1 -2
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
|
@@ -1,203 +1,128 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Input, Component, ViewEncapsulation,
|
|
2
|
+
import { InjectionToken, Inject, Injectable, Input, Component, ViewEncapsulation, ViewChild, EventEmitter, Output, inject } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
|
-
import * as i2$
|
|
5
|
+
import * as i2$2 from '@angular/forms';
|
|
6
6
|
import { FormsModule } from '@angular/forms';
|
|
7
7
|
import * as i2 from 'primeng/button';
|
|
8
8
|
import { ButtonModule } from 'primeng/button';
|
|
9
|
-
import { Avatar } from 'primeng/avatar';
|
|
10
9
|
import { TagModule } from 'primeng/tag';
|
|
11
|
-
import { Textarea } from 'primeng/textarea';
|
|
12
10
|
import { ChipModule } from 'primeng/chip';
|
|
11
|
+
import { BehaviorSubject, combineLatest } from 'rxjs';
|
|
12
|
+
import { Avatar } from 'primeng/avatar';
|
|
13
13
|
import { marked } from 'marked';
|
|
14
14
|
import * as i1$1 from '@angular/platform-browser';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
21
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: TextComponent, isStandalone: true, selector: "app-text", inputs: { text: "text", role: "role" }, ngImport: i0, template: "<div\r\nclass=\"shadow-1 border-1 border-gray-200 px-3 py-2 border-round-bottom-xl w-fit\"\r\n[ngClass]=\"role !== 'user' ? 'bg-white border-1 border-gray-300 text-gray-900 border-round-right-xl' : 'border-round-left-xl border-round-right-sm user-chat'\"\r\n>\r\n<span class=\"text-sm m-0 whitespace-normal md:text-base lg:text-lg\" style=\"word-break: break-word;\">{{text}}</span>\r\n</div>", styles: [".user-chat{background:linear-gradient(to bottom right,#60a5fa,#2563eb);color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }] });
|
|
22
|
-
}
|
|
23
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TextComponent, decorators: [{
|
|
24
|
-
type: Component,
|
|
25
|
-
args: [{ selector: 'app-text', standalone: true, imports: [CommonModule, FormsModule], template: "<div\r\nclass=\"shadow-1 border-1 border-gray-200 px-3 py-2 border-round-bottom-xl w-fit\"\r\n[ngClass]=\"role !== 'user' ? 'bg-white border-1 border-gray-300 text-gray-900 border-round-right-xl' : 'border-round-left-xl border-round-right-sm user-chat'\"\r\n>\r\n<span class=\"text-sm m-0 whitespace-normal md:text-base lg:text-lg\" style=\"word-break: break-word;\">{{text}}</span>\r\n</div>", styles: [".user-chat{background:linear-gradient(to bottom right,#60a5fa,#2563eb);color:#fff}\n"] }]
|
|
26
|
-
}], propDecorators: { text: [{
|
|
27
|
-
type: Input
|
|
28
|
-
}], role: [{
|
|
29
|
-
type: Input
|
|
30
|
-
}] } });
|
|
31
|
-
|
|
32
|
-
class FileComponent {
|
|
33
|
-
files = [];
|
|
34
|
-
role = 'assistant';
|
|
35
|
-
remove = false;
|
|
36
|
-
getFileIcon(mime) {
|
|
37
|
-
if (mime.includes('pdf'))
|
|
38
|
-
return 'pi pi-file-pdf';
|
|
39
|
-
if (mime.includes('image'))
|
|
40
|
-
return 'pi pi-image';
|
|
41
|
-
if (mime.includes('audio'))
|
|
42
|
-
return 'pi pi-volume-up';
|
|
43
|
-
if (mime.includes('video'))
|
|
44
|
-
return 'pi pi-video';
|
|
45
|
-
if (mime.includes('zip'))
|
|
46
|
-
return 'pi pi-file-zip';
|
|
47
|
-
if (mime.includes('xlsx'))
|
|
48
|
-
return 'pi pi-file-excel';
|
|
49
|
-
return 'pi pi-file';
|
|
50
|
-
}
|
|
51
|
-
getFileClasses(mime) {
|
|
52
|
-
if (mime.includes('pdf'))
|
|
53
|
-
return 'bg-red-500 border-red-800 text-white';
|
|
54
|
-
if (mime.includes('image'))
|
|
55
|
-
return 'bg-gray-100 border-gray-200 text-white';
|
|
56
|
-
if (mime.includes('audio'))
|
|
57
|
-
return 'bg-blue-500 border-blue-800 text-white';
|
|
58
|
-
if (mime.includes('video'))
|
|
59
|
-
return 'bg-yellow-500 border-yellow-800 text-white';
|
|
60
|
-
if (mime.includes('zip'))
|
|
61
|
-
return 'bg-indigo-500 border-indigo-800 text-white';
|
|
62
|
-
if (mime.includes('xlsx'))
|
|
63
|
-
return 'bg-green-500 border-green-800 text-white';
|
|
64
|
-
return 'bg-gray-100 border-3 border-gray-200 text-gray-500';
|
|
65
|
-
}
|
|
66
|
-
removeSelectedFile(i) {
|
|
67
|
-
this.files.splice(i, 1);
|
|
68
|
-
}
|
|
69
|
-
getGridClass() {
|
|
70
|
-
const fileCount = this.files.length;
|
|
71
|
-
if (fileCount === 1) {
|
|
72
|
-
return 'col-12';
|
|
73
|
-
}
|
|
74
|
-
else if (fileCount === 2) {
|
|
75
|
-
return 'col-12 sm:col-6';
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
return 'col-12 sm:col-6 md:col-4';
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
82
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: FileComponent, isStandalone: true, selector: "app-file", inputs: { files: "files", role: "role", remove: "remove" }, ngImport: i0, template: "<div *ngIf=\"!remove\" class=\"grid w-full m-o\" [ngClass]=\"role !== 'user' ? 'items-start justify-content-start' : 'items-end justify-content-end'\">\r\n <div *ngFor=\"let file of files\" [ngClass]=\"getGridClass()\">\r\n <div class=\"flex align-items-center gap-3 border-1 border-gray-300 border-round-2xl p-2 bg-gray-100 h-full\">\r\n <p-avatar class=\"p-1 border-1 avatar-responsive\" [ngClass]=\"getFileClasses(file.name)\" [icon]=\"getFileIcon(file.name)\"></p-avatar>\r\n <div class=\"flex flex-column overflow-hidden\" style=\"min-width: 0;\">\r\n <span class=\"text-sm font-semibold text-gray-500 md:text-base lg:text-lg\" style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n {{ file.name }}\r\n </span>\r\n <p class=\"text-xs text-gray-400 md:text-sm lg:text-base\" style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n ({{ file.type || 'Archivo' }})\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<div *ngIf=\"remove\" class=\"grid w-full\">\r\n <div *ngFor=\"let file of files; let i = index\" class=\"col-12 sm:col-6 md:col-4\">\r\n <div class=\"relative border-1 border-gray-300 border-round-xl p-2 h-full\">\r\n <p-button icon=\"pi pi-times\" [rounded]=\"true\" variant=\"text\" severity=\"secondary\" class=\"no-hover absolute top-0 right-0 z-1\" (click)=\"removeSelectedFile(i)\" size=\"small\"/>\r\n <div class=\"flex align-items-center gap-3\">\r\n <p-avatar \r\n class=\"p-1 border-1 avatar-responsive\"\r\n [ngClass]=\"getFileClasses(file.name)\" \r\n [icon]=\"getFileIcon(file.name)\" \r\n >\r\n </p-avatar>\r\n \r\n <div class=\"flex flex-column overflow-hidden\" style=\"min-width: 0;\">\r\n <span class=\"text-sm font-semibold text-gray-500 md:text-base lg:text-lg\" \r\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n {{ file.name }}\r\n </span>\r\n <p class=\"text-xs text-gray-400 md:text-sm lg:text-base\" \r\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n ({{ file.type || 'archivo' }})\r\n </p>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n</div>", styles: [".avatar-responsive{aspect-ratio:1/1;width:2.5rem;height:2.5rem;font-size:2rem;flex-shrink:0}@media (max-width: 768px){.avatar-responsive{width:2rem;height:2rem;font-size:1.5rem}}@media (max-width: 576px){.avatar-responsive{width:1.75rem;height:1.75rem;font-size:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "style", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }] });
|
|
83
|
-
}
|
|
84
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FileComponent, decorators: [{
|
|
85
|
-
type: Component,
|
|
86
|
-
args: [{ selector: 'app-file', standalone: true, imports: [CommonModule, FormsModule, ButtonModule, Avatar], template: "<div *ngIf=\"!remove\" class=\"grid w-full m-o\" [ngClass]=\"role !== 'user' ? 'items-start justify-content-start' : 'items-end justify-content-end'\">\r\n <div *ngFor=\"let file of files\" [ngClass]=\"getGridClass()\">\r\n <div class=\"flex align-items-center gap-3 border-1 border-gray-300 border-round-2xl p-2 bg-gray-100 h-full\">\r\n <p-avatar class=\"p-1 border-1 avatar-responsive\" [ngClass]=\"getFileClasses(file.name)\" [icon]=\"getFileIcon(file.name)\"></p-avatar>\r\n <div class=\"flex flex-column overflow-hidden\" style=\"min-width: 0;\">\r\n <span class=\"text-sm font-semibold text-gray-500 md:text-base lg:text-lg\" style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n {{ file.name }}\r\n </span>\r\n <p class=\"text-xs text-gray-400 md:text-sm lg:text-base\" style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n ({{ file.type || 'Archivo' }})\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<div *ngIf=\"remove\" class=\"grid w-full\">\r\n <div *ngFor=\"let file of files; let i = index\" class=\"col-12 sm:col-6 md:col-4\">\r\n <div class=\"relative border-1 border-gray-300 border-round-xl p-2 h-full\">\r\n <p-button icon=\"pi pi-times\" [rounded]=\"true\" variant=\"text\" severity=\"secondary\" class=\"no-hover absolute top-0 right-0 z-1\" (click)=\"removeSelectedFile(i)\" size=\"small\"/>\r\n <div class=\"flex align-items-center gap-3\">\r\n <p-avatar \r\n class=\"p-1 border-1 avatar-responsive\"\r\n [ngClass]=\"getFileClasses(file.name)\" \r\n [icon]=\"getFileIcon(file.name)\" \r\n >\r\n </p-avatar>\r\n \r\n <div class=\"flex flex-column overflow-hidden\" style=\"min-width: 0;\">\r\n <span class=\"text-sm font-semibold text-gray-500 md:text-base lg:text-lg\" \r\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n {{ file.name }}\r\n </span>\r\n <p class=\"text-xs text-gray-400 md:text-sm lg:text-base\" \r\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n ({{ file.type || 'archivo' }})\r\n </p>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n</div>", styles: [".avatar-responsive{aspect-ratio:1/1;width:2.5rem;height:2.5rem;font-size:2rem;flex-shrink:0}@media (max-width: 768px){.avatar-responsive{width:2rem;height:2rem;font-size:1.5rem}}@media (max-width: 576px){.avatar-responsive{width:1.75rem;height:1.75rem;font-size:1.25rem}}\n"] }]
|
|
87
|
-
}], propDecorators: { files: [{
|
|
88
|
-
type: Input
|
|
89
|
-
}], role: [{
|
|
90
|
-
type: Input
|
|
91
|
-
}], remove: [{
|
|
92
|
-
type: Input
|
|
93
|
-
}] } });
|
|
94
|
-
|
|
95
|
-
class TableComponent {
|
|
96
|
-
table = "";
|
|
97
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
98
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: TableComponent, isStandalone: true, selector: "app-table", inputs: { table: "table" }, ngImport: i0, template: "<div\r\n *ngIf=\"table\"\r\n class=\"mt-2 overflow-auto\"\r\n [innerHTML]=\"table\"\r\n style=\"max-width: 100%; overflow-x: auto;\"\r\n></div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
99
|
-
}
|
|
100
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TableComponent, decorators: [{
|
|
101
|
-
type: Component,
|
|
102
|
-
args: [{ selector: 'app-table', imports: [CommonModule], template: "<div\r\n *ngIf=\"table\"\r\n class=\"mt-2 overflow-auto\"\r\n [innerHTML]=\"table\"\r\n style=\"max-width: 100%; overflow-x: auto;\"\r\n></div>" }]
|
|
103
|
-
}], propDecorators: { table: [{
|
|
104
|
-
type: Input
|
|
105
|
-
}] } });
|
|
106
|
-
|
|
107
|
-
// marked.setOptions({
|
|
108
|
-
// highlight: (code, lang) => {
|
|
109
|
-
// if (lang && hljs.getLanguage(lang)) {
|
|
110
|
-
// return hljs.highlight(code, { language: lang }).value;
|
|
111
|
-
// }
|
|
112
|
-
// return hljs.highlightAuto(code).value;
|
|
113
|
-
// }
|
|
114
|
-
// });
|
|
115
|
-
const renderer = {
|
|
116
|
-
link(token) {
|
|
117
|
-
const href = token.href ?? '';
|
|
118
|
-
const title = token.title ? ` title="${token.title}"` : '';
|
|
119
|
-
const text = token.text ?? token.raw ?? href;
|
|
120
|
-
return `<a href="${href}"${title} target="_blank" rel="noopener noreferrer">${text}</a>`;
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
marked.use({ renderer });
|
|
124
|
-
class MarkdownComponent {
|
|
125
|
-
sanitizer;
|
|
126
|
-
content = '';
|
|
127
|
-
role = '';
|
|
128
|
-
safeHtml = '';
|
|
129
|
-
constructor(sanitizer) {
|
|
130
|
-
this.sanitizer = sanitizer;
|
|
131
|
-
}
|
|
132
|
-
async ngOnChanges(changes) {
|
|
133
|
-
if ('content' in changes) {
|
|
134
|
-
const rawHtml = await marked.parse(this.content || '');
|
|
135
|
-
this.safeHtml = this.sanitizer.bypassSecurityTrustHtml(rawHtml);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MarkdownComponent, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
|
|
139
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: MarkdownComponent, isStandalone: true, selector: "app-markdown", inputs: { content: "content", role: "role" }, usesOnChanges: true, ngImport: i0, template: "<div [ngClass]=\"role !== 'user' ? 'flex items-start gap-2' : ''\">\r\n <p-avatar\r\n *ngIf=\"role !== 'user'\" \r\n image=\"./images/bot.png\"\r\n class=\"bg-gray-100 border-3 border-gray-300 p-1\"\r\n shape=\"circle\" \r\n size=\"large\"\r\n [ngStyle]=\"{ 'aspect-ratio': '1 / 1', 'min-width': '40px' }\">\r\n </p-avatar>\r\n <div>\r\n <div class=\"markdown-container\" [innerHTML]=\"safeHtml\"></div>\r\n </div>\r\n</div>\r\n\r\n", styles: [".markdown-container{font-family:Arial,sans-serif;line-height:1.5;color:#333;min-width:0;flex:1;word-wrap:break-word;white-space:normal;overflow-wrap:break-word}.markdown-container table{display:block;width:100%;border-collapse:collapse;margin:1em 0;font-size:1.125rem;box-shadow:0 1px 2px #0000000d;border-right:none;border-left:none;overflow-x:auto}.markdown-container th{background-color:#f3f4f6;color:#333;font-weight:600;padding:10px;text-align:left;border-bottom:2px solid #d1d5db}.markdown-container td{padding:10px;border-top:1px solid #e5e7eb;vertical-align:top;text-align:left}@media (max-width: 1024px){.markdown-container table{font-size:1rem}}@media (max-width: 768px){.markdown-container table{font-size:.875rem}}.markdown-container tr:nth-child(2n){background-color:#f9fafb}.markdown-container tr:hover{background-color:#f1f5f9}.markdown-container a{color:#0366d6;text-decoration:underline}.markdown-container img{max-width:100%;margin:10px 0;border-radius:6px}.markdown-container pre{background:#f6f8fa;padding:12px;border-radius:6px;overflow-x:auto;margin:1em 0}.markdown-container code{font-family:monospace}.markdown-container p,li{font-size:1.125rem}@media (max-width: 1024px){.markdown-container p,li{font-size:1rem}}@media (max-width: 768px){.markdown-container p,li{font-size:.875rem}}.markdown-container ul,.markdown-container ol{padding-left:1.5rem;margin:.5rem 0}.markdown-container ul{list-style-type:disc}.markdown-container ol{list-style-type:decimal}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "style", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
140
|
-
}
|
|
141
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MarkdownComponent, decorators: [{
|
|
142
|
-
type: Component,
|
|
143
|
-
args: [{ selector: 'app-markdown', standalone: true, imports: [CommonModule, Avatar], encapsulation: ViewEncapsulation.None, template: "<div [ngClass]=\"role !== 'user' ? 'flex items-start gap-2' : ''\">\r\n <p-avatar\r\n *ngIf=\"role !== 'user'\" \r\n image=\"./images/bot.png\"\r\n class=\"bg-gray-100 border-3 border-gray-300 p-1\"\r\n shape=\"circle\" \r\n size=\"large\"\r\n [ngStyle]=\"{ 'aspect-ratio': '1 / 1', 'min-width': '40px' }\">\r\n </p-avatar>\r\n <div>\r\n <div class=\"markdown-container\" [innerHTML]=\"safeHtml\"></div>\r\n </div>\r\n</div>\r\n\r\n", styles: [".markdown-container{font-family:Arial,sans-serif;line-height:1.5;color:#333;min-width:0;flex:1;word-wrap:break-word;white-space:normal;overflow-wrap:break-word}.markdown-container table{display:block;width:100%;border-collapse:collapse;margin:1em 0;font-size:1.125rem;box-shadow:0 1px 2px #0000000d;border-right:none;border-left:none;overflow-x:auto}.markdown-container th{background-color:#f3f4f6;color:#333;font-weight:600;padding:10px;text-align:left;border-bottom:2px solid #d1d5db}.markdown-container td{padding:10px;border-top:1px solid #e5e7eb;vertical-align:top;text-align:left}@media (max-width: 1024px){.markdown-container table{font-size:1rem}}@media (max-width: 768px){.markdown-container table{font-size:.875rem}}.markdown-container tr:nth-child(2n){background-color:#f9fafb}.markdown-container tr:hover{background-color:#f1f5f9}.markdown-container a{color:#0366d6;text-decoration:underline}.markdown-container img{max-width:100%;margin:10px 0;border-radius:6px}.markdown-container pre{background:#f6f8fa;padding:12px;border-radius:6px;overflow-x:auto;margin:1em 0}.markdown-container code{font-family:monospace}.markdown-container p,li{font-size:1.125rem}@media (max-width: 1024px){.markdown-container p,li{font-size:1rem}}@media (max-width: 768px){.markdown-container p,li{font-size:.875rem}}.markdown-container ul,.markdown-container ol{padding-left:1.5rem;margin:.5rem 0}.markdown-container ul{list-style-type:disc}.markdown-container ol{list-style-type:decimal}\n"] }]
|
|
144
|
-
}], ctorParameters: () => [{ type: i1$1.DomSanitizer }], propDecorators: { content: [{
|
|
145
|
-
type: Input
|
|
146
|
-
}], role: [{
|
|
147
|
-
type: Input
|
|
148
|
-
}] } });
|
|
15
|
+
import * as i2$1 from 'primeng/message';
|
|
16
|
+
import { MessageModule } from 'primeng/message';
|
|
17
|
+
import { Skeleton } from 'primeng/skeleton';
|
|
18
|
+
import { Textarea } from 'primeng/textarea';
|
|
19
|
+
import * as i1$2 from 'primeng/api';
|
|
149
20
|
|
|
150
21
|
// projects/general-agent/src/lib/agents-backend.port.ts
|
|
151
22
|
const AI_AGENTS = new InjectionToken('AI_AGENTS');
|
|
152
23
|
|
|
153
|
-
class
|
|
24
|
+
class AgentService {
|
|
154
25
|
aiAgentsGatewayService;
|
|
155
|
-
cdr;
|
|
156
26
|
zone;
|
|
157
|
-
chatContainer;
|
|
158
27
|
// Propiedades internas (ya no inputs/outputs)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
28
|
+
// Propiedades para notificar cambios a los componentes que usan el servicio
|
|
29
|
+
messagesSubject = new BehaviorSubject([]);
|
|
30
|
+
messages$ = this.messagesSubject.asObservable();
|
|
31
|
+
isBotWrittingSubject = new BehaviorSubject(false);
|
|
32
|
+
isBotWritting$ = this.isBotWrittingSubject.asObservable();
|
|
33
|
+
isRecordingSubject = new BehaviorSubject(false);
|
|
34
|
+
isRecording$ = this.isRecordingSubject.asObservable();
|
|
35
|
+
isLoadingSubject = new BehaviorSubject(false);
|
|
36
|
+
isLoading$ = this.isLoadingSubject.asObservable();
|
|
37
|
+
headerConfigSubject = new BehaviorSubject(undefined);
|
|
38
|
+
headerConfig$ = this.headerConfigSubject.asObservable();
|
|
39
|
+
selectedFilesSubject = new BehaviorSubject([]);
|
|
40
|
+
selectedFiles$ = this.selectedFilesSubject.asObservable();
|
|
163
41
|
mediaRecorder;
|
|
164
42
|
audioChunks = [];
|
|
165
43
|
recordingStream;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
agentType = '';
|
|
173
|
-
agentId = '';
|
|
174
|
-
idKatios = '';
|
|
175
|
-
constructor(aiAgentsGatewayService, cdr, zone) {
|
|
44
|
+
_agentInfo;
|
|
45
|
+
_configuration;
|
|
46
|
+
_sessionId = '';
|
|
47
|
+
_agentId = '';
|
|
48
|
+
_idKatios = '';
|
|
49
|
+
constructor(aiAgentsGatewayService, zone) {
|
|
176
50
|
this.aiAgentsGatewayService = aiAgentsGatewayService;
|
|
177
|
-
this.cdr = cdr;
|
|
178
51
|
this.zone = zone;
|
|
179
52
|
}
|
|
180
|
-
|
|
181
|
-
|
|
53
|
+
get agentId() {
|
|
54
|
+
return this._agentId;
|
|
182
55
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
await this.loadAgent();
|
|
186
|
-
}
|
|
56
|
+
set agentId(value) {
|
|
57
|
+
this._agentId = value;
|
|
187
58
|
}
|
|
188
|
-
|
|
189
|
-
this.
|
|
59
|
+
get idKatios() {
|
|
60
|
+
return this._idKatios;
|
|
190
61
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
62
|
+
set idKatios(value) {
|
|
63
|
+
this._idKatios = value;
|
|
64
|
+
}
|
|
65
|
+
set agentInfo(value) {
|
|
66
|
+
this._agentInfo = value;
|
|
67
|
+
}
|
|
68
|
+
get agentInfo() {
|
|
69
|
+
return this._agentInfo;
|
|
70
|
+
}
|
|
71
|
+
set selectedFiles(files) {
|
|
72
|
+
this.selectedFilesSubject.next(files);
|
|
73
|
+
}
|
|
74
|
+
get selectedFiles() {
|
|
75
|
+
return this.selectedFilesSubject.value;
|
|
76
|
+
}
|
|
77
|
+
set sessionId(value) {
|
|
78
|
+
this._sessionId = value;
|
|
79
|
+
}
|
|
80
|
+
get sessionId() {
|
|
81
|
+
return this._sessionId;
|
|
82
|
+
}
|
|
83
|
+
set isRecording(value) {
|
|
84
|
+
this.isRecordingSubject.next(value);
|
|
85
|
+
}
|
|
86
|
+
get isRecording() {
|
|
87
|
+
return this.isRecordingSubject.value;
|
|
88
|
+
}
|
|
89
|
+
set headerConfig(value) {
|
|
90
|
+
this.headerConfigSubject.next(value);
|
|
91
|
+
}
|
|
92
|
+
get headerConfig() {
|
|
93
|
+
return this.headerConfigSubject.value;
|
|
94
|
+
}
|
|
95
|
+
set isLoading(value) {
|
|
96
|
+
this.isLoadingSubject.next(value);
|
|
97
|
+
}
|
|
98
|
+
get isLoading() {
|
|
99
|
+
return this.isLoadingSubject.value;
|
|
100
|
+
}
|
|
101
|
+
set isBotWritting(isWritting) {
|
|
102
|
+
this.isBotWrittingSubject.next(isWritting);
|
|
103
|
+
}
|
|
104
|
+
get isBotWritting() {
|
|
105
|
+
return this.isBotWrittingSubject.value;
|
|
106
|
+
}
|
|
107
|
+
addMessage(message) {
|
|
108
|
+
const next = [...this.messagesSubject.value, message];
|
|
109
|
+
this.messagesSubject.next(next);
|
|
110
|
+
}
|
|
111
|
+
clearMessages() {
|
|
112
|
+
this.messagesSubject.next([]);
|
|
113
|
+
}
|
|
114
|
+
addFile(file) {
|
|
115
|
+
const next = [...this.selectedFilesSubject.value, file];
|
|
116
|
+
this.selectedFilesSubject.next(next);
|
|
117
|
+
}
|
|
118
|
+
addFullMessages(messages) {
|
|
119
|
+
const next = [...this.messagesSubject.value, ...messages];
|
|
120
|
+
this.messagesSubject.next(next);
|
|
196
121
|
}
|
|
197
122
|
async loadAgent() {
|
|
198
123
|
this.isLoading = true;
|
|
199
124
|
this.isBotWritting = false;
|
|
200
|
-
this.
|
|
125
|
+
this.clearMessages();
|
|
201
126
|
this.sessionId = "";
|
|
202
127
|
try {
|
|
203
128
|
const agentInfo = await this.aiAgentsGatewayService.getAgent(this.idKatios, this.agentId);
|
|
@@ -205,16 +130,15 @@ class GeneralAgentComponent {
|
|
|
205
130
|
this.agentInfo = JSON.parse(agentInfo.data.rawConfig);
|
|
206
131
|
let configurationAgent = null;
|
|
207
132
|
if (this.agentInfo.config?.i18n?.en) {
|
|
208
|
-
configurationAgent = this.agentInfo.config
|
|
133
|
+
configurationAgent = this.agentInfo.config?.i18n.en;
|
|
209
134
|
}
|
|
210
135
|
else {
|
|
211
|
-
configurationAgent = this.agentInfo.config
|
|
136
|
+
configurationAgent = this.agentInfo.config?.setUp;
|
|
212
137
|
}
|
|
213
138
|
this.headerConfig = {
|
|
214
139
|
title: configurationAgent.title,
|
|
215
140
|
subtitle: configurationAgent.subtitle,
|
|
216
141
|
initialMessages: this.agentInfo.config.initialMessages,
|
|
217
|
-
type: this.agentType,
|
|
218
142
|
placeholder: configurationAgent.inputPlaceholder,
|
|
219
143
|
footer: configurationAgent.footer
|
|
220
144
|
};
|
|
@@ -227,7 +151,7 @@ class GeneralAgentComponent {
|
|
|
227
151
|
}
|
|
228
152
|
}
|
|
229
153
|
get isInitial() {
|
|
230
|
-
return this.
|
|
154
|
+
return this.messagesSubject.value.length === 0 && !this.isBotWritting;
|
|
231
155
|
}
|
|
232
156
|
/**
|
|
233
157
|
* Converts File objects to Attachment format expected by the backend
|
|
@@ -268,16 +192,16 @@ class GeneralAgentComponent {
|
|
|
268
192
|
reader.onerror = error => reject(error);
|
|
269
193
|
});
|
|
270
194
|
}
|
|
271
|
-
async send() {
|
|
195
|
+
async send(userInput) {
|
|
272
196
|
this.isBotWritting = true;
|
|
273
|
-
let text =
|
|
197
|
+
let text = userInput.trim();
|
|
274
198
|
try {
|
|
275
199
|
if (!text) {
|
|
276
200
|
this.isBotWritting = false;
|
|
277
201
|
return;
|
|
278
202
|
}
|
|
279
203
|
// 2️⃣ Pintar mensaje usuario
|
|
280
|
-
this.
|
|
204
|
+
this.addMessage({ role: 'user', type: 'text', content: text });
|
|
281
205
|
// 3️⃣ Enviar SOLO texto al agente
|
|
282
206
|
const requestData = {
|
|
283
207
|
conversationId: this.sessionId,
|
|
@@ -286,17 +210,23 @@ class GeneralAgentComponent {
|
|
|
286
210
|
metadata: {}
|
|
287
211
|
};
|
|
288
212
|
const response = await this.aiAgentsGatewayService.sendMessage(this.idKatios, this.agentId, requestData);
|
|
289
|
-
this.userInput = '';
|
|
213
|
+
//this.userInput = '';
|
|
290
214
|
this.selectedFiles = [];
|
|
215
|
+
// Asegurar que Angular detecte los cambios después del await
|
|
291
216
|
if (response.success) {
|
|
292
217
|
this.sessionId = response.data.conversationId;
|
|
293
|
-
response.data.messages.
|
|
218
|
+
const incoming = response.data.messages.map((m) => ({
|
|
294
219
|
role: m.role,
|
|
295
220
|
type: m.type || 'markdown',
|
|
296
221
|
content: m.content
|
|
297
222
|
}));
|
|
223
|
+
this.addFullMessages(incoming);
|
|
298
224
|
}
|
|
299
225
|
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
console.error('Error en send:', error);
|
|
228
|
+
this.addMessage({ role: 'assistant', type: 'error', content: 'Ocurrió un error al enviar el mensaje' });
|
|
229
|
+
}
|
|
300
230
|
finally {
|
|
301
231
|
this.isBotWritting = false;
|
|
302
232
|
}
|
|
@@ -345,9 +275,10 @@ class GeneralAgentComponent {
|
|
|
345
275
|
if (!text)
|
|
346
276
|
return;
|
|
347
277
|
// Render inmediato del mensaje del usuario
|
|
348
|
-
this.
|
|
349
|
-
|
|
350
|
-
|
|
278
|
+
this.zone.run(() => {
|
|
279
|
+
this.addMessage({ role: 'user', type: 'text', content: text });
|
|
280
|
+
this.isBotWritting = true;
|
|
281
|
+
});
|
|
351
282
|
const requestData = {
|
|
352
283
|
conversationId: this.sessionId,
|
|
353
284
|
text,
|
|
@@ -355,25 +286,26 @@ class GeneralAgentComponent {
|
|
|
355
286
|
metadata: { source: 'voice' }
|
|
356
287
|
};
|
|
357
288
|
const response = await this.aiAgentsGatewayService.sendMessage(this.idKatios, this.agentId, requestData);
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
response
|
|
361
|
-
this.
|
|
289
|
+
// Asegurar que Angular detecte los cambios después del await
|
|
290
|
+
this.zone.run(() => {
|
|
291
|
+
if (response?.success && response?.data) {
|
|
292
|
+
this.sessionId = response.data.conversationId;
|
|
293
|
+
const incoming = response.data.messages.map((m) => ({
|
|
362
294
|
role: m.role || 'assistant',
|
|
363
295
|
type: m.type || 'markdown',
|
|
364
296
|
content: m.content
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
297
|
+
}));
|
|
298
|
+
this.addFullMessages(incoming);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
370
301
|
}
|
|
371
302
|
catch (err) {
|
|
372
303
|
console.error('Error en sendVoiceMessage:', err);
|
|
373
304
|
}
|
|
374
305
|
finally {
|
|
375
|
-
this.
|
|
376
|
-
|
|
306
|
+
this.zone.run(() => {
|
|
307
|
+
this.isBotWritting = false;
|
|
308
|
+
});
|
|
377
309
|
}
|
|
378
310
|
}
|
|
379
311
|
stopRecording() {
|
|
@@ -398,11 +330,41 @@ class GeneralAgentComponent {
|
|
|
398
330
|
// Agrega los archivos nuevos sin duplicar por nombre
|
|
399
331
|
for (const file of newFiles) {
|
|
400
332
|
if (!this.selectedFiles.some(f => f.name === file.name && f.size === file.size)) {
|
|
401
|
-
this.
|
|
333
|
+
this.addFile(file);
|
|
402
334
|
}
|
|
403
335
|
}
|
|
404
336
|
}
|
|
405
337
|
}
|
|
338
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AgentService, deps: [{ token: AI_AGENTS }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
339
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AgentService });
|
|
340
|
+
}
|
|
341
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AgentService, decorators: [{
|
|
342
|
+
type: Injectable
|
|
343
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
344
|
+
type: Inject,
|
|
345
|
+
args: [AI_AGENTS]
|
|
346
|
+
}] }, { type: i0.NgZone }] });
|
|
347
|
+
|
|
348
|
+
//import { Avatar } from 'primeng/avatar';
|
|
349
|
+
class TextComponent {
|
|
350
|
+
text = '';
|
|
351
|
+
role = '';
|
|
352
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
353
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: TextComponent, isStandalone: true, selector: "app-text", inputs: { text: "text", role: "role" }, ngImport: i0, template: "<div\r\nclass=\"px-3 py-2 border-round-bottom-xl w-fit\"\r\n[ngClass]=\"role !== 'user' ? 'bg-white border-round-right-xl' : 'border-round-left-xl border-round-right-sm bg-primary text-white'\"\r\n>\r\n<span class=\"text-base m-0 whitespace-normal\" style=\"word-break: break-word;\">{{text}}</span>\r\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }] });
|
|
354
|
+
}
|
|
355
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TextComponent, decorators: [{
|
|
356
|
+
type: Component,
|
|
357
|
+
args: [{ selector: 'app-text', standalone: true, imports: [CommonModule, FormsModule], template: "<div\r\nclass=\"px-3 py-2 border-round-bottom-xl w-fit\"\r\n[ngClass]=\"role !== 'user' ? 'bg-white border-round-right-xl' : 'border-round-left-xl border-round-right-sm bg-primary text-white'\"\r\n>\r\n<span class=\"text-base m-0 whitespace-normal\" style=\"word-break: break-word;\">{{text}}</span>\r\n</div>" }]
|
|
358
|
+
}], propDecorators: { text: [{
|
|
359
|
+
type: Input
|
|
360
|
+
}], role: [{
|
|
361
|
+
type: Input
|
|
362
|
+
}] } });
|
|
363
|
+
|
|
364
|
+
class FileComponent {
|
|
365
|
+
files = [];
|
|
366
|
+
role = 'assistant';
|
|
367
|
+
remove = false;
|
|
406
368
|
getFileIcon(mime) {
|
|
407
369
|
if (mime.includes('pdf'))
|
|
408
370
|
return 'pi pi-file-pdf';
|
|
@@ -434,49 +396,367 @@ class GeneralAgentComponent {
|
|
|
434
396
|
return 'bg-gray-100 border-3 border-gray-200 text-gray-500';
|
|
435
397
|
}
|
|
436
398
|
removeSelectedFile(i) {
|
|
437
|
-
this.
|
|
399
|
+
this.files.splice(i, 1);
|
|
438
400
|
}
|
|
401
|
+
getGridClass() {
|
|
402
|
+
const fileCount = this.files.length;
|
|
403
|
+
if (fileCount === 1) {
|
|
404
|
+
return 'col-12';
|
|
405
|
+
}
|
|
406
|
+
else if (fileCount === 2) {
|
|
407
|
+
return 'col-12 sm:col-6';
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
return 'col-12 sm:col-6 md:col-4';
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
414
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: FileComponent, isStandalone: true, selector: "app-file", inputs: { files: "files", role: "role", remove: "remove" }, ngImport: i0, template: "<div *ngIf=\"!remove\" class=\"grid w-full m-o\" [ngClass]=\"role !== 'user' ? 'items-start justify-content-start' : 'items-end justify-content-end'\">\r\n <div *ngFor=\"let file of files\" [ngClass]=\"getGridClass()\">\r\n <div class=\"flex align-items-center gap-3 border-1 border-gray-300 border-round-2xl p-2 bg-gray-100 h-full\">\r\n <p-avatar class=\"p-1 border-1 avatar-responsive\" [ngClass]=\"getFileClasses(file.name)\" [icon]=\"getFileIcon(file.name)\"></p-avatar>\r\n <div class=\"flex flex-column overflow-hidden\" style=\"min-width: 0;\">\r\n <span class=\"font-semibold text-gray-500 text-sm md:text-base\" style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n {{ file.name }}\r\n </span>\r\n <p class=\"text-xs text-gray-400 md:text-base\" style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n ({{ file.type || 'Archivo' }})\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<div *ngIf=\"remove\" class=\"grid w-full\">\r\n <div *ngFor=\"let file of files; let i = index\" class=\"col-12 sm:col-6 md:col-4\">\r\n <div class=\"relative border-1 border-gray-300 border-round-xl p-2 h-full\">\r\n <p-button icon=\"pi pi-times\" [rounded]=\"true\" variant=\"text\" severity=\"secondary\" class=\"no-hover absolute top-0 right-0 z-1\" (click)=\"removeSelectedFile(i)\" size=\"small\"/>\r\n <div class=\"flex align-items-center gap-3\">\r\n <p-avatar \r\n class=\"p-1 border-1 avatar-responsive\"\r\n [ngClass]=\"getFileClasses(file.name)\" \r\n [icon]=\"getFileIcon(file.name)\" \r\n >\r\n </p-avatar>\r\n \r\n <div class=\"flex flex-column overflow-hidden\" style=\"min-width: 0;\">\r\n <span class=\"font-semibold text-gray-500 md:text-base text-sm\" \r\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n {{ file.name }}\r\n </span>\r\n <p class=\"text-xs text-gray-400 md:text-base\" \r\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n ({{ file.type || 'archivo' }})\r\n </p>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n</div>", styles: [".avatar-responsive{aspect-ratio:1/1;width:2.5rem;height:2.5rem;font-size:2rem;flex-shrink:0}@media (max-width: 768px){.avatar-responsive{width:2rem;height:2rem;font-size:1.5rem}}@media (max-width: 576px){.avatar-responsive{width:1.75rem;height:1.75rem;font-size:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "style", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }] });
|
|
415
|
+
}
|
|
416
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FileComponent, decorators: [{
|
|
417
|
+
type: Component,
|
|
418
|
+
args: [{ selector: 'app-file', standalone: true, imports: [CommonModule, FormsModule, ButtonModule, Avatar], template: "<div *ngIf=\"!remove\" class=\"grid w-full m-o\" [ngClass]=\"role !== 'user' ? 'items-start justify-content-start' : 'items-end justify-content-end'\">\r\n <div *ngFor=\"let file of files\" [ngClass]=\"getGridClass()\">\r\n <div class=\"flex align-items-center gap-3 border-1 border-gray-300 border-round-2xl p-2 bg-gray-100 h-full\">\r\n <p-avatar class=\"p-1 border-1 avatar-responsive\" [ngClass]=\"getFileClasses(file.name)\" [icon]=\"getFileIcon(file.name)\"></p-avatar>\r\n <div class=\"flex flex-column overflow-hidden\" style=\"min-width: 0;\">\r\n <span class=\"font-semibold text-gray-500 text-sm md:text-base\" style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n {{ file.name }}\r\n </span>\r\n <p class=\"text-xs text-gray-400 md:text-base\" style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n ({{ file.type || 'Archivo' }})\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<div *ngIf=\"remove\" class=\"grid w-full\">\r\n <div *ngFor=\"let file of files; let i = index\" class=\"col-12 sm:col-6 md:col-4\">\r\n <div class=\"relative border-1 border-gray-300 border-round-xl p-2 h-full\">\r\n <p-button icon=\"pi pi-times\" [rounded]=\"true\" variant=\"text\" severity=\"secondary\" class=\"no-hover absolute top-0 right-0 z-1\" (click)=\"removeSelectedFile(i)\" size=\"small\"/>\r\n <div class=\"flex align-items-center gap-3\">\r\n <p-avatar \r\n class=\"p-1 border-1 avatar-responsive\"\r\n [ngClass]=\"getFileClasses(file.name)\" \r\n [icon]=\"getFileIcon(file.name)\" \r\n >\r\n </p-avatar>\r\n \r\n <div class=\"flex flex-column overflow-hidden\" style=\"min-width: 0;\">\r\n <span class=\"font-semibold text-gray-500 md:text-base text-sm\" \r\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n {{ file.name }}\r\n </span>\r\n <p class=\"text-xs text-gray-400 md:text-base\" \r\n style=\"white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\r\n ({{ file.type || 'archivo' }})\r\n </p>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n</div>", styles: [".avatar-responsive{aspect-ratio:1/1;width:2.5rem;height:2.5rem;font-size:2rem;flex-shrink:0}@media (max-width: 768px){.avatar-responsive{width:2rem;height:2rem;font-size:1.5rem}}@media (max-width: 576px){.avatar-responsive{width:1.75rem;height:1.75rem;font-size:1.25rem}}\n"] }]
|
|
419
|
+
}], propDecorators: { files: [{
|
|
420
|
+
type: Input
|
|
421
|
+
}], role: [{
|
|
422
|
+
type: Input
|
|
423
|
+
}], remove: [{
|
|
424
|
+
type: Input
|
|
425
|
+
}] } });
|
|
426
|
+
|
|
427
|
+
class TableComponent {
|
|
428
|
+
table = "";
|
|
429
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
430
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: TableComponent, isStandalone: true, selector: "app-table", inputs: { table: "table" }, ngImport: i0, template: "<div\r\n *ngIf=\"table\"\r\n class=\"mt-2 overflow-auto\"\r\n [innerHTML]=\"table\"\r\n style=\"max-width: 100%; overflow-x: auto;\"\r\n></div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
431
|
+
}
|
|
432
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TableComponent, decorators: [{
|
|
433
|
+
type: Component,
|
|
434
|
+
args: [{ selector: 'app-table', imports: [CommonModule], template: "<div\r\n *ngIf=\"table\"\r\n class=\"mt-2 overflow-auto\"\r\n [innerHTML]=\"table\"\r\n style=\"max-width: 100%; overflow-x: auto;\"\r\n></div>" }]
|
|
435
|
+
}], propDecorators: { table: [{
|
|
436
|
+
type: Input
|
|
437
|
+
}] } });
|
|
438
|
+
|
|
439
|
+
// marked.setOptions({
|
|
440
|
+
// highlight: (code, lang) => {
|
|
441
|
+
// if (lang && hljs.getLanguage(lang)) {
|
|
442
|
+
// return hljs.highlight(code, { language: lang }).value;
|
|
443
|
+
// }
|
|
444
|
+
// return hljs.highlightAuto(code).value;
|
|
445
|
+
// }
|
|
446
|
+
// });
|
|
447
|
+
const renderer = {
|
|
448
|
+
link(token) {
|
|
449
|
+
const href = token.href ?? '';
|
|
450
|
+
const title = token.title ? ` title="${token.title}"` : '';
|
|
451
|
+
const text = token.text ?? token.raw ?? href;
|
|
452
|
+
return `<a href="${href}"${title} target="_blank" rel="noopener noreferrer">${text}</a>`;
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
marked.use({ renderer });
|
|
456
|
+
class MarkdownComponent {
|
|
457
|
+
sanitizer;
|
|
458
|
+
content = '';
|
|
459
|
+
safeHtml = '';
|
|
460
|
+
constructor(sanitizer) {
|
|
461
|
+
this.sanitizer = sanitizer;
|
|
462
|
+
}
|
|
463
|
+
async ngOnChanges(changes) {
|
|
464
|
+
if ('content' in changes) {
|
|
465
|
+
const rawHtml = await marked.parse(this.content || '');
|
|
466
|
+
this.safeHtml = this.sanitizer.bypassSecurityTrustHtml(rawHtml);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MarkdownComponent, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
|
|
470
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: MarkdownComponent, isStandalone: true, selector: "app-markdown", inputs: { content: "content" }, usesOnChanges: true, ngImport: i0, template: "<div>\r\n <div class=\"markdown-container\" [innerHTML]=\"safeHtml\"></div>\r\n</div>", styles: [".markdown-container{font-family:Arial,sans-serif;line-height:1.5;color:#333;min-width:0;flex:1;word-wrap:break-word;white-space:normal;overflow-wrap:break-word}.markdown-container table{display:block;width:100%;border-collapse:collapse;margin:1em 0;font-size:1.125rem;box-shadow:0 1px 2px #0000000d;border-right:none;border-left:none;overflow-x:auto}.markdown-container th{background-color:#f3f4f6;color:#333;font-weight:600;padding:10px;text-align:left;border-bottom:2px solid #d1d5db}.markdown-container td{padding:10px;border-top:1px solid #e5e7eb;vertical-align:top;text-align:left}@media (max-width: 1024px){.markdown-container table{font-size:1rem}}@media (max-width: 768px){.markdown-container table{font-size:.875rem}}.markdown-container tr:nth-child(2n){background-color:#f9fafb}.markdown-container tr:hover{background-color:#f1f5f9}.markdown-container a{color:#0366d6;text-decoration:underline}.markdown-container img{max-width:100%;margin:10px 0;border-radius:6px}.markdown-container pre{background:#f6f8fa;padding:12px;border-radius:6px;overflow-x:auto;margin:1em 0}.markdown-container code{font-family:monospace}.markdown-container p,li{font-size:1rem}.markdown-container ul,.markdown-container ol{padding-left:1.5rem;margin:.5rem 0}.markdown-container ul{list-style-type:disc}.markdown-container ol{list-style-type:decimal}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None });
|
|
471
|
+
}
|
|
472
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MarkdownComponent, decorators: [{
|
|
473
|
+
type: Component,
|
|
474
|
+
args: [{ selector: 'app-markdown', standalone: true, imports: [CommonModule], encapsulation: ViewEncapsulation.None, template: "<div>\r\n <div class=\"markdown-container\" [innerHTML]=\"safeHtml\"></div>\r\n</div>", styles: [".markdown-container{font-family:Arial,sans-serif;line-height:1.5;color:#333;min-width:0;flex:1;word-wrap:break-word;white-space:normal;overflow-wrap:break-word}.markdown-container table{display:block;width:100%;border-collapse:collapse;margin:1em 0;font-size:1.125rem;box-shadow:0 1px 2px #0000000d;border-right:none;border-left:none;overflow-x:auto}.markdown-container th{background-color:#f3f4f6;color:#333;font-weight:600;padding:10px;text-align:left;border-bottom:2px solid #d1d5db}.markdown-container td{padding:10px;border-top:1px solid #e5e7eb;vertical-align:top;text-align:left}@media (max-width: 1024px){.markdown-container table{font-size:1rem}}@media (max-width: 768px){.markdown-container table{font-size:.875rem}}.markdown-container tr:nth-child(2n){background-color:#f9fafb}.markdown-container tr:hover{background-color:#f1f5f9}.markdown-container a{color:#0366d6;text-decoration:underline}.markdown-container img{max-width:100%;margin:10px 0;border-radius:6px}.markdown-container pre{background:#f6f8fa;padding:12px;border-radius:6px;overflow-x:auto;margin:1em 0}.markdown-container code{font-family:monospace}.markdown-container p,li{font-size:1rem}.markdown-container ul,.markdown-container ol{padding-left:1.5rem;margin:.5rem 0}.markdown-container ul{list-style-type:disc}.markdown-container ol{list-style-type:decimal}\n"] }]
|
|
475
|
+
}], ctorParameters: () => [{ type: i1$1.DomSanitizer }], propDecorators: { content: [{
|
|
476
|
+
type: Input
|
|
477
|
+
}] } });
|
|
478
|
+
|
|
479
|
+
class RobotIconComponent {
|
|
480
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: RobotIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
481
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: RobotIconComponent, isStandalone: true, selector: "lib-robot-icon", host: { styleAttribute: "display: inline-flex; width: 1.5em; height: 1.5em;" }, ngImport: i0, template: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -960 960 960\" width=\"100%\" height=\"100%\" fill=\"currentColor\">\r\n <path d=\"M440-120v-80h320v-284q0-117-81.5-198.5T480-764q-117 0-198.5 81.5T200-484v244h-40q-33 0-56.5-23.5T80-320v-80q0-21 10.5-39.5T120-469l3-53q8-68 39.5-126t79-101q47.5-43 109-67T480-840q68 0 129 24t109 66.5Q766-707 797-649t40 126l3 52q19 9 29.5 27t10.5 38v92q0 20-10.5 38T840-249v49q0 33-23.5 56.5T760-120H440Zm-80-280q-17 0-28.5-11.5T320-440q0-17 11.5-28.5T360-480q17 0 28.5 11.5T400-440q0 17-11.5 28.5T360-400Zm240 0q-17 0-28.5-11.5T560-440q0-17 11.5-28.5T600-480q17 0 28.5 11.5T640-440q0 17-11.5 28.5T600-400Zm-359-62q-7-106 64-182t177-76q89 0 156.5 56.5T720-519q-91-1-167.5-49T435-698q-16 80-67.5 142.5T241-462Z\"/>\r\n</svg>\r\n", styles: [""] });
|
|
482
|
+
}
|
|
483
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: RobotIconComponent, decorators: [{
|
|
484
|
+
type: Component,
|
|
485
|
+
args: [{ selector: 'lib-robot-icon', imports: [], host: {
|
|
486
|
+
style: 'display: inline-flex; width: 1.5em; height: 1.5em;'
|
|
487
|
+
}, template: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -960 960 960\" width=\"100%\" height=\"100%\" fill=\"currentColor\">\r\n <path d=\"M440-120v-80h320v-284q0-117-81.5-198.5T480-764q-117 0-198.5 81.5T200-484v244h-40q-33 0-56.5-23.5T80-320v-80q0-21 10.5-39.5T120-469l3-53q8-68 39.5-126t79-101q47.5-43 109-67T480-840q68 0 129 24t109 66.5Q766-707 797-649t40 126l3 52q19 9 29.5 27t10.5 38v92q0 20-10.5 38T840-249v49q0 33-23.5 56.5T760-120H440Zm-80-280q-17 0-28.5-11.5T320-440q0-17 11.5-28.5T360-480q17 0 28.5 11.5T400-440q0 17-11.5 28.5T360-400Zm240 0q-17 0-28.5-11.5T560-440q0-17 11.5-28.5T600-480q17 0 28.5 11.5T640-440q0 17-11.5 28.5T600-400Zm-359-62q-7-106 64-182t177-76q89 0 156.5 56.5T720-519q-91-1-167.5-49T435-698q-16 80-67.5 142.5T241-462Z\"/>\r\n</svg>\r\n" }]
|
|
488
|
+
}] });
|
|
489
|
+
|
|
490
|
+
class AgentAvatarComponent {
|
|
491
|
+
size = 'large';
|
|
492
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AgentAvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
493
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: AgentAvatarComponent, isStandalone: true, selector: "lib-agent-avatar", inputs: { size: "size" }, ngImport: i0, template: "<p-avatar [size]=\"size\" class=\"agent-avatar\" shape=\"circle\">\r\n <lib-robot-icon style=\"color: var(--primary-color)\"></lib-robot-icon>\r\n</p-avatar> \r\n\r\n", styles: [".agent-avatar{--p1: color-mix(in srgb, var(--primary-color) 18%, transparent);flex-shrink:0;background-color:var(--p1)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "style", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "component", type: RobotIconComponent, selector: "lib-robot-icon" }] });
|
|
494
|
+
}
|
|
495
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AgentAvatarComponent, decorators: [{
|
|
496
|
+
type: Component,
|
|
497
|
+
args: [{ selector: 'lib-agent-avatar', imports: [CommonModule, Avatar, RobotIconComponent], template: "<p-avatar [size]=\"size\" class=\"agent-avatar\" shape=\"circle\">\r\n <lib-robot-icon style=\"color: var(--primary-color)\"></lib-robot-icon>\r\n</p-avatar> \r\n\r\n", styles: [".agent-avatar{--p1: color-mix(in srgb, var(--primary-color) 18%, transparent);flex-shrink:0;background-color:var(--p1)}\n"] }]
|
|
498
|
+
}], propDecorators: { size: [{
|
|
499
|
+
type: Input
|
|
500
|
+
}] } });
|
|
501
|
+
|
|
502
|
+
class MessagesComponent {
|
|
503
|
+
chatContainer;
|
|
504
|
+
messages = [];
|
|
505
|
+
isBotWritting = false;
|
|
506
|
+
scrollMode = 'edge';
|
|
507
|
+
agentIcon = 'pi pi-sparkles';
|
|
508
|
+
ngAfterViewChecked() {
|
|
509
|
+
this.scrollToBottom();
|
|
510
|
+
}
|
|
511
|
+
scrollToBottom(behavior = 'smooth') {
|
|
512
|
+
if (!this.chatContainer)
|
|
513
|
+
return;
|
|
514
|
+
const el = this.chatContainer.nativeElement;
|
|
515
|
+
el.scrollTo({ top: el.scrollHeight, behavior });
|
|
516
|
+
}
|
|
517
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MessagesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
518
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: MessagesComponent, isStandalone: true, selector: "lib-messages", inputs: { messages: "messages", isBotWritting: "isBotWritting", scrollMode: "scrollMode", agentIcon: "agentIcon" }, host: { classAttribute: "block h-full w-full overflow-hidden" }, viewQueries: [{ propertyName: "chatContainer", first: true, predicate: ["chatContainer"], descendants: true }], ngImport: i0, template: "<div class=\"messages-container\" [class.scroll-inline]=\"scrollMode === 'inline'\" #chatContainer>\r\n <div class=\"messages-content\">\r\n <div *ngFor=\"let msg of messages\" class=\"message-item\">\r\n <div class=\"flex w-full\" [ngClass]=\"msg.role !== 'user' ? 'justify-content-start' : 'justify-content-end'\">\r\n <div [ngClass]=\"msg.role !== 'user' ? 'message-bot' : 'message-user'\">\r\n <app-table *ngIf=\"msg.type === 'table'\" [table]=\"msg.content\"></app-table>\r\n <app-file *ngIf=\"msg.type === 'file'\" [files]=\"msg.content\" [role]=\"msg.role\"></app-file>\r\n <app-text *ngIf=\"msg.type === 'text'\" [text]=\"msg.content\" [role]=\"msg.role\"></app-text>\r\n <div [ngClass]=\"msg.role !== 'user' ? 'flex items-start gap-2' : ''\" *ngIf=\"msg.type === 'markdown'\">\r\n <lib-agent-avatar *ngIf=\"msg.role !== 'user'\" size=\"normal\"></lib-agent-avatar>\r\n <app-markdown [content]=\"msg.content\"></app-markdown>\r\n </div>\r\n <div *ngIf=\"msg.type === 'error'\" class=\"message-item\">\r\n <div class=\"flex gap-2 message-bot\">\r\n <lib-agent-avatar size=\"normal\"></lib-agent-avatar>\r\n <p-message severity=\"error\">{{msg.content}}</p-message>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div *ngIf=\"isBotWritting\" class=\"message-item\">\r\n <div class=\"flex gap-2 message-bot\">\r\n <lib-agent-avatar size=\"normal\"></lib-agent-avatar>\r\n <div\r\n class=\"w-fit\">\r\n <span class=\"typing-dots\">\r\n <span class=\"dot\"></span>\r\n <span class=\"dot\"></span>\r\n <span class=\"dot\"></span>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div #bottom></div>\r\n</div>", styles: ["@charset \"UTF-8\";.messages-container{height:100%;width:100%;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:rgba(156,163,175,.5) transparent}.messages-container::-webkit-scrollbar{width:8px}.messages-container::-webkit-scrollbar-track{background:transparent}.messages-container::-webkit-scrollbar-thumb{background-color:#9ca3af80;border-radius:4px}.messages-container::-webkit-scrollbar-thumb:hover{background-color:#9ca3afb3}@media (max-width: 768px){.messages-container{scrollbar-width:none;-ms-overflow-style:none}.messages-container::-webkit-scrollbar{display:none}}.messages-container.scroll-inline{max-width:60rem;margin:0 auto}.messages-container.scroll-inline .messages-content{max-width:100%}.messages-content{max-width:60rem;margin:0 auto;padding:1rem;display:flex;flex-direction:column;gap:.75rem}.message-item{width:100%}.message-bot{width:100%;max-width:100%}.message-user{max-width:85%}.typing-dots{display:inline-flex;align-items:center;gap:4px;padding:4px 0}.typing-dots .dot{width:8px;height:8px;background-color:var(--primary-color);border-radius:50%;animation:typingBounce 1.4s infinite ease-in-out both}.typing-dots .dot:nth-child(1){animation-delay:0s}.typing-dots .dot:nth-child(2){animation-delay:.2s}.typing-dots .dot:nth-child(3){animation-delay:.4s}@keyframes typingBounce{0%,80%,to{transform:scale(.6);opacity:.5}40%{transform:scale(1);opacity:1}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: TextComponent, selector: "app-text", inputs: ["text", "role"] }, { kind: "component", type: FileComponent, selector: "app-file", inputs: ["files", "role", "remove"] }, { kind: "component", type: TableComponent, selector: "app-table", inputs: ["table"] }, { kind: "component", type: MarkdownComponent, selector: "app-markdown", inputs: ["content"] }, { kind: "component", type: AgentAvatarComponent, selector: "lib-agent-avatar", inputs: ["size"] }, { kind: "ngmodule", type: MessageModule }, { kind: "component", type: i2$1.Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }] });
|
|
519
|
+
}
|
|
520
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MessagesComponent, decorators: [{
|
|
521
|
+
type: Component,
|
|
522
|
+
args: [{ selector: 'lib-messages', imports: [CommonModule, FormsModule, ButtonModule, TextComponent, FileComponent, TableComponent, MarkdownComponent, AgentAvatarComponent, MessageModule], host: {
|
|
523
|
+
'class': 'block h-full w-full overflow-hidden'
|
|
524
|
+
}, template: "<div class=\"messages-container\" [class.scroll-inline]=\"scrollMode === 'inline'\" #chatContainer>\r\n <div class=\"messages-content\">\r\n <div *ngFor=\"let msg of messages\" class=\"message-item\">\r\n <div class=\"flex w-full\" [ngClass]=\"msg.role !== 'user' ? 'justify-content-start' : 'justify-content-end'\">\r\n <div [ngClass]=\"msg.role !== 'user' ? 'message-bot' : 'message-user'\">\r\n <app-table *ngIf=\"msg.type === 'table'\" [table]=\"msg.content\"></app-table>\r\n <app-file *ngIf=\"msg.type === 'file'\" [files]=\"msg.content\" [role]=\"msg.role\"></app-file>\r\n <app-text *ngIf=\"msg.type === 'text'\" [text]=\"msg.content\" [role]=\"msg.role\"></app-text>\r\n <div [ngClass]=\"msg.role !== 'user' ? 'flex items-start gap-2' : ''\" *ngIf=\"msg.type === 'markdown'\">\r\n <lib-agent-avatar *ngIf=\"msg.role !== 'user'\" size=\"normal\"></lib-agent-avatar>\r\n <app-markdown [content]=\"msg.content\"></app-markdown>\r\n </div>\r\n <div *ngIf=\"msg.type === 'error'\" class=\"message-item\">\r\n <div class=\"flex gap-2 message-bot\">\r\n <lib-agent-avatar size=\"normal\"></lib-agent-avatar>\r\n <p-message severity=\"error\">{{msg.content}}</p-message>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div *ngIf=\"isBotWritting\" class=\"message-item\">\r\n <div class=\"flex gap-2 message-bot\">\r\n <lib-agent-avatar size=\"normal\"></lib-agent-avatar>\r\n <div\r\n class=\"w-fit\">\r\n <span class=\"typing-dots\">\r\n <span class=\"dot\"></span>\r\n <span class=\"dot\"></span>\r\n <span class=\"dot\"></span>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div #bottom></div>\r\n</div>", styles: ["@charset \"UTF-8\";.messages-container{height:100%;width:100%;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:rgba(156,163,175,.5) transparent}.messages-container::-webkit-scrollbar{width:8px}.messages-container::-webkit-scrollbar-track{background:transparent}.messages-container::-webkit-scrollbar-thumb{background-color:#9ca3af80;border-radius:4px}.messages-container::-webkit-scrollbar-thumb:hover{background-color:#9ca3afb3}@media (max-width: 768px){.messages-container{scrollbar-width:none;-ms-overflow-style:none}.messages-container::-webkit-scrollbar{display:none}}.messages-container.scroll-inline{max-width:60rem;margin:0 auto}.messages-container.scroll-inline .messages-content{max-width:100%}.messages-content{max-width:60rem;margin:0 auto;padding:1rem;display:flex;flex-direction:column;gap:.75rem}.message-item{width:100%}.message-bot{width:100%;max-width:100%}.message-user{max-width:85%}.typing-dots{display:inline-flex;align-items:center;gap:4px;padding:4px 0}.typing-dots .dot{width:8px;height:8px;background-color:var(--primary-color);border-radius:50%;animation:typingBounce 1.4s infinite ease-in-out both}.typing-dots .dot:nth-child(1){animation-delay:0s}.typing-dots .dot:nth-child(2){animation-delay:.2s}.typing-dots .dot:nth-child(3){animation-delay:.4s}@keyframes typingBounce{0%,80%,to{transform:scale(.6);opacity:.5}40%{transform:scale(1);opacity:1}}\n"] }]
|
|
525
|
+
}], propDecorators: { chatContainer: [{
|
|
526
|
+
type: ViewChild,
|
|
527
|
+
args: ['chatContainer']
|
|
528
|
+
}], messages: [{
|
|
529
|
+
type: Input
|
|
530
|
+
}], isBotWritting: [{
|
|
531
|
+
type: Input
|
|
532
|
+
}], scrollMode: [{
|
|
533
|
+
type: Input
|
|
534
|
+
}], agentIcon: [{
|
|
535
|
+
type: Input
|
|
536
|
+
}] } });
|
|
537
|
+
|
|
538
|
+
class InputComponent {
|
|
539
|
+
files = [];
|
|
540
|
+
isRecording = false;
|
|
541
|
+
isLoading = false;
|
|
542
|
+
isBotWritting = false;
|
|
543
|
+
header;
|
|
544
|
+
mode = 'default';
|
|
545
|
+
sendInput = new EventEmitter();
|
|
546
|
+
startRecording = new EventEmitter();
|
|
547
|
+
stopRecording = new EventEmitter();
|
|
548
|
+
uploadFile = new EventEmitter();
|
|
549
|
+
userInput = '';
|
|
439
550
|
handleKeyDown(event) {
|
|
440
551
|
const trimmedInput = this.userInput?.trim();
|
|
441
552
|
if (event.key === 'Enter' && !event.shiftKey) {
|
|
442
|
-
if (!trimmedInput || this.isBotWritting) {
|
|
553
|
+
if (!trimmedInput || this.isBotWritting || this.isLoading) {
|
|
443
554
|
event.preventDefault();
|
|
444
555
|
return;
|
|
445
556
|
}
|
|
446
557
|
event.preventDefault();
|
|
447
|
-
this.
|
|
558
|
+
this.sendInput.emit(trimmedInput);
|
|
559
|
+
this.userInput = '';
|
|
448
560
|
}
|
|
449
561
|
}
|
|
450
|
-
|
|
451
|
-
const
|
|
452
|
-
if (
|
|
453
|
-
return
|
|
562
|
+
send() {
|
|
563
|
+
const trimmedInput = this.userInput?.trim();
|
|
564
|
+
if (!trimmedInput || this.isBotWritting) {
|
|
565
|
+
return;
|
|
454
566
|
}
|
|
455
|
-
|
|
456
|
-
|
|
567
|
+
this.sendInput.emit(trimmedInput);
|
|
568
|
+
this.userInput = '';
|
|
569
|
+
}
|
|
570
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: InputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
571
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: InputComponent, isStandalone: true, selector: "lib-input", inputs: { files: "files", isRecording: "isRecording", isLoading: "isLoading", isBotWritting: "isBotWritting", header: "header", mode: "mode" }, outputs: { sendInput: "sendInput", startRecording: "startRecording", stopRecording: "stopRecording", uploadFile: "uploadFile" }, host: { properties: { "class.inputModeDefault": "mode === \"default\"", "class.inputModeBubble": "mode === \"bubble\"" }, classAttribute: "block w-full" }, ngImport: i0, template: "<div class=\"input-wrapper input-default\">\r\n <app-file [files]=\"files || []\" role=\"assitant\" [remove]=\"true\" />\r\n <form (ngSubmit)=\"send()\" class=\"w-full flex flex-column gap-2\">\r\n <textarea pTextarea id=\"input\" name=\"userInput\" [(ngModel)]=\"userInput\"\r\n class=\"custom-input w-full text-base\" style=\"max-height: 200px\"\r\n [placeholder]=\"header?.placeholder || ''\" rows=\"1\" [autoResize]=\"true\"\r\n (keydown)=\"handleKeyDown($event)\"></textarea>\r\n <div class=\"flex align-items-center justify-content-between w-full\">\r\n\r\n <p-button icon=\"pi pi-paperclip\" [text]=\"true\" (click)=\"fileInputChat.click()\"\r\n [rounded]=\"true\" />\r\n <input type=\"file\" #fileInputChat multiple (change)=\"uploadFile.emit($event)\" style=\"display: none;\">\r\n\r\n <div class=\"flex align-items-center justify-content-end w-full gap-2\">\r\n <p-button *ngIf=\"!isRecording\" icon=\"pi pi-microphone\" [text]=\"true\" (click)=\"startRecording.emit()\"\r\n [rounded]=\"true\" />\r\n <p-button *ngIf=\"isRecording\" icon=\"pi pi-stop\" [text]=\"true\" (click)=\"stopRecording.emit()\"\r\n severity=\"danger\" [rounded]=\"true\" />\r\n <p-button icon=\"pi pi-send\" [rounded]=\"true\" type=\"submit\"\r\n [disabled]=\"isLoading || isBotWritting || (!userInput.trim() && (!files || files.length === 0))\" />\r\n </div>\r\n </div>\r\n </form>\r\n</div>", styles: [".input-wrapper{display:flex;flex-direction:column;gap:.5rem;padding:.5rem;width:100%}:host.inputModeDefault .input-default{border:1px solid var(--p-surface-400);border-radius:1.5rem;background-color:var(--p-surface-0)}:host.inputModeBubble .input-default{border:none;border-radius:0;background-color:var(--p-surface-0);border-top:1px solid var(--p-surface-200);padding:.75rem 1rem}.custom-input{border:none!important;box-shadow:none!important}.custom-input:focus{outline:none!important;box-shadow:none!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: ChipModule }, { kind: "component", type: FileComponent, selector: "app-file", inputs: ["files", "role", "remove"] }] });
|
|
572
|
+
}
|
|
573
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: InputComponent, decorators: [{
|
|
574
|
+
type: Component,
|
|
575
|
+
args: [{ selector: 'lib-input', imports: [CommonModule, FormsModule, ButtonModule, Textarea, ChipModule, FileComponent], host: {
|
|
576
|
+
'class': 'block w-full',
|
|
577
|
+
'[class.inputModeDefault]': 'mode === "default"',
|
|
578
|
+
'[class.inputModeBubble]': 'mode === "bubble"',
|
|
579
|
+
}, template: "<div class=\"input-wrapper input-default\">\r\n <app-file [files]=\"files || []\" role=\"assitant\" [remove]=\"true\" />\r\n <form (ngSubmit)=\"send()\" class=\"w-full flex flex-column gap-2\">\r\n <textarea pTextarea id=\"input\" name=\"userInput\" [(ngModel)]=\"userInput\"\r\n class=\"custom-input w-full text-base\" style=\"max-height: 200px\"\r\n [placeholder]=\"header?.placeholder || ''\" rows=\"1\" [autoResize]=\"true\"\r\n (keydown)=\"handleKeyDown($event)\"></textarea>\r\n <div class=\"flex align-items-center justify-content-between w-full\">\r\n\r\n <p-button icon=\"pi pi-paperclip\" [text]=\"true\" (click)=\"fileInputChat.click()\"\r\n [rounded]=\"true\" />\r\n <input type=\"file\" #fileInputChat multiple (change)=\"uploadFile.emit($event)\" style=\"display: none;\">\r\n\r\n <div class=\"flex align-items-center justify-content-end w-full gap-2\">\r\n <p-button *ngIf=\"!isRecording\" icon=\"pi pi-microphone\" [text]=\"true\" (click)=\"startRecording.emit()\"\r\n [rounded]=\"true\" />\r\n <p-button *ngIf=\"isRecording\" icon=\"pi pi-stop\" [text]=\"true\" (click)=\"stopRecording.emit()\"\r\n severity=\"danger\" [rounded]=\"true\" />\r\n <p-button icon=\"pi pi-send\" [rounded]=\"true\" type=\"submit\"\r\n [disabled]=\"isLoading || isBotWritting || (!userInput.trim() && (!files || files.length === 0))\" />\r\n </div>\r\n </div>\r\n </form>\r\n</div>", styles: [".input-wrapper{display:flex;flex-direction:column;gap:.5rem;padding:.5rem;width:100%}:host.inputModeDefault .input-default{border:1px solid var(--p-surface-400);border-radius:1.5rem;background-color:var(--p-surface-0)}:host.inputModeBubble .input-default{border:none;border-radius:0;background-color:var(--p-surface-0);border-top:1px solid var(--p-surface-200);padding:.75rem 1rem}.custom-input{border:none!important;box-shadow:none!important}.custom-input:focus{outline:none!important;box-shadow:none!important}\n"] }]
|
|
580
|
+
}], propDecorators: { files: [{
|
|
581
|
+
type: Input
|
|
582
|
+
}], isRecording: [{
|
|
583
|
+
type: Input
|
|
584
|
+
}], isLoading: [{
|
|
585
|
+
type: Input
|
|
586
|
+
}], isBotWritting: [{
|
|
587
|
+
type: Input
|
|
588
|
+
}], header: [{
|
|
589
|
+
type: Input
|
|
590
|
+
}], mode: [{
|
|
591
|
+
type: Input
|
|
592
|
+
}], sendInput: [{
|
|
593
|
+
type: Output
|
|
594
|
+
}], startRecording: [{
|
|
595
|
+
type: Output
|
|
596
|
+
}], stopRecording: [{
|
|
597
|
+
type: Output
|
|
598
|
+
}], uploadFile: [{
|
|
599
|
+
type: Output
|
|
600
|
+
}] } });
|
|
601
|
+
|
|
602
|
+
class GeneralAgentComponent {
|
|
603
|
+
// Propiedades internas (ya no inputs/outputs)
|
|
604
|
+
agentService = inject(AgentService);
|
|
605
|
+
messages$ = this.agentService.messages$;
|
|
606
|
+
isBotWritting$ = this.agentService.isBotWritting$;
|
|
607
|
+
headerConfig$ = this.agentService.headerConfig$;
|
|
608
|
+
isRecording$ = this.agentService.isRecording$;
|
|
609
|
+
selectedFiles$ = this.agentService.selectedFiles$;
|
|
610
|
+
isLoading$ = this.agentService.isLoading$;
|
|
611
|
+
vm$ = combineLatest({
|
|
612
|
+
header: this.headerConfig$,
|
|
613
|
+
files: this.selectedFiles$,
|
|
614
|
+
isRec: this.isRecording$,
|
|
615
|
+
bot: this.isBotWritting$,
|
|
616
|
+
messages: this.messages$,
|
|
617
|
+
loading: this.isLoading$
|
|
618
|
+
});
|
|
619
|
+
agentId = '';
|
|
620
|
+
idKatios = '';
|
|
621
|
+
/** 'edge' = scroll en el borde del contenedor, 'inline' = scroll pegado al chat */
|
|
622
|
+
scrollMode = 'edge';
|
|
623
|
+
async ngOnChanges(changes) {
|
|
624
|
+
if (changes['agentId'] || changes['idKatios']) {
|
|
625
|
+
await this.initAgent();
|
|
457
626
|
}
|
|
458
|
-
return { 'font-size': '2rem', 'width': '2.5rem', 'height': '2.5rem', 'aspect-ratio': '1 / 1' };
|
|
459
627
|
}
|
|
460
|
-
|
|
461
|
-
|
|
628
|
+
async initAgent() {
|
|
629
|
+
this.agentService.agentId = this.agentId;
|
|
630
|
+
this.agentService.idKatios = this.idKatios;
|
|
631
|
+
await this.agentService.loadAgent();
|
|
632
|
+
}
|
|
633
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: GeneralAgentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
634
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: GeneralAgentComponent, isStandalone: true, selector: "app-general-agent", inputs: { agentId: "agentId", idKatios: "idKatios", scrollMode: "scrollMode" }, host: { classAttribute: "block w-full h-full" }, providers: [AgentService], usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"vm$ | async as vm\">\r\n <!-- Vista inicial (bienvenida) -->\r\n <div *ngIf=\"agentService.isInitial\" class=\"custom-chat flex flex-column items-center justify-content-center w-full\">\r\n <div class=\"flex flex-column items-center justify-content-center gap-4 p-3 mb-3 text-center md:p-1\">\r\n <h2 *ngIf=\"!vm.loading\" class=\"text-3xl font-semibold text-gray-900 lg:text-4xl\">{{vm.header?.title ||\r\n 'Agente'}}</h2>\r\n <p-skeleton *ngIf=\"vm.loading\" width=\"25rem\" height=\"2rem\" borderRadius=\"16px\"></p-skeleton>\r\n <div\r\n class=\"flex items-start justify-content-center gap-2\">\r\n <lib-agent-avatar [size]=\"'large'\"></lib-agent-avatar>\r\n <div *ngIf=\"vm.header?.initialMessages && !vm.loading\" class=\"flex flex-column items-start\">\r\n <div *ngFor=\"let msg of vm.header?.initialMessages\" class=\"text-left\">\r\n <span class=\"m-0 text-gray-700 text-base lg:text-lg\">{{msg}}</span>\r\n </div>\r\n </div>\r\n <div class=\"flex flex-column gap-2\">\r\n <p-skeleton *ngIf=\"vm.loading\" width=\"5rem\" borderRadius=\"16px\"></p-skeleton>\r\n <p-skeleton *ngIf=\"vm.loading\" width=\"15rem\" borderRadius=\"16px\"></p-skeleton>\r\n </div>\r\n </div>\r\n <p *ngIf=\"!vm.loading\" class=\"text-base text-gray-500\">{{vm.header?.subtitle}}</p>\r\n <p-skeleton *ngIf=\"vm.loading\" width=\"17rem\" borderRadius=\"16px\"></p-skeleton>\r\n </div>\r\n <!-- Input centrado para vista inicial -->\r\n <div class=\"flex flex-column items-center justify-content-center custom-size w-full pt-1 pb-1\">\r\n <lib-input [files]=\"vm.files || []\" [isRecording]=\"vm.isRec || false\" [isLoading]=\"vm.loading || false\" [isBotWritting]=\"vm.bot || false\" [header]=\"vm.header\" (sendInput)=\"agentService.send($event)\" (startRecording)=\"agentService.startRecording()\" (stopRecording)=\"agentService.stopRecording()\" (uploadFile)=\"agentService.uploadFile($event)\"></lib-input>\r\n <p class=\"mx-auto text-sm text-gray-600 mt-1\">{{vm.header?.footer}}</p>\r\n </div>\r\n </div>\r\n\r\n <!-- Vista de chat (con mensajes) -->\r\n <div *ngIf=\"!agentService.isInitial\" class=\"custom-chat chat-layout flex flex-column w-full\">\r\n <!-- \u00C1rea de mensajes con scroll -->\r\n <div class=\"messages-area flex-1 overflow-hidden\">\r\n <lib-messages [messages]=\"vm.messages || []\" [isBotWritting]=\"vm.bot || false\" [scrollMode]=\"scrollMode\"></lib-messages>\r\n </div>\r\n <!-- Input fijo abajo -->\r\n <div class=\"input-area flex flex-column items-center w-full py-2 px-2 md:px-0\">\r\n <div class=\"custom-size w-full\">\r\n <lib-input [files]=\"vm.files || []\" [isRecording]=\"vm.isRec || false\" [isLoading]=\"vm.loading || false\" [isBotWritting]=\"vm.bot || false\" [header]=\"vm.header\" (sendInput)=\"agentService.send($event)\" (startRecording)=\"agentService.startRecording()\" (stopRecording)=\"agentService.stopRecording()\" (uploadFile)=\"agentService.uploadFile($event)\"></lib-input>\r\n <p class=\"mx-auto text-sm text-gray-600 mt-1 text-center\">{{vm.header?.footer}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n</ng-container>", styles: [".custom-chat{height:100%;width:100%}.chat-layout{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden}.messages-area{flex:1 1 auto;min-height:0;overflow:hidden}.input-area{flex:0 0 auto;z-index:10}.custom-size{width:100%;max-width:60rem;padding:0 1rem;margin:0 auto}.welcome-agent{--p1: color-mix(in srgb, var(--primary-color) 8%, transparent);background-color:var(--p1)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: AgentAvatarComponent, selector: "lib-agent-avatar", inputs: ["size"] }, { kind: "ngmodule", type: TagModule }, { kind: "ngmodule", type: ChipModule }, { kind: "component", type: MessagesComponent, selector: "lib-messages", inputs: ["messages", "isBotWritting", "scrollMode", "agentIcon"] }, { kind: "component", type: Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: InputComponent, selector: "lib-input", inputs: ["files", "isRecording", "isLoading", "isBotWritting", "header", "mode"], outputs: ["sendInput", "startRecording", "stopRecording", "uploadFile"] }] });
|
|
462
635
|
}
|
|
463
636
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: GeneralAgentComponent, decorators: [{
|
|
464
637
|
type: Component,
|
|
465
|
-
args: [{ selector: 'app-general-agent', standalone: true, imports: [CommonModule, FormsModule, ButtonModule,
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}] }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }], propDecorators: { chatContainer: [{
|
|
470
|
-
type: ViewChild,
|
|
471
|
-
args: ['chatContainer']
|
|
472
|
-
}], agentType: [{
|
|
638
|
+
args: [{ selector: 'app-general-agent', standalone: true, imports: [CommonModule, FormsModule, ButtonModule, AgentAvatarComponent, TagModule, ChipModule, MessagesComponent, Skeleton, InputComponent], providers: [AgentService], host: {
|
|
639
|
+
'class': 'block w-full h-full'
|
|
640
|
+
}, template: "<ng-container *ngIf=\"vm$ | async as vm\">\r\n <!-- Vista inicial (bienvenida) -->\r\n <div *ngIf=\"agentService.isInitial\" class=\"custom-chat flex flex-column items-center justify-content-center w-full\">\r\n <div class=\"flex flex-column items-center justify-content-center gap-4 p-3 mb-3 text-center md:p-1\">\r\n <h2 *ngIf=\"!vm.loading\" class=\"text-3xl font-semibold text-gray-900 lg:text-4xl\">{{vm.header?.title ||\r\n 'Agente'}}</h2>\r\n <p-skeleton *ngIf=\"vm.loading\" width=\"25rem\" height=\"2rem\" borderRadius=\"16px\"></p-skeleton>\r\n <div\r\n class=\"flex items-start justify-content-center gap-2\">\r\n <lib-agent-avatar [size]=\"'large'\"></lib-agent-avatar>\r\n <div *ngIf=\"vm.header?.initialMessages && !vm.loading\" class=\"flex flex-column items-start\">\r\n <div *ngFor=\"let msg of vm.header?.initialMessages\" class=\"text-left\">\r\n <span class=\"m-0 text-gray-700 text-base lg:text-lg\">{{msg}}</span>\r\n </div>\r\n </div>\r\n <div class=\"flex flex-column gap-2\">\r\n <p-skeleton *ngIf=\"vm.loading\" width=\"5rem\" borderRadius=\"16px\"></p-skeleton>\r\n <p-skeleton *ngIf=\"vm.loading\" width=\"15rem\" borderRadius=\"16px\"></p-skeleton>\r\n </div>\r\n </div>\r\n <p *ngIf=\"!vm.loading\" class=\"text-base text-gray-500\">{{vm.header?.subtitle}}</p>\r\n <p-skeleton *ngIf=\"vm.loading\" width=\"17rem\" borderRadius=\"16px\"></p-skeleton>\r\n </div>\r\n <!-- Input centrado para vista inicial -->\r\n <div class=\"flex flex-column items-center justify-content-center custom-size w-full pt-1 pb-1\">\r\n <lib-input [files]=\"vm.files || []\" [isRecording]=\"vm.isRec || false\" [isLoading]=\"vm.loading || false\" [isBotWritting]=\"vm.bot || false\" [header]=\"vm.header\" (sendInput)=\"agentService.send($event)\" (startRecording)=\"agentService.startRecording()\" (stopRecording)=\"agentService.stopRecording()\" (uploadFile)=\"agentService.uploadFile($event)\"></lib-input>\r\n <p class=\"mx-auto text-sm text-gray-600 mt-1\">{{vm.header?.footer}}</p>\r\n </div>\r\n </div>\r\n\r\n <!-- Vista de chat (con mensajes) -->\r\n <div *ngIf=\"!agentService.isInitial\" class=\"custom-chat chat-layout flex flex-column w-full\">\r\n <!-- \u00C1rea de mensajes con scroll -->\r\n <div class=\"messages-area flex-1 overflow-hidden\">\r\n <lib-messages [messages]=\"vm.messages || []\" [isBotWritting]=\"vm.bot || false\" [scrollMode]=\"scrollMode\"></lib-messages>\r\n </div>\r\n <!-- Input fijo abajo -->\r\n <div class=\"input-area flex flex-column items-center w-full py-2 px-2 md:px-0\">\r\n <div class=\"custom-size w-full\">\r\n <lib-input [files]=\"vm.files || []\" [isRecording]=\"vm.isRec || false\" [isLoading]=\"vm.loading || false\" [isBotWritting]=\"vm.bot || false\" [header]=\"vm.header\" (sendInput)=\"agentService.send($event)\" (startRecording)=\"agentService.startRecording()\" (stopRecording)=\"agentService.stopRecording()\" (uploadFile)=\"agentService.uploadFile($event)\"></lib-input>\r\n <p class=\"mx-auto text-sm text-gray-600 mt-1 text-center\">{{vm.header?.footer}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n</ng-container>", styles: [".custom-chat{height:100%;width:100%}.chat-layout{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden}.messages-area{flex:1 1 auto;min-height:0;overflow:hidden}.input-area{flex:0 0 auto;z-index:10}.custom-size{width:100%;max-width:60rem;padding:0 1rem;margin:0 auto}.welcome-agent{--p1: color-mix(in srgb, var(--primary-color) 8%, transparent);background-color:var(--p1)}\n"] }]
|
|
641
|
+
}], propDecorators: { agentId: [{
|
|
473
642
|
type: Input
|
|
474
|
-
}],
|
|
643
|
+
}], idKatios: [{
|
|
644
|
+
type: Input
|
|
645
|
+
}], scrollMode: [{
|
|
646
|
+
type: Input
|
|
647
|
+
}] } });
|
|
648
|
+
|
|
649
|
+
class BubbleChatComponent {
|
|
650
|
+
agentService = inject(AgentService);
|
|
651
|
+
messages$ = this.agentService.messages$;
|
|
652
|
+
isBotWritting$ = this.agentService.isBotWritting$;
|
|
653
|
+
headerConfig$ = this.agentService.headerConfig$;
|
|
654
|
+
isRecording$ = this.agentService.isRecording$;
|
|
655
|
+
selectedFiles$ = this.agentService.selectedFiles$;
|
|
656
|
+
isLoading$ = this.agentService.isLoading$;
|
|
657
|
+
vm$ = combineLatest({
|
|
658
|
+
header: this.headerConfig$,
|
|
659
|
+
files: this.selectedFiles$,
|
|
660
|
+
isRec: this.isRecording$,
|
|
661
|
+
bot: this.isBotWritting$,
|
|
662
|
+
messages: this.messages$,
|
|
663
|
+
loading: this.isLoading$
|
|
664
|
+
});
|
|
665
|
+
agentId = '';
|
|
666
|
+
idKatios = '';
|
|
667
|
+
async ngOnChanges(changes) {
|
|
668
|
+
if (changes['agentId'] || changes['agentType'] || changes['idKatios']) {
|
|
669
|
+
await this.initAgent();
|
|
670
|
+
this.addInitialMessages();
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
async initAgent() {
|
|
674
|
+
this.agentService.agentId = this.agentId;
|
|
675
|
+
this.agentService.idKatios = this.idKatios;
|
|
676
|
+
await this.agentService.loadAgent();
|
|
677
|
+
}
|
|
678
|
+
addInitialMessages() {
|
|
679
|
+
const initialMessages = [];
|
|
680
|
+
this.agentService.headerConfig?.initialMessages?.map(msg => {
|
|
681
|
+
initialMessages.push({
|
|
682
|
+
role: 'assistant',
|
|
683
|
+
type: 'markdown',
|
|
684
|
+
content: msg
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
this.agentService.addFullMessages(initialMessages);
|
|
688
|
+
}
|
|
689
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: BubbleChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
690
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: BubbleChatComponent, isStandalone: true, selector: "lib-bubble-chat", inputs: { agentId: "agentId", idKatios: "idKatios" }, host: { classAttribute: "block w-full h-full" }, providers: [AgentService], usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"vm$ | async as vm\">\r\n <div class=\"chat-view\">\r\n <div class=\"messages-wrapper\">\r\n <lib-messages [messages]=\"vm.messages\" [isBotWritting]=\"vm.bot || vm.loading\" scrollMode=\"inline\" [agentIcon]=\"vm.header?.avatar || 'pi pi-sparkles'\">\r\n </lib-messages>\r\n </div>\r\n <div class=\"input-wrapper\">\r\n <lib-input mode=\"bubble\" [files]=\"vm.files || []\" [isRecording]=\"vm.isRec || false\" [isLoading]=\"vm.loading || false\"\r\n [isBotWritting]=\"vm.bot || vm.loading\" [header]=\"vm.header\" (sendInput)=\"agentService.send($event)\"\r\n (startRecording)=\"agentService.startRecording()\" (stopRecording)=\"agentService.stopRecording()\"\r\n (uploadFile)=\"agentService.uploadFile($event)\">\r\n </lib-input>\r\n </div>\r\n </div>\r\n</ng-container>", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden}.chat-view{display:flex;flex-direction:column;height:100%;overflow:hidden}.messages-wrapper{flex:1;overflow:hidden;min-height:0}.chat-view .input-wrapper{flex-shrink:0;padding-bottom:env(safe-area-inset-bottom,0)}@media (max-width: 768px){.chat-view{height:100%;max-height:100%}.input-wrapper{padding:8px;padding-bottom:calc(8px + env(safe-area-inset-bottom,0))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "component", type: MessagesComponent, selector: "lib-messages", inputs: ["messages", "isBotWritting", "scrollMode", "agentIcon"] }, { kind: "component", type: InputComponent, selector: "lib-input", inputs: ["files", "isRecording", "isLoading", "isBotWritting", "header", "mode"], outputs: ["sendInput", "startRecording", "stopRecording", "uploadFile"] }] });
|
|
691
|
+
}
|
|
692
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: BubbleChatComponent, decorators: [{
|
|
693
|
+
type: Component,
|
|
694
|
+
args: [{ selector: 'lib-bubble-chat', imports: [CommonModule, MessagesComponent, InputComponent], providers: [AgentService], host: {
|
|
695
|
+
'class': 'block w-full h-full',
|
|
696
|
+
}, template: "<ng-container *ngIf=\"vm$ | async as vm\">\r\n <div class=\"chat-view\">\r\n <div class=\"messages-wrapper\">\r\n <lib-messages [messages]=\"vm.messages\" [isBotWritting]=\"vm.bot || vm.loading\" scrollMode=\"inline\" [agentIcon]=\"vm.header?.avatar || 'pi pi-sparkles'\">\r\n </lib-messages>\r\n </div>\r\n <div class=\"input-wrapper\">\r\n <lib-input mode=\"bubble\" [files]=\"vm.files || []\" [isRecording]=\"vm.isRec || false\" [isLoading]=\"vm.loading || false\"\r\n [isBotWritting]=\"vm.bot || vm.loading\" [header]=\"vm.header\" (sendInput)=\"agentService.send($event)\"\r\n (startRecording)=\"agentService.startRecording()\" (stopRecording)=\"agentService.stopRecording()\"\r\n (uploadFile)=\"agentService.uploadFile($event)\">\r\n </lib-input>\r\n </div>\r\n </div>\r\n</ng-container>", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden}.chat-view{display:flex;flex-direction:column;height:100%;overflow:hidden}.messages-wrapper{flex:1;overflow:hidden;min-height:0}.chat-view .input-wrapper{flex-shrink:0;padding-bottom:env(safe-area-inset-bottom,0)}@media (max-width: 768px){.chat-view{height:100%;max-height:100%}.input-wrapper{padding:8px;padding-bottom:calc(8px + env(safe-area-inset-bottom,0))}}\n"] }]
|
|
697
|
+
}], propDecorators: { agentId: [{
|
|
475
698
|
type: Input
|
|
476
699
|
}], idKatios: [{
|
|
477
700
|
type: Input
|
|
478
701
|
}] } });
|
|
479
702
|
|
|
703
|
+
class BubbleAgentComponent {
|
|
704
|
+
aiAgentsGatewayService;
|
|
705
|
+
messageService;
|
|
706
|
+
idKatios = '';
|
|
707
|
+
// Estado del componente
|
|
708
|
+
isOpen = false;
|
|
709
|
+
selectedAgent = null;
|
|
710
|
+
agents = [];
|
|
711
|
+
loading = false;
|
|
712
|
+
constructor(aiAgentsGatewayService, messageService) {
|
|
713
|
+
this.aiAgentsGatewayService = aiAgentsGatewayService;
|
|
714
|
+
this.messageService = messageService;
|
|
715
|
+
}
|
|
716
|
+
ngOnChanges() {
|
|
717
|
+
if (this.idKatios) {
|
|
718
|
+
this.loading = true;
|
|
719
|
+
this.aiAgentsGatewayService.getAgents(this.idKatios).subscribe({
|
|
720
|
+
next: (response) => {
|
|
721
|
+
if (response.data)
|
|
722
|
+
this.agents = response.data;
|
|
723
|
+
this.loading = false;
|
|
724
|
+
},
|
|
725
|
+
error: (err) => {
|
|
726
|
+
console.error('Error al cargar agentes desde el servicio:', err);
|
|
727
|
+
this.agents = [];
|
|
728
|
+
this.messageService.add({ severity: 'error', summary: 'Error', detail: 'No se pudieron cargar los agentes. Por favor, inténtalo de nuevo más tarde.' });
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
toggleChat() {
|
|
734
|
+
this.isOpen = !this.isOpen;
|
|
735
|
+
// Si se cierra, volver a la lista de agentes
|
|
736
|
+
// if (!this.isOpen) {
|
|
737
|
+
// this.selectedAgent = null;
|
|
738
|
+
// this.messages = [];
|
|
739
|
+
// }
|
|
740
|
+
}
|
|
741
|
+
selectAgent(agent) {
|
|
742
|
+
this.selectedAgent = agent;
|
|
743
|
+
}
|
|
744
|
+
goBack() {
|
|
745
|
+
this.selectedAgent = null;
|
|
746
|
+
}
|
|
747
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: BubbleAgentComponent, deps: [{ token: AI_AGENTS }, { token: i1$2.MessageService }], target: i0.ɵɵFactoryTarget.Component });
|
|
748
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: BubbleAgentComponent, isStandalone: true, selector: "lib-bubble-agent", inputs: { idKatios: "idKatios" }, usesOnChanges: true, ngImport: i0, template: "<!-- Bot\u00F3n burbuja flotante -->\r\n<button class=\"bubble-button\" (click)=\"toggleChat()\" [class.is-open]=\"isOpen\">\r\n <i class=\"pi\" [ngClass]=\"isOpen ? 'pi-times' : 'pi-comments'\"></i>\r\n</button>\r\n\r\n<!-- Panel del chat -->\r\n<div class=\"chat-panel\" [class.is-open]=\"isOpen\">\r\n <!-- Header del panel -->\r\n <div class=\"p-3 chat-header\">\r\n <div class=\"flex align-items-center gap-2\">\r\n <button *ngIf=\"selectedAgent\" class=\"back-button\" (click)=\"goBack()\">\r\n <i class=\"pi pi-arrow-left\"></i>\r\n </button>\r\n <lib-robot-icon style=\"color: white\" *ngIf=\"selectedAgent\"></lib-robot-icon>\r\n <div class=\"header-text\">\r\n <p class=\"header-title text-white font-semibold text-lg\">{{ selectedAgent ? selectedAgent.name : 'Asistentes disponibles' }}</p>\r\n </div>\r\n </div>\r\n <button class=\"close-button\" (click)=\"toggleChat()\">\r\n <i class=\"pi pi-times\"></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Contenido del panel -->\r\n <div class=\"chat-content\">\r\n <!-- Vista de lista de agentes -->\r\n <div *ngIf=\"!selectedAgent\" class=\"agents-list\">\r\n <!-- Mensaje cuando no hay agentes -->\r\n <div *ngIf=\"!agents || agents.length === 0 || loading\" class=\"no-agents\">\r\n <i class=\"pi text-4xl text-gray-400\" [ngClass]=\"loading ? 'pi-spin pi-cog' : 'pi-users'\"></i>\r\n <p class=\"text-gray-500 mt-2\">{{ loading ? 'Cargando agentes...' : 'No se encontraron agentes' }}</p>\r\n </div>\r\n <!-- Lista de agentes -->\r\n <div class=\"agent-card\" *ngFor=\"let agent of agents\" (click)=\"selectAgent(agent)\">\r\n <lib-agent-avatar \r\n [size]=\"'large'\">\r\n </lib-agent-avatar>\r\n <div class=\"agent-info\">\r\n <p class=\"agent-name text-base font-medium\">{{ agent.name }}</p>\r\n <p class=\"agent-description text-sm text-color-secondary\">{{ agent.description }}</p>\r\n </div>\r\n <i class=\"pi pi-chevron-right agent-arrow\"></i>\r\n </div>\r\n </div>\r\n\r\n <!-- Vista de chat con el agente seleccionado -->\r\n <lib-bubble-chat *ngIf=\"selectedAgent\" \r\n [agentId]=\"selectedAgent.Id\" \r\n [idKatios]=\"selectedAgent.idKatios\">\r\n </lib-bubble-chat>\r\n </div>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";:host{display:block}.bubble-button{position:fixed;bottom:24px;right:24px;width:60px;height:60px;border-radius:50%;background:var(--primary-color);border:none;cursor:pointer;box-shadow:0 4px 20px #b0b4b9;display:flex;align-items:center;justify-content:center;transition:all .3s ease;z-index:1000}.bubble-button:hover{transform:scale(1.1);box-shadow:0 6px 25px #96999d}.bubble-button.is-open{transform:rotate(90deg)}.bubble-button i{font-size:1.5rem;color:#fff;transition:transform .3s ease}.chat-panel{position:fixed;bottom:100px;right:24px;width:380px;height:550px;background:var(--p-surface-0);border-radius:16px;box-shadow:0 10px 40px #00000026;display:flex;flex-direction:column;overflow:hidden;opacity:0;visibility:hidden;transform:translateY(20px) scale(.95);transition:all .3s ease;z-index:999}.chat-panel.is-open{opacity:1;visibility:visible;transform:translateY(0) scale(1)}.chat-header{background:var(--primary-color);color:#fff;display:flex;align-items:center;justify-content:space-between;flex-shrink:0}.header-text{display:flex;flex-direction:column}.header-title{margin:0}.header-subtitle{opacity:.9}.back-button,.close-button{background:#fff3;border:none;border-radius:50%;width:32px;height:32px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .2s ease}.back-button:hover,.close-button:hover{background:#ffffff4d}.back-button i,.close-button i{color:#fff;font-size:.875rem}.chat-content{flex:1;overflow:hidden;display:flex;flex-direction:column}.agents-list{overflow-y:auto;flex:1}.no-agents{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:2rem;text-align:center}.agents-subtitle{color:var(--p-text-muted-color);font-size:.875rem;margin:0 0 16px;text-align:center}.agent-card{display:flex;align-items:center;gap:12px;padding:12px;cursor:pointer;transition:all .2s ease;border-bottom:1px solid var(--p-surface-200)}.agent-card:hover{background:var(--p-surface-100)}.agent-card:last-child{margin-bottom:0}.agent-info{flex:1;min-width:0}.agent-name{margin:0 0 4px}.agent-description{margin:0;line-height:1.4;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;line-clamp:2;-webkit-line-clamp:2}.agent-arrow{color:var(--p-text-muted-color);font-size:.875rem;flex-shrink:0}@media (max-width: 768px){.chat-panel{width:100vw;height:100vh;height:-webkit-fill-available;height:100dvh;max-height:none;inset:0;border-radius:0;transform:translateY(100%)}.chat-panel.is-open{transform:translateY(0)}.bubble-button{bottom:16px;right:16px;width:56px;height:56px}.bubble-button.is-open{opacity:0;visibility:hidden;transform:scale(0)}.chat-content{flex:1;min-height:0;overflow:hidden}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: AgentAvatarComponent, selector: "lib-agent-avatar", inputs: ["size"] }, { kind: "component", type: BubbleChatComponent, selector: "lib-bubble-chat", inputs: ["agentId", "idKatios"] }, { kind: "component", type: RobotIconComponent, selector: "lib-robot-icon" }] });
|
|
749
|
+
}
|
|
750
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: BubbleAgentComponent, decorators: [{
|
|
751
|
+
type: Component,
|
|
752
|
+
args: [{ selector: 'lib-bubble-agent', standalone: true, imports: [CommonModule, ButtonModule, AgentAvatarComponent, BubbleChatComponent, RobotIconComponent], template: "<!-- Bot\u00F3n burbuja flotante -->\r\n<button class=\"bubble-button\" (click)=\"toggleChat()\" [class.is-open]=\"isOpen\">\r\n <i class=\"pi\" [ngClass]=\"isOpen ? 'pi-times' : 'pi-comments'\"></i>\r\n</button>\r\n\r\n<!-- Panel del chat -->\r\n<div class=\"chat-panel\" [class.is-open]=\"isOpen\">\r\n <!-- Header del panel -->\r\n <div class=\"p-3 chat-header\">\r\n <div class=\"flex align-items-center gap-2\">\r\n <button *ngIf=\"selectedAgent\" class=\"back-button\" (click)=\"goBack()\">\r\n <i class=\"pi pi-arrow-left\"></i>\r\n </button>\r\n <lib-robot-icon style=\"color: white\" *ngIf=\"selectedAgent\"></lib-robot-icon>\r\n <div class=\"header-text\">\r\n <p class=\"header-title text-white font-semibold text-lg\">{{ selectedAgent ? selectedAgent.name : 'Asistentes disponibles' }}</p>\r\n </div>\r\n </div>\r\n <button class=\"close-button\" (click)=\"toggleChat()\">\r\n <i class=\"pi pi-times\"></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Contenido del panel -->\r\n <div class=\"chat-content\">\r\n <!-- Vista de lista de agentes -->\r\n <div *ngIf=\"!selectedAgent\" class=\"agents-list\">\r\n <!-- Mensaje cuando no hay agentes -->\r\n <div *ngIf=\"!agents || agents.length === 0 || loading\" class=\"no-agents\">\r\n <i class=\"pi text-4xl text-gray-400\" [ngClass]=\"loading ? 'pi-spin pi-cog' : 'pi-users'\"></i>\r\n <p class=\"text-gray-500 mt-2\">{{ loading ? 'Cargando agentes...' : 'No se encontraron agentes' }}</p>\r\n </div>\r\n <!-- Lista de agentes -->\r\n <div class=\"agent-card\" *ngFor=\"let agent of agents\" (click)=\"selectAgent(agent)\">\r\n <lib-agent-avatar \r\n [size]=\"'large'\">\r\n </lib-agent-avatar>\r\n <div class=\"agent-info\">\r\n <p class=\"agent-name text-base font-medium\">{{ agent.name }}</p>\r\n <p class=\"agent-description text-sm text-color-secondary\">{{ agent.description }}</p>\r\n </div>\r\n <i class=\"pi pi-chevron-right agent-arrow\"></i>\r\n </div>\r\n </div>\r\n\r\n <!-- Vista de chat con el agente seleccionado -->\r\n <lib-bubble-chat *ngIf=\"selectedAgent\" \r\n [agentId]=\"selectedAgent.Id\" \r\n [idKatios]=\"selectedAgent.idKatios\">\r\n </lib-bubble-chat>\r\n </div>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";:host{display:block}.bubble-button{position:fixed;bottom:24px;right:24px;width:60px;height:60px;border-radius:50%;background:var(--primary-color);border:none;cursor:pointer;box-shadow:0 4px 20px #b0b4b9;display:flex;align-items:center;justify-content:center;transition:all .3s ease;z-index:1000}.bubble-button:hover{transform:scale(1.1);box-shadow:0 6px 25px #96999d}.bubble-button.is-open{transform:rotate(90deg)}.bubble-button i{font-size:1.5rem;color:#fff;transition:transform .3s ease}.chat-panel{position:fixed;bottom:100px;right:24px;width:380px;height:550px;background:var(--p-surface-0);border-radius:16px;box-shadow:0 10px 40px #00000026;display:flex;flex-direction:column;overflow:hidden;opacity:0;visibility:hidden;transform:translateY(20px) scale(.95);transition:all .3s ease;z-index:999}.chat-panel.is-open{opacity:1;visibility:visible;transform:translateY(0) scale(1)}.chat-header{background:var(--primary-color);color:#fff;display:flex;align-items:center;justify-content:space-between;flex-shrink:0}.header-text{display:flex;flex-direction:column}.header-title{margin:0}.header-subtitle{opacity:.9}.back-button,.close-button{background:#fff3;border:none;border-radius:50%;width:32px;height:32px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .2s ease}.back-button:hover,.close-button:hover{background:#ffffff4d}.back-button i,.close-button i{color:#fff;font-size:.875rem}.chat-content{flex:1;overflow:hidden;display:flex;flex-direction:column}.agents-list{overflow-y:auto;flex:1}.no-agents{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:2rem;text-align:center}.agents-subtitle{color:var(--p-text-muted-color);font-size:.875rem;margin:0 0 16px;text-align:center}.agent-card{display:flex;align-items:center;gap:12px;padding:12px;cursor:pointer;transition:all .2s ease;border-bottom:1px solid var(--p-surface-200)}.agent-card:hover{background:var(--p-surface-100)}.agent-card:last-child{margin-bottom:0}.agent-info{flex:1;min-width:0}.agent-name{margin:0 0 4px}.agent-description{margin:0;line-height:1.4;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;line-clamp:2;-webkit-line-clamp:2}.agent-arrow{color:var(--p-text-muted-color);font-size:.875rem;flex-shrink:0}@media (max-width: 768px){.chat-panel{width:100vw;height:100vh;height:-webkit-fill-available;height:100dvh;max-height:none;inset:0;border-radius:0;transform:translateY(100%)}.chat-panel.is-open{transform:translateY(0)}.bubble-button{bottom:16px;right:16px;width:56px;height:56px}.bubble-button.is-open{opacity:0;visibility:hidden;transform:scale(0)}.chat-content{flex:1;min-height:0;overflow:hidden}}\n"] }]
|
|
753
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
754
|
+
type: Inject,
|
|
755
|
+
args: [AI_AGENTS]
|
|
756
|
+
}] }, { type: i1$2.MessageService }], propDecorators: { idKatios: [{
|
|
757
|
+
type: Input
|
|
758
|
+
}] } });
|
|
759
|
+
|
|
480
760
|
/*
|
|
481
761
|
* Public API Surface of general-agent
|
|
482
762
|
*/
|
|
@@ -485,5 +765,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
485
765
|
* Generated bundle index. Do not edit.
|
|
486
766
|
*/
|
|
487
767
|
|
|
488
|
-
export { AI_AGENTS, GeneralAgentComponent };
|
|
768
|
+
export { AI_AGENTS, BubbleAgentComponent, GeneralAgentComponent };
|
|
489
769
|
//# sourceMappingURL=sf-aiembedded.mjs.map
|