@senior-gestao-relacionamento/angular-components 2.3.0 → 2.4.0

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.
@@ -0,0 +1,198 @@
1
+ import { Component, NgZone, ViewChild, inject } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { animate, style, transition, trigger } from '@angular/animations';
5
+ import { ButtonModule } from 'primeng/button';
6
+ import { Textarea } from 'primeng/textarea';
7
+ import { BadgeModule } from 'primeng/badge';
8
+ import { TranslatePipe, TranslationService, WebSocketService } from '@seniorsistemas/components-ai';
9
+ import { ChatbotService } from './services/chatbot.service';
10
+ import { ChatbotMarkdownPipe } from './pipes/markdown.pipe';
11
+ import * as i0 from "@angular/core";
12
+ import * as i1 from "@angular/common";
13
+ import * as i2 from "@angular/forms";
14
+ import * as i3 from "primeng/button";
15
+ /**
16
+ * Delay (ms) used before scrolling the chat container to the bottom.
17
+ * Gives Angular time to render the newly pushed message into the DOM.
18
+ */
19
+ const SCROLL_TO_BOTTOM_DELAY_MS = 50;
20
+ /**
21
+ * Interval (ms) between rotations of the "typing" status phrase.
22
+ */
23
+ const TYPING_PHRASE_ROTATION_MS = 2500;
24
+ /**
25
+ * Translation keys cycled while the assistant is preparing a response.
26
+ * Order matters: phrases are shown in this sequence.
27
+ */
28
+ const TYPING_PHRASE_KEYS = [
29
+ 'crmx.components.chatbot_typing_1',
30
+ 'crmx.components.chatbot_typing_2',
31
+ 'crmx.components.chatbot_typing_3',
32
+ 'crmx.components.chatbot_typing_4',
33
+ 'crmx.components.chatbot_typing_5'
34
+ ];
35
+ /**
36
+ * Chatbot global do CRM.
37
+ *
38
+ * Standalone, plug-and-play: basta declarar `<s-chatbot></s-chatbot>` no
39
+ * `app.component.html` da aplicação consumidora. Toda a integração
40
+ * (WebSocket de respostas, REST de envio, traduções e renderização de
41
+ * Markdown) está encapsulada na biblioteca.
42
+ */
43
+ export class ChatbotComponent {
44
+ chatMessagesContainer;
45
+ isOpen = false;
46
+ userInput = '';
47
+ sendingMessage = false;
48
+ chatMessages = [];
49
+ chatId = null;
50
+ unreadCount = 0;
51
+ typingPhraseKey = TYPING_PHRASE_KEYS[0];
52
+ wsSubscription = null;
53
+ typingPhraseTimer = null;
54
+ typingPhraseIndex = 0;
55
+ wsConfig = {
56
+ domain: 'crmx',
57
+ service: 'chatbot',
58
+ primitive: 'wsChatResponse',
59
+ userScoped: true
60
+ };
61
+ webSocketService = inject(WebSocketService);
62
+ ngZone = inject(NgZone);
63
+ chatbotService = inject(ChatbotService);
64
+ translationService = inject(TranslationService);
65
+ ngOnInit() {
66
+ this.subscribeToWebSocket();
67
+ }
68
+ ngOnDestroy() {
69
+ this.wsSubscription?.unsubscribe();
70
+ this.webSocketService.unsubscribe(this.wsConfig);
71
+ this.webSocketService.disconnect();
72
+ this.stopTypingPhraseRotation();
73
+ }
74
+ toggleChat() {
75
+ this.isOpen = !this.isOpen;
76
+ if (this.isOpen) {
77
+ this.unreadCount = 0;
78
+ setTimeout(() => this.scrollChatToBottom(), 100);
79
+ }
80
+ }
81
+ sendMessage() {
82
+ const text = this.userInput.trim();
83
+ if (!text || this.sendingMessage) {
84
+ return;
85
+ }
86
+ this.chatMessages.push({ role: 'user', content: text, timestamp: new Date() });
87
+ this.userInput = '';
88
+ this.sendingMessage = true;
89
+ this.startTypingPhraseRotation();
90
+ this.scrollChatToBottom();
91
+ this.chatbotService.chat(text, this.chatId ?? undefined).subscribe({
92
+ error: () => {
93
+ this.chatMessages.push({
94
+ role: 'assistant',
95
+ content: this.translationService.translate('crmx.components.chatbot_error'),
96
+ timestamp: new Date()
97
+ });
98
+ this.sendingMessage = false;
99
+ this.stopTypingPhraseRotation();
100
+ this.scrollChatToBottom();
101
+ }
102
+ });
103
+ }
104
+ onChatKeydown(event) {
105
+ if (event.key === 'Enter' && !event.shiftKey) {
106
+ event.preventDefault();
107
+ this.sendMessage();
108
+ }
109
+ }
110
+ subscribeToWebSocket() {
111
+ this.wsSubscription = this.webSocketService
112
+ .subscribe(this.wsConfig)
113
+ .subscribe(msg => {
114
+ const data = msg.body.data;
115
+ this.ngZone.run(() => {
116
+ if (!data) {
117
+ return;
118
+ }
119
+ if (data.chatId) {
120
+ this.chatId = data.chatId;
121
+ }
122
+ this.chatMessages.push({
123
+ role: 'assistant',
124
+ content: data.response,
125
+ timestamp: new Date(),
126
+ responseTime: data.responseTime
127
+ });
128
+ this.sendingMessage = false;
129
+ this.stopTypingPhraseRotation();
130
+ if (!this.isOpen) {
131
+ this.unreadCount++;
132
+ }
133
+ this.scrollChatToBottom();
134
+ });
135
+ });
136
+ }
137
+ scrollChatToBottom() {
138
+ setTimeout(() => {
139
+ const el = this.chatMessagesContainer?.nativeElement;
140
+ if (el) {
141
+ el.scrollTop = el.scrollHeight;
142
+ }
143
+ }, SCROLL_TO_BOTTOM_DELAY_MS);
144
+ }
145
+ /**
146
+ * Starts cycling through the typing phrases. Always begins from the first
147
+ * key so the user sees the same friendly opening every time.
148
+ */
149
+ startTypingPhraseRotation() {
150
+ this.stopTypingPhraseRotation();
151
+ this.typingPhraseIndex = 0;
152
+ this.typingPhraseKey = TYPING_PHRASE_KEYS[0];
153
+ this.typingPhraseTimer = setInterval(() => {
154
+ this.ngZone.run(() => {
155
+ this.typingPhraseIndex = (this.typingPhraseIndex + 1) % TYPING_PHRASE_KEYS.length;
156
+ this.typingPhraseKey = TYPING_PHRASE_KEYS[this.typingPhraseIndex];
157
+ });
158
+ }, TYPING_PHRASE_ROTATION_MS);
159
+ }
160
+ stopTypingPhraseRotation() {
161
+ if (this.typingPhraseTimer !== null) {
162
+ clearInterval(this.typingPhraseTimer);
163
+ this.typingPhraseTimer = null;
164
+ }
165
+ }
166
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
167
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ChatbotComponent, isStandalone: true, selector: "s-chatbot", viewQueries: [{ propertyName: "chatMessagesContainer", first: true, predicate: ["chatMessagesContainer"], descendants: true }], ngImport: i0, template: "<!-- Floating Action Button -->\n<button type=\"button\" class=\"chatbot-fab\" [class.open]=\"isOpen\"\n [attr.aria-label]=\"'crmx.components.chatbot_title' | translate\"\n [attr.aria-expanded]=\"isOpen\"\n (click)=\"toggleChat()\">\n <i *ngIf=\"!isOpen\" class=\"pi pi-lightbulb fab-icon\"></i>\n <i *ngIf=\"isOpen\" class=\"pi pi-times\"></i>\n <span class=\"unread-badge\" *ngIf=\"unreadCount > 0 && !isOpen\">{{ unreadCount }}</span>\n</button>\n\n<!-- Chat Window -->\n<div class=\"chatbot-window\" *ngIf=\"isOpen\">\n <div class=\"chatbot-header\">\n <i class=\"pi pi-lightbulb header-icon\"></i>\n <span>{{ 'crmx.components.chatbot_title' | translate }}</span>\n </div>\n\n <div class=\"chatbot-messages\" #chatMessagesContainer>\n <!-- Welcome -->\n <div class=\"chatbot-welcome\" *ngIf=\"chatMessages.length === 0\">\n <i class=\"pi pi-lightbulb welcome-icon\"></i>\n <p>{{ 'crmx.components.chatbot_welcome' | translate }}</p>\n </div>\n\n <!-- Messages -->\n <div *ngFor=\"let msg of chatMessages\" class=\"chatbot-message\"\n [ngClass]=\"{ 'user-message': msg.role === 'user', 'assistant-message': msg.role === 'assistant' }\">\n <div class=\"message-avatar\">\n <i *ngIf=\"msg.role === 'user'\" class=\"pi pi-user\"></i>\n <i *ngIf=\"msg.role === 'assistant'\" class=\"pi pi-lightbulb assistant-avatar-icon\"></i>\n </div>\n <div class=\"message-bubble\" [class.markdown-content]=\"msg.role === 'assistant'\">\n <p *ngIf=\"msg.role === 'user'\">{{ msg.content }}</p>\n <div *ngIf=\"msg.role === 'assistant'\" [innerHTML]=\"msg.content | sChatbotMarkdown\"></div>\n <div class=\"message-meta\">\n <span class=\"message-time\">{{ msg.timestamp | date:'HH:mm' }}</span>\n <span class=\"response-time\" *ngIf=\"msg.responseTime\">\u23F1 {{ msg.responseTime }}s</span>\n </div>\n </div>\n </div>\n\n <!-- Typing indicator -->\n <div class=\"chatbot-typing\" *ngIf=\"sendingMessage\">\n <div class=\"message-avatar\"><i class=\"pi pi-lightbulb assistant-avatar-icon\"></i></div>\n <div class=\"typing-bubble\" aria-live=\"polite\">\n <span class=\"typing-phrase\" [@chatbotPhraseFade]=\"typingPhraseKey\">{{ typingPhraseKey | translate }}</span>\n <span class=\"typing-pulse\" aria-hidden=\"true\">\n <span></span><span></span><span></span>\n </span>\n </div>\n </div>\n </div>\n\n <div class=\"chatbot-input-area\">\n <div class=\"input-row\">\n <label for=\"s-chatbot-input\" class=\"sr-only\">{{ 'crmx.components.chatbot_placeholder' | translate }}</label>\n <textarea pTextarea id=\"s-chatbot-input\" [(ngModel)]=\"userInput\"\n [placeholder]=\"'crmx.components.chatbot_placeholder' | translate\"\n [autoResize]=\"true\" [rows]=\"1\" [maxlength]=\"2000\"\n (keydown)=\"onChatKeydown($event)\" [disabled]=\"sendingMessage\"></textarea>\n <button pButton type=\"button\" class=\"p-button-rounded send-btn\"\n [attr.aria-label]=\"'crmx.components.chatbot_placeholder' | translate\"\n [disabled]=\"!userInput.trim() || sendingMessage\"\n (click)=\"sendMessage()\">\n <i class=\"pi pi-send\"></i>\n </button>\n </div>\n </div>\n</div>\n", styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.chatbot-fab{position:fixed;bottom:1.5rem;right:1.5rem;width:56px;height:56px;border:0;padding:0;border-radius:50%;background:var(--p-primary-color, #667eea);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 12px #00000040;z-index:1100;transition:transform .2s ease,background .2s ease;font:inherit}.chatbot-fab:hover{transform:scale(1.08)}.chatbot-fab:focus-visible{outline:2px solid var(--p-primary-color, #667eea);outline-offset:2px}.chatbot-fab.open{background:var(--p-surface-500, #6c757d)}.chatbot-fab .pi-times{font-size:1.25rem}.chatbot-fab .fab-icon{font-size:1.5rem}.chatbot-fab .unread-badge{position:absolute;top:-4px;right:-4px;min-width:20px;height:20px;border-radius:10px;background:#ef4444;color:#fff;font-size:.7rem;font-weight:700;display:flex;align-items:center;justify-content:center;padding:0 5px;box-shadow:0 2px 4px #0003}.chatbot-window{position:fixed;bottom:6.5rem;right:1.5rem;width:380px;height:520px;background:#fff;border-radius:12px;box-shadow:0 8px 32px #0000002e;z-index:1100;display:flex;flex-direction:column;overflow:hidden;animation:chatbot-slide-up .25s ease-out}.chatbot-header{display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem;background:var(--p-primary-color, #667eea);color:#fff;font-weight:600;font-size:.9rem;flex-shrink:0}.chatbot-header .header-icon{font-size:1.1rem}.chatbot-messages{flex:1;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:.75rem}.chatbot-welcome{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:#adb5bd;text-align:center;padding:2rem}.chatbot-welcome .welcome-icon{font-size:2.5rem;color:var(--p-primary-color, #667eea);opacity:.7}.chatbot-welcome p{font-size:.9rem;color:#6c757d;margin:.75rem 0 0;line-height:1.5}.chatbot-message{display:flex;gap:.5rem;max-width:90%;align-items:flex-end}.message-avatar{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0}.message-avatar .assistant-avatar-icon{font-size:1rem;color:var(--p-primary-color, #667eea)}.message-bubble{padding:.6rem .85rem;font-size:.85rem;line-height:1.4}.message-bubble p{margin:0;white-space:pre-wrap;word-break:break-word}.message-bubble .response-time{opacity:.7}.message-bubble .message-meta{display:flex;justify-content:flex-start;align-items:center;gap:.5rem;margin-top:.25rem;font-size:.7rem}.chatbot-message.assistant-message{align-self:flex-start}.chatbot-message.assistant-message .message-bubble{background:#f1f3f5;color:#2c3e50;border-radius:12px 12px 12px 0}.chatbot-message.assistant-message .message-bubble .message-time{color:#adb5bd}.chatbot-message.user-message{align-self:flex-end;flex-direction:row-reverse}.chatbot-message.user-message .message-avatar{background:var(--p-primary-color, #667eea);color:#fff}.chatbot-message.user-message .message-bubble{background:var(--p-primary-color, #667eea);color:#fff;border-radius:12px 12px 0}.chatbot-message.user-message .message-bubble .message-meta{justify-content:flex-end}.chatbot-message.user-message .message-bubble .message-time{color:#ffffffb3}.chatbot-typing{display:flex;gap:.5rem;align-self:flex-start;align-items:flex-end}.chatbot-typing .typing-bubble{display:flex;align-items:center;gap:.5rem;padding:.55rem .85rem;background:#f1f3f5;color:#495057;border-radius:12px 12px 12px 0;font-size:.85rem;line-height:1.3;max-width:240px}.chatbot-typing .typing-phrase{display:inline-block}.chatbot-typing .typing-pulse{display:inline-flex;gap:3px;flex-shrink:0}.chatbot-typing .typing-pulse span{width:4px;height:4px;border-radius:50%;background:#adb5bd;animation:chatbot-typing 1.4s infinite}.chatbot-typing .typing-pulse span:nth-child(2){animation-delay:.2s}.chatbot-typing .typing-pulse span:nth-child(3){animation-delay:.4s}.chatbot-input-area{display:flex;flex-direction:column;gap:.5rem;padding:.75rem;border-top:1px solid #e9ecef;background:#fff;flex-shrink:0}.chatbot-input-area .input-row{display:flex;align-items:flex-end;gap:.5rem}.chatbot-input-area textarea{flex:1;resize:none;border-radius:8px;font-size:.85rem;max-height:100px}.chatbot-input-area .send-btn{flex-shrink:0;width:36px;height:36px;align-self:center}:host ::ng-deep .message-bubble.markdown-content{overflow-x:auto}:host ::ng-deep .message-bubble.markdown-content>*{margin-top:0;margin-bottom:0}:host ::ng-deep .message-bubble.markdown-content>*+*{margin-top:.4rem}:host ::ng-deep .message-bubble.markdown-content a{color:var(--p-primary-color, #667eea);text-decoration:underline}:host ::ng-deep .message-bubble.markdown-content strong{font-weight:600}:host ::ng-deep .message-bubble.markdown-content hr{border:0;border-top:1px solid rgba(0,0,0,.12);margin:.5rem 0}:host ::ng-deep .message-bubble.markdown-content code{background:#00000014;padding:.1rem .3rem;border-radius:3px;font-size:.8rem;font-family:Courier New,monospace}:host ::ng-deep .message-bubble.markdown-content pre{background:#0000000f;padding:.5rem;border-radius:6px;overflow-x:auto;font-size:.8rem}:host ::ng-deep .message-bubble.markdown-content pre code{background:none;padding:0}:host ::ng-deep .message-bubble.markdown-content blockquote{border-left:3px solid rgba(0,0,0,.15);padding-left:.6rem;opacity:.85}:host ::ng-deep .message-bubble.markdown-content ul,:host ::ng-deep .message-bubble.markdown-content ol{padding-left:1.2rem}:host ::ng-deep .message-bubble.markdown-content li{margin-bottom:.15rem}:host ::ng-deep .message-bubble.markdown-content h1,:host ::ng-deep .message-bubble.markdown-content h2,:host ::ng-deep .message-bubble.markdown-content h3,:host ::ng-deep .message-bubble.markdown-content h4,:host ::ng-deep .message-bubble.markdown-content h5,:host ::ng-deep .message-bubble.markdown-content h6{font-weight:600;line-height:1.3}:host ::ng-deep .message-bubble.markdown-content h1{font-size:1.1rem}:host ::ng-deep .message-bubble.markdown-content h2{font-size:1rem}:host ::ng-deep .message-bubble.markdown-content h3{font-size:.95rem}:host ::ng-deep .message-bubble.markdown-content table{border-collapse:collapse;width:100%;font-size:.8rem;margin:.4rem 0}:host ::ng-deep .message-bubble.markdown-content table th,:host ::ng-deep .message-bubble.markdown-content table td{border:1px solid rgba(0,0,0,.2);padding:.3rem .5rem;text-align:left;vertical-align:top}:host ::ng-deep .message-bubble.markdown-content table th{font-weight:600;background:#0000000d}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table th,:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table td{border-color:#fff3}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table th{background:#ffffff14}:host ::ng-deep .message-bubble.markdown-content table tbody tr:nth-child(2n) td{background:#00000005}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table tbody tr:nth-child(2n) td{background:#ffffff08}@keyframes chatbot-slide-up{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes chatbot-typing{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-4px);opacity:1}}:host-context(.app-dark) .chatbot-window{background:var(--p-surface-100, #1e1e1e)}:host-context(.app-dark) .chatbot-messages{background:var(--p-surface-100, #1e1e1e)}:host-context(.app-dark) .chatbot-welcome p{color:var(--p-surface-500, #adb5bd)}:host-context(.app-dark) .chatbot-message.assistant-message .message-bubble{background:var(--p-surface-200, #2d2d2d);color:var(--p-surface-900, #e0e0e0)}:host-context(.app-dark) .chatbot-input-area{background:var(--p-surface-100, #1e1e1e);border-top-color:var(--p-surface-300, #3d3d3d)}:host-context(.app-dark) .chatbot-typing .typing-bubble{background:var(--p-surface-200, #2d2d2d);color:var(--p-surface-900, #e0e0e0)}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content code{background:#ffffff1a}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content pre{background:#ffffff14}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content blockquote{border-left-color:#fff3}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content hr{border-top-color:#ffffff26}@media (max-width: 480px){.chatbot-window{width:calc(100vw - 2rem);height:calc(100vh - 8rem);right:1rem;bottom:5rem}.chatbot-fab{bottom:1rem;right:1rem}}\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: "pipe", type: i1.DatePipe, name: "date" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i3.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "directive", type: Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: BadgeModule }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: ChatbotMarkdownPipe, name: "sChatbotMarkdown" }], animations: [
168
+ trigger('chatbotPhraseFade', [
169
+ transition('* => *', [
170
+ style({ opacity: 0, transform: 'translateY(2px)' }),
171
+ animate('220ms ease-out', style({ opacity: 1, transform: 'translateY(0)' }))
172
+ ])
173
+ ])
174
+ ] });
175
+ }
176
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotComponent, decorators: [{
177
+ type: Component,
178
+ args: [{ selector: 's-chatbot', standalone: true, imports: [
179
+ CommonModule,
180
+ FormsModule,
181
+ ButtonModule,
182
+ Textarea,
183
+ BadgeModule,
184
+ TranslatePipe,
185
+ ChatbotMarkdownPipe
186
+ ], animations: [
187
+ trigger('chatbotPhraseFade', [
188
+ transition('* => *', [
189
+ style({ opacity: 0, transform: 'translateY(2px)' }),
190
+ animate('220ms ease-out', style({ opacity: 1, transform: 'translateY(0)' }))
191
+ ])
192
+ ])
193
+ ], template: "<!-- Floating Action Button -->\n<button type=\"button\" class=\"chatbot-fab\" [class.open]=\"isOpen\"\n [attr.aria-label]=\"'crmx.components.chatbot_title' | translate\"\n [attr.aria-expanded]=\"isOpen\"\n (click)=\"toggleChat()\">\n <i *ngIf=\"!isOpen\" class=\"pi pi-lightbulb fab-icon\"></i>\n <i *ngIf=\"isOpen\" class=\"pi pi-times\"></i>\n <span class=\"unread-badge\" *ngIf=\"unreadCount > 0 && !isOpen\">{{ unreadCount }}</span>\n</button>\n\n<!-- Chat Window -->\n<div class=\"chatbot-window\" *ngIf=\"isOpen\">\n <div class=\"chatbot-header\">\n <i class=\"pi pi-lightbulb header-icon\"></i>\n <span>{{ 'crmx.components.chatbot_title' | translate }}</span>\n </div>\n\n <div class=\"chatbot-messages\" #chatMessagesContainer>\n <!-- Welcome -->\n <div class=\"chatbot-welcome\" *ngIf=\"chatMessages.length === 0\">\n <i class=\"pi pi-lightbulb welcome-icon\"></i>\n <p>{{ 'crmx.components.chatbot_welcome' | translate }}</p>\n </div>\n\n <!-- Messages -->\n <div *ngFor=\"let msg of chatMessages\" class=\"chatbot-message\"\n [ngClass]=\"{ 'user-message': msg.role === 'user', 'assistant-message': msg.role === 'assistant' }\">\n <div class=\"message-avatar\">\n <i *ngIf=\"msg.role === 'user'\" class=\"pi pi-user\"></i>\n <i *ngIf=\"msg.role === 'assistant'\" class=\"pi pi-lightbulb assistant-avatar-icon\"></i>\n </div>\n <div class=\"message-bubble\" [class.markdown-content]=\"msg.role === 'assistant'\">\n <p *ngIf=\"msg.role === 'user'\">{{ msg.content }}</p>\n <div *ngIf=\"msg.role === 'assistant'\" [innerHTML]=\"msg.content | sChatbotMarkdown\"></div>\n <div class=\"message-meta\">\n <span class=\"message-time\">{{ msg.timestamp | date:'HH:mm' }}</span>\n <span class=\"response-time\" *ngIf=\"msg.responseTime\">\u23F1 {{ msg.responseTime }}s</span>\n </div>\n </div>\n </div>\n\n <!-- Typing indicator -->\n <div class=\"chatbot-typing\" *ngIf=\"sendingMessage\">\n <div class=\"message-avatar\"><i class=\"pi pi-lightbulb assistant-avatar-icon\"></i></div>\n <div class=\"typing-bubble\" aria-live=\"polite\">\n <span class=\"typing-phrase\" [@chatbotPhraseFade]=\"typingPhraseKey\">{{ typingPhraseKey | translate }}</span>\n <span class=\"typing-pulse\" aria-hidden=\"true\">\n <span></span><span></span><span></span>\n </span>\n </div>\n </div>\n </div>\n\n <div class=\"chatbot-input-area\">\n <div class=\"input-row\">\n <label for=\"s-chatbot-input\" class=\"sr-only\">{{ 'crmx.components.chatbot_placeholder' | translate }}</label>\n <textarea pTextarea id=\"s-chatbot-input\" [(ngModel)]=\"userInput\"\n [placeholder]=\"'crmx.components.chatbot_placeholder' | translate\"\n [autoResize]=\"true\" [rows]=\"1\" [maxlength]=\"2000\"\n (keydown)=\"onChatKeydown($event)\" [disabled]=\"sendingMessage\"></textarea>\n <button pButton type=\"button\" class=\"p-button-rounded send-btn\"\n [attr.aria-label]=\"'crmx.components.chatbot_placeholder' | translate\"\n [disabled]=\"!userInput.trim() || sendingMessage\"\n (click)=\"sendMessage()\">\n <i class=\"pi pi-send\"></i>\n </button>\n </div>\n </div>\n</div>\n", styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.chatbot-fab{position:fixed;bottom:1.5rem;right:1.5rem;width:56px;height:56px;border:0;padding:0;border-radius:50%;background:var(--p-primary-color, #667eea);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 12px #00000040;z-index:1100;transition:transform .2s ease,background .2s ease;font:inherit}.chatbot-fab:hover{transform:scale(1.08)}.chatbot-fab:focus-visible{outline:2px solid var(--p-primary-color, #667eea);outline-offset:2px}.chatbot-fab.open{background:var(--p-surface-500, #6c757d)}.chatbot-fab .pi-times{font-size:1.25rem}.chatbot-fab .fab-icon{font-size:1.5rem}.chatbot-fab .unread-badge{position:absolute;top:-4px;right:-4px;min-width:20px;height:20px;border-radius:10px;background:#ef4444;color:#fff;font-size:.7rem;font-weight:700;display:flex;align-items:center;justify-content:center;padding:0 5px;box-shadow:0 2px 4px #0003}.chatbot-window{position:fixed;bottom:6.5rem;right:1.5rem;width:380px;height:520px;background:#fff;border-radius:12px;box-shadow:0 8px 32px #0000002e;z-index:1100;display:flex;flex-direction:column;overflow:hidden;animation:chatbot-slide-up .25s ease-out}.chatbot-header{display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem;background:var(--p-primary-color, #667eea);color:#fff;font-weight:600;font-size:.9rem;flex-shrink:0}.chatbot-header .header-icon{font-size:1.1rem}.chatbot-messages{flex:1;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:.75rem}.chatbot-welcome{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:#adb5bd;text-align:center;padding:2rem}.chatbot-welcome .welcome-icon{font-size:2.5rem;color:var(--p-primary-color, #667eea);opacity:.7}.chatbot-welcome p{font-size:.9rem;color:#6c757d;margin:.75rem 0 0;line-height:1.5}.chatbot-message{display:flex;gap:.5rem;max-width:90%;align-items:flex-end}.message-avatar{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0}.message-avatar .assistant-avatar-icon{font-size:1rem;color:var(--p-primary-color, #667eea)}.message-bubble{padding:.6rem .85rem;font-size:.85rem;line-height:1.4}.message-bubble p{margin:0;white-space:pre-wrap;word-break:break-word}.message-bubble .response-time{opacity:.7}.message-bubble .message-meta{display:flex;justify-content:flex-start;align-items:center;gap:.5rem;margin-top:.25rem;font-size:.7rem}.chatbot-message.assistant-message{align-self:flex-start}.chatbot-message.assistant-message .message-bubble{background:#f1f3f5;color:#2c3e50;border-radius:12px 12px 12px 0}.chatbot-message.assistant-message .message-bubble .message-time{color:#adb5bd}.chatbot-message.user-message{align-self:flex-end;flex-direction:row-reverse}.chatbot-message.user-message .message-avatar{background:var(--p-primary-color, #667eea);color:#fff}.chatbot-message.user-message .message-bubble{background:var(--p-primary-color, #667eea);color:#fff;border-radius:12px 12px 0}.chatbot-message.user-message .message-bubble .message-meta{justify-content:flex-end}.chatbot-message.user-message .message-bubble .message-time{color:#ffffffb3}.chatbot-typing{display:flex;gap:.5rem;align-self:flex-start;align-items:flex-end}.chatbot-typing .typing-bubble{display:flex;align-items:center;gap:.5rem;padding:.55rem .85rem;background:#f1f3f5;color:#495057;border-radius:12px 12px 12px 0;font-size:.85rem;line-height:1.3;max-width:240px}.chatbot-typing .typing-phrase{display:inline-block}.chatbot-typing .typing-pulse{display:inline-flex;gap:3px;flex-shrink:0}.chatbot-typing .typing-pulse span{width:4px;height:4px;border-radius:50%;background:#adb5bd;animation:chatbot-typing 1.4s infinite}.chatbot-typing .typing-pulse span:nth-child(2){animation-delay:.2s}.chatbot-typing .typing-pulse span:nth-child(3){animation-delay:.4s}.chatbot-input-area{display:flex;flex-direction:column;gap:.5rem;padding:.75rem;border-top:1px solid #e9ecef;background:#fff;flex-shrink:0}.chatbot-input-area .input-row{display:flex;align-items:flex-end;gap:.5rem}.chatbot-input-area textarea{flex:1;resize:none;border-radius:8px;font-size:.85rem;max-height:100px}.chatbot-input-area .send-btn{flex-shrink:0;width:36px;height:36px;align-self:center}:host ::ng-deep .message-bubble.markdown-content{overflow-x:auto}:host ::ng-deep .message-bubble.markdown-content>*{margin-top:0;margin-bottom:0}:host ::ng-deep .message-bubble.markdown-content>*+*{margin-top:.4rem}:host ::ng-deep .message-bubble.markdown-content a{color:var(--p-primary-color, #667eea);text-decoration:underline}:host ::ng-deep .message-bubble.markdown-content strong{font-weight:600}:host ::ng-deep .message-bubble.markdown-content hr{border:0;border-top:1px solid rgba(0,0,0,.12);margin:.5rem 0}:host ::ng-deep .message-bubble.markdown-content code{background:#00000014;padding:.1rem .3rem;border-radius:3px;font-size:.8rem;font-family:Courier New,monospace}:host ::ng-deep .message-bubble.markdown-content pre{background:#0000000f;padding:.5rem;border-radius:6px;overflow-x:auto;font-size:.8rem}:host ::ng-deep .message-bubble.markdown-content pre code{background:none;padding:0}:host ::ng-deep .message-bubble.markdown-content blockquote{border-left:3px solid rgba(0,0,0,.15);padding-left:.6rem;opacity:.85}:host ::ng-deep .message-bubble.markdown-content ul,:host ::ng-deep .message-bubble.markdown-content ol{padding-left:1.2rem}:host ::ng-deep .message-bubble.markdown-content li{margin-bottom:.15rem}:host ::ng-deep .message-bubble.markdown-content h1,:host ::ng-deep .message-bubble.markdown-content h2,:host ::ng-deep .message-bubble.markdown-content h3,:host ::ng-deep .message-bubble.markdown-content h4,:host ::ng-deep .message-bubble.markdown-content h5,:host ::ng-deep .message-bubble.markdown-content h6{font-weight:600;line-height:1.3}:host ::ng-deep .message-bubble.markdown-content h1{font-size:1.1rem}:host ::ng-deep .message-bubble.markdown-content h2{font-size:1rem}:host ::ng-deep .message-bubble.markdown-content h3{font-size:.95rem}:host ::ng-deep .message-bubble.markdown-content table{border-collapse:collapse;width:100%;font-size:.8rem;margin:.4rem 0}:host ::ng-deep .message-bubble.markdown-content table th,:host ::ng-deep .message-bubble.markdown-content table td{border:1px solid rgba(0,0,0,.2);padding:.3rem .5rem;text-align:left;vertical-align:top}:host ::ng-deep .message-bubble.markdown-content table th{font-weight:600;background:#0000000d}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table th,:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table td{border-color:#fff3}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table th{background:#ffffff14}:host ::ng-deep .message-bubble.markdown-content table tbody tr:nth-child(2n) td{background:#00000005}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table tbody tr:nth-child(2n) td{background:#ffffff08}@keyframes chatbot-slide-up{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes chatbot-typing{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-4px);opacity:1}}:host-context(.app-dark) .chatbot-window{background:var(--p-surface-100, #1e1e1e)}:host-context(.app-dark) .chatbot-messages{background:var(--p-surface-100, #1e1e1e)}:host-context(.app-dark) .chatbot-welcome p{color:var(--p-surface-500, #adb5bd)}:host-context(.app-dark) .chatbot-message.assistant-message .message-bubble{background:var(--p-surface-200, #2d2d2d);color:var(--p-surface-900, #e0e0e0)}:host-context(.app-dark) .chatbot-input-area{background:var(--p-surface-100, #1e1e1e);border-top-color:var(--p-surface-300, #3d3d3d)}:host-context(.app-dark) .chatbot-typing .typing-bubble{background:var(--p-surface-200, #2d2d2d);color:var(--p-surface-900, #e0e0e0)}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content code{background:#ffffff1a}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content pre{background:#ffffff14}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content blockquote{border-left-color:#fff3}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content hr{border-top-color:#ffffff26}@media (max-width: 480px){.chatbot-window{width:calc(100vw - 2rem);height:calc(100vh - 8rem);right:1rem;bottom:5rem}.chatbot-fab{bottom:1rem;right:1rem}}\n"] }]
194
+ }], propDecorators: { chatMessagesContainer: [{
195
+ type: ViewChild,
196
+ args: ['chatMessagesContainer']
197
+ }] } });
198
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhdGJvdC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9hbmd1bGFyLWNvbXBvbmVudHMvc3JjL2xpYi9jb21wb25lbnRzL2NoYXRib3QvY2hhdGJvdC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9hbmd1bGFyLWNvbXBvbmVudHMvc3JjL2xpYi9jb21wb25lbnRzL2NoYXRib3QvY2hhdGJvdC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFjLE1BQU0sRUFBcUIsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNwRyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdDLE9BQU8sRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxPQUFPLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUcxRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDOUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDNUMsT0FBTyxFQUFFLGFBQWEsRUFBRSxrQkFBa0IsRUFBbUIsZ0JBQWdCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUVySCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDNUQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sdUJBQXVCLENBQUM7Ozs7O0FBaUI1RDs7O0dBR0c7QUFDSCxNQUFNLHlCQUF5QixHQUFHLEVBQUUsQ0FBQztBQUVyQzs7R0FFRztBQUNILE1BQU0seUJBQXlCLEdBQUcsSUFBSSxDQUFDO0FBRXZDOzs7R0FH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QUFDLEVBQUUsT0FBTyxFQUFFLENBQUMsRUFBRSxTQUFTLEVBQUUsZUFBZSxFQUFFLENBQUMsQ0FBQzs2QkFDN0UsQ0FBQzt5QkFDSCxDQUFDO3FCQUNIOzhCQUtvRCxxQkFBcUI7c0JBQXpFLFNBQVM7dUJBQUMsdUJBQXVCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBFbGVtZW50UmVmLCBOZ1pvbmUsIE9uRGVzdHJveSwgT25Jbml0LCBWaWV3Q2hpbGQsIGluamVjdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7IEZvcm1zTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgYW5pbWF0ZSwgc3R5bGUsIHRyYW5zaXRpb24sIHRyaWdnZXIgfSBmcm9tICdAYW5ndWxhci9hbmltYXRpb25zJztcbmltcG9ydCB7IFN1YnNjcmlwdGlvbiB9IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQgeyBCdXR0b25Nb2R1bGUgfSBmcm9tICdwcmltZW5nL2J1dHRvbic7XG5pbXBvcnQgeyBUZXh0YXJlYSB9IGZyb20gJ3ByaW1lbmcvdGV4dGFyZWEnO1xuaW1wb3J0IHsgQmFkZ2VNb2R1bGUgfSBmcm9tICdwcmltZW5nL2JhZGdlJztcbmltcG9ydCB7IFRyYW5zbGF0ZVBpcGUsIFRyYW5zbGF0aW9uU2VydmljZSwgV2ViU29ja2V0Q29uZmlnLCBXZWJTb2NrZXRTZXJ2aWNlIH0gZnJvbSAnQHNlbmlvcnNpc3RlbWFzL2NvbXBvbmVudHMtYWknO1xuXG5pbXBvcnQgeyBDaGF0Ym90U2VydmljZSB9IGZyb20gJy4vc2VydmljZXMvY2hhdGJvdC5zZXJ2aWNlJztcbmltcG9ydCB7IENoYXRib3RNYXJrZG93blBpcGUgfSBmcm9tICcuL3BpcGVzL21hcmtkb3duLnBpcGUnO1xuXG5leHBvcnQgaW50ZXJmYWNlIENoYXRib3RNZXNzYWdlIHtcbiAgcm9sZTogJ3VzZXInIHwgJ2Fzc2lzdGFudCc7XG4gIGNvbnRlbnQ6IHN0cmluZztcbiAgdGltZXN0YW1wOiBEYXRlO1xuICByZXNwb25zZVRpbWU/OiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBDaGF0Ym90V3NQYXlsb2FkIHtcbiAgZGF0YToge1xuICAgIHJlc3BvbnNlOiBzdHJpbmc7XG4gICAgcmVzcG9uc2VUaW1lOiBudW1iZXI7XG4gICAgY2hhdElkOiBzdHJpbmc7XG4gIH07XG59XG5cbi8qKlxuICogRGVsYXkgKG1zKSB1c2VkIGJlZm9yZSBzY3JvbGxpbmcgdGhlIGNoYXQgY29udGFpbmVyIHRvIHRoZSBib3R0b20uXG4gKiBHaXZlcyBBbmd1bGFyIHRpbWUgdG8gcmVuZGVyIHRoZSBuZXdseSBwdXNoZWQgbWVzc2FnZSBpbnRvIHRoZSBET00uXG4gKi9cbmNvbnN0IFNDUk9MTF9UT19CT1RUT01fREVMQVlfTVMgPSA1MDtcblxuLyoqXG4gKiBJbnRlcnZhbCAobXMpIGJldHdlZW4gcm90YXRpb25zIG9mIHRoZSBcInR5cGluZ1wiIHN0YXR1cyBwaHJhc2UuXG4gKi9cbmNvbnN0IFRZUElOR19QSFJBU0VfUk9UQVRJT05fTVMgPSAyNTAwO1xuXG4vKipcbiAqIFRyYW5zbGF0aW9uIGtleXMgY3ljbGVkIHdoaWxlIHRoZSBhc3Npc3RhbnQgaXMgcHJlcGFyaW5nIGEgcmVzcG9uc2UuXG4gKiBPcmRlciBtYXR0ZXJzOiBwaHJhc2VzIGFyZSBzaG93biBpbiB0aGlzIHNlcXVlbmNlLlxuICovXG5jb25zdCBUWVBJTkdfUEhSQVNFX0tFWVM6IHJlYWRvbmx5IHN0cmluZ1tdID0gW1xuICAnY3JteC5jb21wb25lbnRzLmNoYXRib3RfdHlwaW5nXzEnLFxuICAnY3JteC5jb21wb25lbnRzLmNoYXRib3RfdHlwaW5nXzInLFxuICAnY3JteC5jb21wb25lbnRzLmNoYXRib3RfdHlwaW5nXzMnLFxuICAnY3JteC5jb21wb25lbnRzLmNoYXRib3RfdHlwaW5nXzQnLFxuICAnY3JteC5jb21wb25lbnRzLmNoYXRib3RfdHlwaW5nXzUnXG5dO1xuXG4vKipcbiAqIENoYXRib3QgZ2xvYmFsIGRvIENSTS5cbiAqXG4gKiBTdGFuZGFsb25lLCBwbHVnLWFuZC1wbGF5OiBiYXN0YSBkZWNsYXJhciBgPHMtY2hhdGJvdD48L3MtY2hhdGJvdD5gIG5vXG4gKiBgYXBwLmNvbXBvbmVudC5odG1sYCBkYSBhcGxpY2HDp8OjbyBjb25zdW1pZG9yYS4gVG9kYSBhIGludGVncmHDp8Ojb1xuICogKFdlYlNvY2tldCBkZSByZXNwb3N0YXMsIFJFU1QgZGUgZW52aW8sIHRyYWR1w6fDtWVzIGUgcmVuZGVyaXphw6fDo28gZGVcbiAqIE1hcmtkb3duKSBlc3TDoSBlbmNhcHN1bGFkYSBuYSBiaWJsaW90ZWNhLlxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdzLWNoYXRib3QnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgQ29tbW9uTW9kdWxlLFxuICAgIEZvcm1zTW9kdWxlLFxuICAgIEJ1dHRvbk1vZHVsZSxcbiAgICBUZXh0YXJlYSxcbiAgICBCYWRnZU1vZHVsZSxcbiAgICBUcmFuc2xhdGVQaXBlLFxuICAgIENoYXRib3RNYXJrZG93blBpcGVcbiAgXSxcbiAgYW5pbWF0aW9uczogW1xuICAgIHRyaWdnZXIoJ2NoYXRib3RQaHJhc2VGYWRlJywgW1xuICAgICAgdHJhbnNpdGlvbignKiA9PiAqJywgW1xuICAgICAgICBzdHlsZSh7IG9wYWNpdHk6IDAsIHRyYW5zZm9ybTogJ3RyYW5zbGF0ZVkoMnB4KScgfSksXG4gICAgICAgIGFuaW1hdGUoJzIyMG1zIGVhc2Utb3V0Jywgc3R5bGUoeyBvcGFjaXR5OiAxLCB0cmFuc2Zvcm06ICd0cmFuc2xhdGVZKDApJyB9KSlcbiAgICAgIF0pXG4gICAgXSlcbiAgXSxcbiAgdGVtcGxhdGVVcmw6ICcuL2NoYXRib3QuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybDogJy4vY2hhdGJvdC5jb21wb25lbnQuc2Nzcydcbn0pXG5leHBvcnQgY2xhc3MgQ2hhdGJvdENvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95IHtcbiAgQFZpZXdDaGlsZCgnY2hhdE1lc3NhZ2VzQ29udGFpbmVyJykgcHJpdmF0ZSByZWFkb25seSBjaGF0TWVzc2FnZXNDb250YWluZXIhOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PjtcblxuICBpc09wZW4gPSBmYWxzZTtcbiAgdXNlcklucHV0ID0gJyc7XG4gIHNlbmRpbmdNZXNzYWdlID0gZmFsc2U7XG4gIGNoYXRNZXNzYWdlczogQ2hhdGJvdE1lc3NhZ2VbXSA9IFtdO1xuICBjaGF0SWQ6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICB1bnJlYWRDb3VudCA9IDA7XG4gIHR5cGluZ1BocmFzZUtleTogc3RyaW5nID0gVFlQSU5HX1BIUkFTRV9LRVlTWzBdO1xuXG4gIHByaXZhdGUgd3NTdWJzY3JpcHRpb246IFN1YnNjcmlwdGlvbiB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHR5cGluZ1BocmFzZVRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRJbnRlcnZhbD4gfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSB0eXBpbmdQaHJhc2VJbmRleCA9IDA7XG4gIHByaXZhdGUgcmVhZG9ubHkgd3NDb25maWc6IFdlYlNvY2tldENvbmZpZyA9IHtcbiAgICBkb21haW46ICdjcm14JyxcbiAgICBzZXJ2aWNlOiAnY2hhdGJvdCcsXG4gICAgcHJpbWl0aXZlOiAnd3NDaGF0UmVzcG9uc2UnLFxuICAgIHVzZXJTY29wZWQ6IHRydWVcbiAgfTtcblxuICBwcml2YXRlIHJlYWRvbmx5IHdlYlNvY2tldFNlcnZpY2UgPSBpbmplY3QoV2ViU29ja2V0U2VydmljZSk7XG4gIHByaXZhdGUgcmVhZG9ubHkgbmdab25lID0gaW5qZWN0KE5nWm9uZSk7XG4gIHByaXZhdGUgcmVhZG9ubHkgY2hhdGJvdFNlcnZpY2UgPSBpbmplY3QoQ2hhdGJvdFNlcnZpY2UpO1xuICBwcml2YXRlIHJlYWRvbmx5IHRyYW5zbGF0aW9uU2VydmljZSA9IGluamVjdChUcmFuc2xhdGlvblNlcnZpY2UpO1xuXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIHRoaXMuc3Vic2NyaWJlVG9XZWJTb2NrZXQoKTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMud3NTdWJzY3JpcHRpb24/LnVuc3Vic2NyaWJlKCk7XG4gICAgdGhpcy53ZWJTb2NrZXRTZXJ2aWNlLnVuc3Vic2NyaWJlKHRoaXMud3NDb25maWcpO1xuICAgIHRoaXMud2ViU29ja2V0U2VydmljZS5kaXNjb25uZWN0KCk7XG4gICAgdGhpcy5zdG9wVHlwaW5nUGhyYXNlUm90YXRpb24oKTtcbiAgfVxuXG4gIHRvZ2dsZUNoYXQoKTogdm9pZCB7XG4gICAgdGhpcy5pc09wZW4gPSAhdGhpcy5pc09wZW47XG4gICAgaWYgKHRoaXMuaXNPcGVuKSB7XG4gICAgICB0aGlzLnVucmVhZENvdW50ID0gMDtcbiAgICAgIHNldFRpbWVvdXQoKCkgPT4gdGhpcy5zY3JvbGxDaGF0VG9Cb3R0b20oKSwgMTAwKTtcbiAgICB9XG4gIH1cblxuICBzZW5kTWVzc2FnZSgpOiB2b2lkIHtcbiAgICBjb25zdCB0ZXh0ID0gdGhpcy51c2VySW5wdXQudHJpbSgpO1xuICAgIGlmICghdGV4dCB8fCB0aGlzLnNlbmRpbmdNZXNzYWdlKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5jaGF0TWVzc2FnZXMucHVzaCh7IHJvbGU6ICd1c2VyJywgY29udGVudDogdGV4dCwgdGltZXN0YW1wOiBuZXcgRGF0ZSgpIH0pO1xuICAgIHRoaXMudXNlcklucHV0ID0gJyc7XG4gICAgdGhpcy5zZW5kaW5nTWVzc2FnZSA9IHRydWU7XG4gICAgdGhpcy5zdGFydFR5cGluZ1BocmFzZVJvdGF0aW9uKCk7XG4gICAgdGhpcy5zY3JvbGxDaGF0VG9Cb3R0b20oKTtcblxuICAgIHRoaXMuY2hhdGJvdFNlcnZpY2UuY2hhdCh0ZXh0LCB0aGlzLmNoYXRJZCA/PyB1bmRlZmluZWQpLnN1YnNjcmliZSh7XG4gICAgICBlcnJvcjogKCkgPT4ge1xuICAgICAgICB0aGlzLmNoYXRNZXNzYWdlcy5wdXNoKHtcbiAgICAgICAgICByb2xlOiAnYXNzaXN0YW50JyxcbiAgICAgICAgICBjb250ZW50OiB0aGlzLnRyYW5zbGF0aW9uU2VydmljZS50cmFuc2xhdGUoJ2NybXguY29tcG9uZW50cy5jaGF0Ym90X2Vycm9yJyksXG4gICAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpXG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLnNlbmRpbmdNZXNzYWdlID0gZmFsc2U7XG4gICAgICAgIHRoaXMuc3RvcFR5cGluZ1BocmFzZVJvdGF0aW9uKCk7XG4gICAgICAgIHRoaXMuc2Nyb2xsQ2hhdFRvQm90dG9tKCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBvbkNoYXRLZXlkb3duKGV2ZW50OiBLZXlib2FyZEV2ZW50KTogdm9pZCB7XG4gICAgaWYgKGV2ZW50LmtleSA9PT0gJ0VudGVyJyAmJiAhZXZlbnQuc2hpZnRLZXkpIHtcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICB0aGlzLnNlbmRNZXNzYWdlKCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzdWJzY3JpYmVUb1dlYlNvY2tldCgpOiB2b2lkIHtcbiAgICB0aGlzLndzU3Vic2NyaXB0aW9uID0gdGhpcy53ZWJTb2NrZXRTZXJ2aWNlXG4gICAgICAuc3Vic2NyaWJlPENoYXRib3RXc1BheWxvYWQ+KHRoaXMud3NDb25maWcpXG4gICAgICAuc3Vic2NyaWJlKG1zZyA9PiB7XG4gICAgICAgIGNvbnN0IGRhdGEgPSBtc2cuYm9keS5kYXRhO1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgICAgIGlmICghZGF0YSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmIChkYXRhLmNoYXRJZCkge1xuICAgICAgICAgICAgdGhpcy5jaGF0SWQgPSBkYXRhLmNoYXRJZDtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICB0aGlzLmNoYXRNZXNzYWdlcy5wdXNoKHtcbiAgICAgICAgICAgIHJvbGU6ICdhc3Npc3RhbnQnLFxuICAgICAgICAgICAgY29udGVudDogZGF0YS5yZXNwb25zZSxcbiAgICAgICAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKSxcbiAgICAgICAgICAgIHJlc3BvbnNlVGltZTogZGF0YS5yZXNwb25zZVRpbWVcbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIHRoaXMuc2VuZGluZ01lc3NhZ2UgPSBmYWxzZTtcbiAgICAgICAgICB0aGlzLnN0b3BUeXBpbmdQaHJhc2VSb3RhdGlvbigpO1xuXG4gICAgICAgICAgaWYgKCF0aGlzLmlzT3Blbikge1xuICAgICAgICAgICAgdGhpcy51bnJlYWRDb3VudCsrO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHRoaXMuc2Nyb2xsQ2hhdFRvQm90dG9tKCk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gIH1cblxuICBwcml2YXRlIHNjcm9sbENoYXRUb0JvdHRvbSgpOiB2b2lkIHtcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIGNvbnN0IGVsID0gdGhpcy5jaGF0TWVzc2FnZXNDb250YWluZXI/Lm5hdGl2ZUVsZW1lbnQ7XG4gICAgICBpZiAoZWwpIHtcbiAgICAgICAgZWwuc2Nyb2xsVG9wID0gZWwuc2Nyb2xsSGVpZ2h0O1xuICAgICAgfVxuICAgIH0sIFNDUk9MTF9UT19CT1RUT01fREVMQVlfTVMpO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0YXJ0cyBjeWNsaW5nIHRocm91Z2ggdGhlIHR5cGluZyBwaHJhc2VzLiBBbHdheXMgYmVnaW5zIGZyb20gdGhlIGZpcnN0XG4gICAqIGtleSBzbyB0aGUgdXNlciBzZWVzIHRoZSBzYW1lIGZyaWVuZGx5IG9wZW5pbmcgZXZlcnkgdGltZS5cbiAgICovXG4gIHByaXZhdGUgc3RhcnRUeXBpbmdQaHJhc2VSb3RhdGlvbigpOiB2b2lkIHtcbiAgICB0aGlzLnN0b3BUeXBpbmdQaHJhc2VSb3RhdGlvbigpO1xuICAgIHRoaXMudHlwaW5nUGhyYXNlSW5kZXggPSAwO1xuICAgIHRoaXMudHlwaW5nUGhyYXNlS2V5ID0gVFlQSU5HX1BIUkFTRV9LRVlTWzBdO1xuICAgIHRoaXMudHlwaW5nUGhyYXNlVGltZXIgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XG4gICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgICB0aGlzLnR5cGluZ1BocmFzZUluZGV4ID0gKHRoaXMudHlwaW5nUGhyYXNlSW5kZXggKyAxKSAlIFRZUElOR19QSFJBU0VfS0VZUy5sZW5ndGg7XG4gICAgICAgIHRoaXMudHlwaW5nUGhyYXNlS2V5ID0gVFlQSU5HX1BIUkFTRV9LRVlTW3RoaXMudHlwaW5nUGhyYXNlSW5kZXhdO1xuICAgICAgfSk7XG4gICAgfSwgVFlQSU5HX1BIUkFTRV9ST1RBVElPTl9NUyk7XG4gIH1cblxuICBwcml2YXRlIHN0b3BUeXBpbmdQaHJhc2VSb3RhdGlvbigpOiB2b2lkIHtcbiAgICBpZiAodGhpcy50eXBpbmdQaHJhc2VUaW1lciAhPT0gbnVsbCkge1xuICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLnR5cGluZ1BocmFzZVRpbWVyKTtcbiAgICAgIHRoaXMudHlwaW5nUGhyYXNlVGltZXIgPSBudWxsO1xuICAgIH1cbiAgfVxufVxuIiwiPCEtLSBGbG9hdGluZyBBY3Rpb24gQnV0dG9uIC0tPlxuPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJjaGF0Ym90LWZhYlwiIFtjbGFzcy5vcGVuXT1cImlzT3BlblwiXG4gICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiJ2NybXguY29tcG9uZW50cy5jaGF0Ym90X3RpdGxlJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgIFthdHRyLmFyaWEtZXhwYW5kZWRdPVwiaXNPcGVuXCJcbiAgICAgICAgKGNsaWNrKT1cInRvZ2dsZUNoYXQoKVwiPlxuICA8aSAqbmdJZj1cIiFpc09wZW5cIiBjbGFzcz1cInBpIHBpLWxpZ2h0YnVsYiBmYWItaWNvblwiPjwvaT5cbiAgPGkgKm5nSWY9XCJpc09wZW5cIiBjbGFzcz1cInBpIHBpLXRpbWVzXCI+PC9pPlxuICA8c3BhbiBjbGFzcz1cInVucmVhZC1iYWRnZVwiICpuZ0lmPVwidW5yZWFkQ291bnQgPiAwICYmICFpc09wZW5cIj57eyB1bnJlYWRDb3VudCB9fTwvc3Bhbj5cbjwvYnV0dG9uPlxuXG48IS0tIENoYXQgV2luZG93IC0tPlxuPGRpdiBjbGFzcz1cImNoYXRib3Qtd2luZG93XCIgKm5nSWY9XCJpc09wZW5cIj5cbiAgPGRpdiBjbGFzcz1cImNoYXRib3QtaGVhZGVyXCI+XG4gICAgPGkgY2xhc3M9XCJwaSBwaS1saWdodGJ1bGIgaGVhZGVyLWljb25cIj48L2k+XG4gICAgPHNwYW4+e3sgJ2NybXguY29tcG9uZW50cy5jaGF0Ym90X3RpdGxlJyB8IHRyYW5zbGF0ZSB9fTwvc3Bhbj5cbiAgPC9kaXY+XG5cbiAgPGRpdiBjbGFzcz1cImNoYXRib3QtbWVzc2FnZXNcIiAjY2hhdE1lc3NhZ2VzQ29udGFpbmVyPlxuICAgIDwhLS0gV2VsY29tZSAtLT5cbiAgICA8ZGl2IGNsYXNzPVwiY2hhdGJvdC13ZWxjb21lXCIgKm5nSWY9XCJjaGF0TWVzc2FnZXMubGVuZ3RoID09PSAwXCI+XG4gICAgICA8aSBjbGFzcz1cInBpIHBpLWxpZ2h0YnVsYiB3ZWxjb21lLWljb25cIj48L2k+XG4gICAgICA8cD57eyAnY3JteC5jb21wb25lbnRzLmNoYXRib3Rfd2VsY29tZScgfCB0cmFuc2xhdGUgfX08L3A+XG4gICAgPC9kaXY+XG5cbiAgICA8IS0tIE1lc3NhZ2VzIC0tPlxuICAgIDxkaXYgKm5nRm9yPVwibGV0IG1zZyBvZiBjaGF0TWVzc2FnZXNcIiBjbGFzcz1cImNoYXRib3QtbWVzc2FnZVwiXG4gICAgICAgICBbbmdDbGFzc109XCJ7ICd1c2VyLW1lc3NhZ2UnOiBtc2cucm9sZSA9PT0gJ3VzZXInLCAnYXNzaXN0YW50LW1lc3NhZ2UnOiBtc2cucm9sZSA9PT0gJ2Fzc2lzdGFudCcgfVwiPlxuICAgICAgPGRpdiBjbGFzcz1cIm1lc3NhZ2UtYXZhdGFyXCI+XG4gICAgICAgIDxpICpuZ0lmPVwibXNnLnJvbGUgPT09ICd1c2VyJ1wiIGNsYXNzPVwicGkgcGktdXNlclwiPjwvaT5cbiAgICAgICAgPGkgKm5nSWY9XCJtc2cucm9sZSA9PT0gJ2Fzc2lzdGFudCdcIiBjbGFzcz1cInBpIHBpLWxpZ2h0YnVsYiBhc3Npc3RhbnQtYXZhdGFyLWljb25cIj48L2k+XG4gICAgICA8L2Rpdj5cbiAgICAgIDxkaXYgY2xhc3M9XCJtZXNzYWdlLWJ1YmJsZVwiIFtjbGFzcy5tYXJrZG93bi1jb250ZW50XT1cIm1zZy5yb2xlID09PSAnYXNzaXN0YW50J1wiPlxuICAgICAgICA8cCAqbmdJZj1cIm1zZy5yb2xlID09PSAndXNlcidcIj57eyBtc2cuY29udGVudCB9fTwvcD5cbiAgICAgICAgPGRpdiAqbmdJZj1cIm1zZy5yb2xlID09PSAnYXNzaXN0YW50J1wiIFtpbm5lckhUTUxdPVwibXNnLmNvbnRlbnQgfCBzQ2hhdGJvdE1hcmtkb3duXCI+PC9kaXY+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJtZXNzYWdlLW1ldGFcIj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm1lc3NhZ2UtdGltZVwiPnt7IG1zZy50aW1lc3RhbXAgfCBkYXRlOidISDptbScgfX08L3NwYW4+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJyZXNwb25zZS10aW1lXCIgKm5nSWY9XCJtc2cucmVzcG9uc2VUaW1lXCI+4o+xIHt7IG1zZy5yZXNwb25zZVRpbWUgfX1zPC9zcGFuPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuXG4gICAgPCEtLSBUeXBpbmcgaW5kaWNhdG9yIC0tPlxuICAgIDxkaXYgY2xhc3M9XCJjaGF0Ym90LXR5cGluZ1wiICpuZ0lmPVwic2VuZGluZ01lc3NhZ2VcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJtZXNzYWdlLWF2YXRhclwiPjxpIGNsYXNzPVwicGkgcGktbGlnaHRidWxiIGFzc2lzdGFudC1hdmF0YXItaWNvblwiPjwvaT48L2Rpdj5cbiAgICAgIDxkaXYgY2xhc3M9XCJ0eXBpbmctYnViYmxlXCIgYXJpYS1saXZlPVwicG9saXRlXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwidHlwaW5nLXBocmFzZVwiIFtAY2hhdGJvdFBocmFzZUZhZGVdPVwidHlwaW5nUGhyYXNlS2V5XCI+e3sgdHlwaW5nUGhyYXNlS2V5IHwgdHJhbnNsYXRlIH19PC9zcGFuPlxuICAgICAgICA8c3BhbiBjbGFzcz1cInR5cGluZy1wdWxzZVwiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPlxuICAgICAgICAgIDxzcGFuPjwvc3Bhbj48c3Bhbj48L3NwYW4+PHNwYW4+PC9zcGFuPlxuICAgICAgICA8L3NwYW4+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG5cbiAgPGRpdiBjbGFzcz1cImNoYXRib3QtaW5wdXQtYXJlYVwiPlxuICAgIDxkaXYgY2xhc3M9XCJpbnB1dC1yb3dcIj5cbiAgICAgIDxsYWJlbCBmb3I9XCJzLWNoYXRib3QtaW5wdXRcIiBjbGFzcz1cInNyLW9ubHlcIj57eyAnY3JteC5jb21wb25lbnRzLmNoYXRib3RfcGxhY2Vob2xkZXInIHwgdHJhbnNsYXRlIH19PC9sYWJlbD5cbiAgICAgIDx0ZXh0YXJlYSBwVGV4dGFyZWEgaWQ9XCJzLWNoYXRib3QtaW5wdXRcIiBbKG5nTW9kZWwpXT1cInVzZXJJbnB1dFwiXG4gICAgICAgICAgICAgICAgW3BsYWNlaG9sZGVyXT1cIidjcm14LmNvbXBvbmVudHMuY2hhdGJvdF9wbGFjZWhvbGRlcicgfCB0cmFuc2xhdGVcIlxuICAgICAgICAgICAgICAgIFthdXRvUmVzaXplXT1cInRydWVcIiBbcm93c109XCIxXCIgW21heGxlbmd0aF09XCIyMDAwXCJcbiAgICAgICAgICAgICAgICAoa2V5ZG93bik9XCJvbkNoYXRLZXlkb3duKCRldmVudClcIiBbZGlzYWJsZWRdPVwic2VuZGluZ01lc3NhZ2VcIj48L3RleHRhcmVhPlxuICAgICAgPGJ1dHRvbiBwQnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cInAtYnV0dG9uLXJvdW5kZWQgc2VuZC1idG5cIlxuICAgICAgICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidjcm14LmNvbXBvbmVudHMuY2hhdGJvdF9wbGFjZWhvbGRlcicgfCB0cmFuc2xhdGVcIlxuICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiIXVzZXJJbnB1dC50cmltKCkgfHwgc2VuZGluZ01lc3NhZ2VcIlxuICAgICAgICAgICAgICAoY2xpY2spPVwic2VuZE1lc3NhZ2UoKVwiPlxuICAgICAgICA8aSBjbGFzcz1cInBpIHBpLXNlbmRcIj48L2k+XG4gICAgICA8L2J1dHRvbj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG48L2Rpdj5cbiJdfQ==
@@ -0,0 +1,48 @@
1
+ import { Pipe } from '@angular/core';
2
+ import DOMPurify from 'dompurify';
3
+ import { marked } from 'marked';
4
+ import * as i0 from "@angular/core";
5
+ import * as i1 from "@angular/platform-browser";
6
+ /**
7
+ * Pipe interno do chatbot para renderizar Markdown como HTML seguro.
8
+ *
9
+ * Fluxo de segurança:
10
+ * 1. `marked.parse` converte Markdown em HTML (sem JS executável por padrão).
11
+ * 2. `DOMPurify.sanitize` remove qualquer tag/atributo potencialmente
12
+ * malicioso (script, on*, javascript:..., etc.).
13
+ * 3. `bypassSecurityTrustHtml` libera o HTML já sanitizado para o
14
+ * `[innerHTML]` sem que o Angular faça uma segunda sanitização que
15
+ * removeria estilos e classes legítimas do markdown.
16
+ *
17
+ * Não exportado no public-api: uso restrito ao componente de chatbot.
18
+ */
19
+ export class ChatbotMarkdownPipe {
20
+ sanitizer;
21
+ constructor(sanitizer) {
22
+ this.sanitizer = sanitizer;
23
+ marked.setOptions({
24
+ breaks: true,
25
+ gfm: true
26
+ });
27
+ }
28
+ transform(value) {
29
+ if (!value) {
30
+ return '';
31
+ }
32
+ const rawHtml = marked.parse(value);
33
+ const cleanHtml = DOMPurify.sanitize(rawHtml);
34
+ // HTML sanitized via DOMPurify above; the bypass is required so the
35
+ // already-safe markdown styles aren't stripped by Angular's sanitizer.
36
+ return this.sanitizer.bypassSecurityTrustHtml(cleanHtml);
37
+ }
38
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotMarkdownPipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
39
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: ChatbotMarkdownPipe, isStandalone: true, name: "sChatbotMarkdown" });
40
+ }
41
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotMarkdownPipe, decorators: [{
42
+ type: Pipe,
43
+ args: [{
44
+ name: 'sChatbotMarkdown',
45
+ standalone: true
46
+ }]
47
+ }], ctorParameters: () => [{ type: i1.DomSanitizer }] });
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFya2Rvd24ucGlwZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2FuZ3VsYXItY29tcG9uZW50cy9zcmMvbGliL2NvbXBvbmVudHMvY2hhdGJvdC9waXBlcy9tYXJrZG93bi5waXBlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQWlCLE1BQU0sZUFBZSxDQUFDO0FBRXBELE9BQU8sU0FBUyxNQUFNLFdBQVcsQ0FBQztBQUNsQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sUUFBUSxDQUFDOzs7QUFFaEM7Ozs7Ozs7Ozs7OztHQVlHO0FBS0gsTUFBTSxPQUFPLG1CQUFtQjtJQUNEO0lBQTdCLFlBQTZCLFNBQXVCO1FBQXZCLGNBQVMsR0FBVCxTQUFTLENBQWM7UUFDbEQsTUFBTSxDQUFDLFVBQVUsQ0FBQztZQUNoQixNQUFNLEVBQUUsSUFBSTtZQUNaLEdBQUcsRUFBRSxJQUFJO1NBQ1YsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELFNBQVMsQ0FBQyxLQUFhO1FBQ3JCLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNYLE9BQU8sRUFBRSxDQUFDO1FBQ1osQ0FBQztRQUNELE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFXLENBQUM7UUFDOUMsTUFBTSxTQUFTLEdBQUcsU0FBUyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUM5QyxvRUFBb0U7UUFDcEUsdUVBQXVFO1FBQ3ZFLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyx1QkFBdUIsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUMzRCxDQUFDO3dHQWpCVSxtQkFBbUI7c0dBQW5CLG1CQUFtQjs7NEZBQW5CLG1CQUFtQjtrQkFKL0IsSUFBSTttQkFBQztvQkFDSixJQUFJLEVBQUUsa0JBQWtCO29CQUN4QixVQUFVLEVBQUUsSUFBSTtpQkFDakIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBQaXBlLCBQaXBlVHJhbnNmb3JtIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBEb21TYW5pdGl6ZXIsIFNhZmVIdG1sIH0gZnJvbSAnQGFuZ3VsYXIvcGxhdGZvcm0tYnJvd3Nlcic7XG5pbXBvcnQgRE9NUHVyaWZ5IGZyb20gJ2RvbXB1cmlmeSc7XG5pbXBvcnQgeyBtYXJrZWQgfSBmcm9tICdtYXJrZWQnO1xuXG4vKipcbiAqIFBpcGUgaW50ZXJubyBkbyBjaGF0Ym90IHBhcmEgcmVuZGVyaXphciBNYXJrZG93biBjb21vIEhUTUwgc2VndXJvLlxuICpcbiAqIEZsdXhvIGRlIHNlZ3VyYW7Dp2E6XG4gKiAgIDEuIGBtYXJrZWQucGFyc2VgIGNvbnZlcnRlIE1hcmtkb3duIGVtIEhUTUwgKHNlbSBKUyBleGVjdXTDoXZlbCBwb3IgcGFkcsOjbykuXG4gKiAgIDIuIGBET01QdXJpZnkuc2FuaXRpemVgIHJlbW92ZSBxdWFscXVlciB0YWcvYXRyaWJ1dG8gcG90ZW5jaWFsbWVudGVcbiAqICAgICAgbWFsaWNpb3NvIChzY3JpcHQsIG9uKiwgamF2YXNjcmlwdDouLi4sIGV0Yy4pLlxuICogICAzLiBgYnlwYXNzU2VjdXJpdHlUcnVzdEh0bWxgIGxpYmVyYSBvIEhUTUwgasOhIHNhbml0aXphZG8gcGFyYSBvXG4gKiAgICAgIGBbaW5uZXJIVE1MXWAgc2VtIHF1ZSBvIEFuZ3VsYXIgZmHDp2EgdW1hIHNlZ3VuZGEgc2FuaXRpemHDp8OjbyBxdWVcbiAqICAgICAgcmVtb3ZlcmlhIGVzdGlsb3MgZSBjbGFzc2VzIGxlZ8OtdGltYXMgZG8gbWFya2Rvd24uXG4gKlxuICogTsOjbyBleHBvcnRhZG8gbm8gcHVibGljLWFwaTogdXNvIHJlc3RyaXRvIGFvIGNvbXBvbmVudGUgZGUgY2hhdGJvdC5cbiAqL1xuQFBpcGUoe1xuICBuYW1lOiAnc0NoYXRib3RNYXJrZG93bicsXG4gIHN0YW5kYWxvbmU6IHRydWVcbn0pXG5leHBvcnQgY2xhc3MgQ2hhdGJvdE1hcmtkb3duUGlwZSBpbXBsZW1lbnRzIFBpcGVUcmFuc2Zvcm0ge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IHNhbml0aXplcjogRG9tU2FuaXRpemVyKSB7XG4gICAgbWFya2VkLnNldE9wdGlvbnMoe1xuICAgICAgYnJlYWtzOiB0cnVlLFxuICAgICAgZ2ZtOiB0cnVlXG4gICAgfSk7XG4gIH1cblxuICB0cmFuc2Zvcm0odmFsdWU6IHN0cmluZyk6IFNhZmVIdG1sIHtcbiAgICBpZiAoIXZhbHVlKSB7XG4gICAgICByZXR1cm4gJyc7XG4gICAgfVxuICAgIGNvbnN0IHJhd0h0bWwgPSBtYXJrZWQucGFyc2UodmFsdWUpIGFzIHN0cmluZztcbiAgICBjb25zdCBjbGVhbkh0bWwgPSBET01QdXJpZnkuc2FuaXRpemUocmF3SHRtbCk7XG4gICAgLy8gSFRNTCBzYW5pdGl6ZWQgdmlhIERPTVB1cmlmeSBhYm92ZTsgdGhlIGJ5cGFzcyBpcyByZXF1aXJlZCBzbyB0aGVcbiAgICAvLyBhbHJlYWR5LXNhZmUgbWFya2Rvd24gc3R5bGVzIGFyZW4ndCBzdHJpcHBlZCBieSBBbmd1bGFyJ3Mgc2FuaXRpemVyLlxuICAgIHJldHVybiB0aGlzLnNhbml0aXplci5ieXBhc3NTZWN1cml0eVRydXN0SHRtbChjbGVhbkh0bWwpO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,30 @@
1
+ import { Injectable } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ import * as i1 from "@angular/common/http";
4
+ /**
5
+ * Serviço de integração com o backend do chatbot.
6
+ *
7
+ * Endpoint fixo: `crmx/chatbot/signals/chat` (compartilhado por todos os
8
+ * frontends do CRM).
9
+ */
10
+ export class ChatbotService {
11
+ http;
12
+ signalsUrl = 'crmx/chatbot/signals';
13
+ constructor(http) {
14
+ this.http = http;
15
+ }
16
+ chat(message, chatId) {
17
+ const payload = { message };
18
+ if (chatId) {
19
+ payload.chatId = chatId;
20
+ }
21
+ return this.http.post(`${this.signalsUrl}/chat`, payload);
22
+ }
23
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
24
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotService, providedIn: 'root' });
25
+ }
26
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotService, decorators: [{
27
+ type: Injectable,
28
+ args: [{ providedIn: 'root' }]
29
+ }], ctorParameters: () => [{ type: i1.HttpClient }] });
30
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhdGJvdC5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvYW5ndWxhci1jb21wb25lbnRzL3NyYy9saWIvY29tcG9uZW50cy9jaGF0Ym90L3NlcnZpY2VzL2NoYXRib3Quc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDOzs7QUFJM0M7Ozs7O0dBS0c7QUFFSCxNQUFNLE9BQU8sY0FBYztJQUdJO0lBRlosVUFBVSxHQUFHLHNCQUFzQixDQUFDO0lBRXJELFlBQTZCLElBQWdCO1FBQWhCLFNBQUksR0FBSixJQUFJLENBQVk7SUFBRyxDQUFDO0lBRWpELElBQUksQ0FBQyxPQUFlLEVBQUUsTUFBZTtRQUNuQyxNQUFNLE9BQU8sR0FBeUMsRUFBRSxPQUFPLEVBQUUsQ0FBQztRQUNsRSxJQUFJLE1BQU0sRUFBRSxDQUFDO1lBQ1gsT0FBTyxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDMUIsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQU8sR0FBRyxJQUFJLENBQUMsVUFBVSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDbEUsQ0FBQzt3R0FYVSxjQUFjOzRHQUFkLGNBQWMsY0FERCxNQUFNOzs0RkFDbkIsY0FBYztrQkFEMUIsVUFBVTttQkFBQyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBIdHRwQ2xpZW50IH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSB9IGZyb20gJ3J4anMnO1xuXG4vKipcbiAqIFNlcnZpw6dvIGRlIGludGVncmHDp8OjbyBjb20gbyBiYWNrZW5kIGRvIGNoYXRib3QuXG4gKlxuICogRW5kcG9pbnQgZml4bzogYGNybXgvY2hhdGJvdC9zaWduYWxzL2NoYXRgIChjb21wYXJ0aWxoYWRvIHBvciB0b2RvcyBvc1xuICogZnJvbnRlbmRzIGRvIENSTSkuXG4gKi9cbkBJbmplY3RhYmxlKHsgcHJvdmlkZWRJbjogJ3Jvb3QnIH0pXG5leHBvcnQgY2xhc3MgQ2hhdGJvdFNlcnZpY2Uge1xuICBwcml2YXRlIHJlYWRvbmx5IHNpZ25hbHNVcmwgPSAnY3JteC9jaGF0Ym90L3NpZ25hbHMnO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcmVhZG9ubHkgaHR0cDogSHR0cENsaWVudCkge31cblxuICBjaGF0KG1lc3NhZ2U6IHN0cmluZywgY2hhdElkPzogc3RyaW5nKTogT2JzZXJ2YWJsZTx2b2lkPiB7XG4gICAgY29uc3QgcGF5bG9hZDogeyBtZXNzYWdlOiBzdHJpbmc7IGNoYXRJZD86IHN0cmluZyB9ID0geyBtZXNzYWdlIH07XG4gICAgaWYgKGNoYXRJZCkge1xuICAgICAgcGF5bG9hZC5jaGF0SWQgPSBjaGF0SWQ7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmh0dHAucG9zdDx2b2lkPihgJHt0aGlzLnNpZ25hbHNVcmx9L2NoYXRgLCBwYXlsb2FkKTtcbiAgfVxufVxuIl19
@@ -97,5 +97,14 @@
97
97
  "crmx.components.schedule_status": "Status",
98
98
  "crmx.components.schedule_confirmation_yes": "Confirmed",
99
99
  "crmx.components.schedule_confirmation_no": "Declined",
100
- "crmx.components.schedule_confirmation_pending": "Pending"
100
+ "crmx.components.schedule_confirmation_pending": "Pending",
101
+ "crmx.components.chatbot_title": "AI Assistant",
102
+ "crmx.components.chatbot_welcome": "Hello! I'm the CRMX virtual assistant. How can I help you?",
103
+ "crmx.components.chatbot_placeholder": "Type your message...",
104
+ "crmx.components.chatbot_error": "An error occurred while processing your message. Please try again.",
105
+ "crmx.components.chatbot_typing_1": "Searching...",
106
+ "crmx.components.chatbot_typing_2": "Analyzing your question...",
107
+ "crmx.components.chatbot_typing_3": "Gathering information...",
108
+ "crmx.components.chatbot_typing_4": "Almost there!",
109
+ "crmx.components.chatbot_typing_5": "Wrapping up the answer..."
101
110
  }
@@ -97,5 +97,14 @@
97
97
  "crmx.components.schedule_status": "Estado",
98
98
  "crmx.components.schedule_confirmation_yes": "Confirmado",
99
99
  "crmx.components.schedule_confirmation_no": "Rechazado",
100
- "crmx.components.schedule_confirmation_pending": "Pendiente"
100
+ "crmx.components.schedule_confirmation_pending": "Pendiente",
101
+ "crmx.components.chatbot_title": "Asistente IA",
102
+ "crmx.components.chatbot_welcome": "¡Hola! Soy el asistente virtual de CRMX. ¿Cómo puedo ayudarte?",
103
+ "crmx.components.chatbot_placeholder": "Escribe tu mensaje...",
104
+ "crmx.components.chatbot_error": "Ocurrió un error al procesar tu mensaje. Inténtalo de nuevo.",
105
+ "crmx.components.chatbot_typing_1": "Buscando...",
106
+ "crmx.components.chatbot_typing_2": "Analizando tu pregunta...",
107
+ "crmx.components.chatbot_typing_3": "Reuniendo información...",
108
+ "crmx.components.chatbot_typing_4": "¡Casi listo!",
109
+ "crmx.components.chatbot_typing_5": "Finalizando la respuesta..."
101
110
  }
@@ -97,5 +97,14 @@
97
97
  "crmx.components.schedule_status": "Status",
98
98
  "crmx.components.schedule_confirmation_yes": "Confirmado",
99
99
  "crmx.components.schedule_confirmation_no": "Recusado",
100
- "crmx.components.schedule_confirmation_pending": "Pendente"
100
+ "crmx.components.schedule_confirmation_pending": "Pendente",
101
+ "crmx.components.chatbot_title": "Assistente IA",
102
+ "crmx.components.chatbot_welcome": "Olá! Sou o assistente virtual do CRMX. Como posso ajudar você?",
103
+ "crmx.components.chatbot_placeholder": "Digite sua mensagem...",
104
+ "crmx.components.chatbot_error": "Ocorreu um erro ao processar sua mensagem. Tente novamente.",
105
+ "crmx.components.chatbot_typing_1": "Procurando...",
106
+ "crmx.components.chatbot_typing_2": "Analisando sua pergunta...",
107
+ "crmx.components.chatbot_typing_3": "Reunindo informações...",
108
+ "crmx.components.chatbot_typing_4": "Quase pronto!",
109
+ "crmx.components.chatbot_typing_5": "Finalizando a resposta..."
101
110
  }
@@ -7,10 +7,12 @@ export { StorageService } from './lib/services/storage/storage.service';
7
7
  export { CurrentCollaboratorService } from './lib/services/current-collaborator/current-collaborator.service';
8
8
  // Config
9
9
  export { provideAngularComponentsTranslations } from './lib/config/provide-angular-components-translations';
10
+ // Chatbot
11
+ export { ChatbotComponent } from './lib/components/chatbot/chatbot.component';
10
12
  // Schedule
11
13
  export { ScheduleFormComponent } from './lib/components/schedule/schedule-form/schedule-form.component';
12
14
  export { ScheduleDetailComponent } from './lib/components/schedule/schedule-detail/schedule-detail.component';
13
15
  export { ScheduleService } from './lib/components/schedule/services/schedule.service';
14
16
  export { ScheduleTypeService } from './lib/components/schedule/services/schedule-type.service';
15
17
  export { SchedulePriority, ScheduleRecurrence, WeekDay, Month, RepeatWhen, OrdinalWeekDay, ParticipantConfirmation, Status } from './lib/components/schedule/enums/schedule.enums';
16
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3Byb2plY3RzL2FuZ3VsYXItY29tcG9uZW50cy9zcmMvcHVibGljLWFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRTFFLFdBQVc7QUFDWCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDeEUsT0FBTyxFQUFFLDBCQUEwQixFQUFFLE1BQU0sa0VBQWtFLENBQUM7QUFFOUcsU0FBUztBQUNULE9BQU8sRUFBRSxvQ0FBb0MsRUFBRSxNQUFNLHNEQUFzRCxDQUFDO0FBWTVHLFdBQVc7QUFDWCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxpRUFBaUUsQ0FBQztBQUN4RyxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxxRUFBcUUsQ0FBQztBQUM5RyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0scURBQXFELENBQUM7QUFDdEYsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sMERBQTBELENBQUM7QUFJL0YsT0FBTyxFQUFFLGdCQUFnQixFQUFFLGtCQUFrQixFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLGNBQWMsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLEVBQUUsTUFBTSxnREFBZ0QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBQdWJsaWMgQVBJIFN1cmZhY2Ugb2YgQHNlbmlvci1nZXN0YW8tcmVsYWNpb25hbWVudG8vYW5ndWxhci1jb21wb25lbnRzXG4gKi9cblxuZXhwb3J0IHsgQW5ndWxhckNvbXBvbmVudHNNb2R1bGUgfSBmcm9tICcuL2xpYi9hbmd1bGFyLWNvbXBvbmVudHMubW9kdWxlJztcblxuLy8gU2VydmljZXNcbmV4cG9ydCB7IFN0b3JhZ2VTZXJ2aWNlIH0gZnJvbSAnLi9saWIvc2VydmljZXMvc3RvcmFnZS9zdG9yYWdlLnNlcnZpY2UnO1xuZXhwb3J0IHsgQ3VycmVudENvbGxhYm9yYXRvclNlcnZpY2UgfSBmcm9tICcuL2xpYi9zZXJ2aWNlcy9jdXJyZW50LWNvbGxhYm9yYXRvci9jdXJyZW50LWNvbGxhYm9yYXRvci5zZXJ2aWNlJztcblxuLy8gQ29uZmlnXG5leHBvcnQgeyBwcm92aWRlQW5ndWxhckNvbXBvbmVudHNUcmFuc2xhdGlvbnMgfSBmcm9tICcuL2xpYi9jb25maWcvcHJvdmlkZS1hbmd1bGFyLWNvbXBvbmVudHMtdHJhbnNsYXRpb25zJztcblxuLy8gTW9kZWxzXG5leHBvcnQgdHlwZSB7XG4gIENvbGxhYm9yYXRvcixcbiAgQ29sbGFib3JhdG9yTGVhZGVyLFxuICBDb2xsYWJvcmF0b3JCcmFuY2gsXG4gIEJyYW5jaCxcbiAgQ29tcGFueSxcbiAgSm9iVGl0bGVcbn0gZnJvbSAnLi9saWIvc2VydmljZXMvY3VycmVudC1jb2xsYWJvcmF0b3IvbW9kZWxzL2NvbGxhYm9yYXRvci5tb2RlbCc7XG5cbi8vIFNjaGVkdWxlXG5leHBvcnQgeyBTY2hlZHVsZUZvcm1Db21wb25lbnQgfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL3NjaGVkdWxlL3NjaGVkdWxlLWZvcm0vc2NoZWR1bGUtZm9ybS5jb21wb25lbnQnO1xuZXhwb3J0IHsgU2NoZWR1bGVEZXRhaWxDb21wb25lbnQgfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL3NjaGVkdWxlL3NjaGVkdWxlLWRldGFpbC9zY2hlZHVsZS1kZXRhaWwuY29tcG9uZW50JztcbmV4cG9ydCB7IFNjaGVkdWxlU2VydmljZSB9IGZyb20gJy4vbGliL2NvbXBvbmVudHMvc2NoZWR1bGUvc2VydmljZXMvc2NoZWR1bGUuc2VydmljZSc7XG5leHBvcnQgeyBTY2hlZHVsZVR5cGVTZXJ2aWNlIH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy9zY2hlZHVsZS9zZXJ2aWNlcy9zY2hlZHVsZS10eXBlLnNlcnZpY2UnO1xuZXhwb3J0IHR5cGUgeyBTY2hlZHVsZSwgU2NoZWR1bGVSZWN1cnJlbmNlSXRlbSwgUmVjdXJyZW5jZURhdGVzUmVjb3JkLCBTY2hlZHVsZVBhcnRpY2lwYW50IH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy9zY2hlZHVsZS9lbnRpdGllcy9zY2hlZHVsZS5pbnRlcmZhY2UnO1xuZXhwb3J0IHR5cGUgeyBTY2hlZHVsZVR5cGUgfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL3NjaGVkdWxlL2VudGl0aWVzL3NjaGVkdWxlLXR5cGUuaW50ZXJmYWNlJztcbmV4cG9ydCB0eXBlIHsgUGFydGljaXBhbnRMb29rdXAsIFBhcnRpY2lwYW50T3JpZ2luLCBTY2hlZHVsZVN1bW1hcnlCeURhdGUgfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL3NjaGVkdWxlL2VudGl0aWVzL3BhcnRpY2lwYW50LWxvb2t1cC5pbnRlcmZhY2UnO1xuZXhwb3J0IHsgU2NoZWR1bGVQcmlvcml0eSwgU2NoZWR1bGVSZWN1cnJlbmNlLCBXZWVrRGF5LCBNb250aCwgUmVwZWF0V2hlbiwgT3JkaW5hbFdlZWtEYXksIFBhcnRpY2lwYW50Q29uZmlybWF0aW9uLCBTdGF0dXMgfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL3NjaGVkdWxlL2VudW1zL3NjaGVkdWxlLmVudW1zJztcbiJdfQ==
18
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3Byb2plY3RzL2FuZ3VsYXItY29tcG9uZW50cy9zcmMvcHVibGljLWFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRTFFLFdBQVc7QUFDWCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDeEUsT0FBTyxFQUFFLDBCQUEwQixFQUFFLE1BQU0sa0VBQWtFLENBQUM7QUFFOUcsU0FBUztBQUNULE9BQU8sRUFBRSxvQ0FBb0MsRUFBRSxNQUFNLHNEQUFzRCxDQUFDO0FBWTVHLFVBQVU7QUFDVixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUc5RSxXQUFXO0FBQ1gsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0saUVBQWlFLENBQUM7QUFDeEcsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0scUVBQXFFLENBQUM7QUFDOUcsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHFEQUFxRCxDQUFDO0FBQ3RGLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDBEQUEwRCxDQUFDO0FBSS9GLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxrQkFBa0IsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxjQUFjLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxFQUFFLE1BQU0sZ0RBQWdELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogUHVibGljIEFQSSBTdXJmYWNlIG9mIEBzZW5pb3ItZ2VzdGFvLXJlbGFjaW9uYW1lbnRvL2FuZ3VsYXItY29tcG9uZW50c1xuICovXG5cbmV4cG9ydCB7IEFuZ3VsYXJDb21wb25lbnRzTW9kdWxlIH0gZnJvbSAnLi9saWIvYW5ndWxhci1jb21wb25lbnRzLm1vZHVsZSc7XG5cbi8vIFNlcnZpY2VzXG5leHBvcnQgeyBTdG9yYWdlU2VydmljZSB9IGZyb20gJy4vbGliL3NlcnZpY2VzL3N0b3JhZ2Uvc3RvcmFnZS5zZXJ2aWNlJztcbmV4cG9ydCB7IEN1cnJlbnRDb2xsYWJvcmF0b3JTZXJ2aWNlIH0gZnJvbSAnLi9saWIvc2VydmljZXMvY3VycmVudC1jb2xsYWJvcmF0b3IvY3VycmVudC1jb2xsYWJvcmF0b3Iuc2VydmljZSc7XG5cbi8vIENvbmZpZ1xuZXhwb3J0IHsgcHJvdmlkZUFuZ3VsYXJDb21wb25lbnRzVHJhbnNsYXRpb25zIH0gZnJvbSAnLi9saWIvY29uZmlnL3Byb3ZpZGUtYW5ndWxhci1jb21wb25lbnRzLXRyYW5zbGF0aW9ucyc7XG5cbi8vIE1vZGVsc1xuZXhwb3J0IHR5cGUge1xuICBDb2xsYWJvcmF0b3IsXG4gIENvbGxhYm9yYXRvckxlYWRlcixcbiAgQ29sbGFib3JhdG9yQnJhbmNoLFxuICBCcmFuY2gsXG4gIENvbXBhbnksXG4gIEpvYlRpdGxlXG59IGZyb20gJy4vbGliL3NlcnZpY2VzL2N1cnJlbnQtY29sbGFib3JhdG9yL21vZGVscy9jb2xsYWJvcmF0b3IubW9kZWwnO1xuXG4vLyBDaGF0Ym90XG5leHBvcnQgeyBDaGF0Ym90Q29tcG9uZW50IH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy9jaGF0Ym90L2NoYXRib3QuY29tcG9uZW50JztcbmV4cG9ydCB0eXBlIHsgQ2hhdGJvdE1lc3NhZ2UgfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL2NoYXRib3QvY2hhdGJvdC5jb21wb25lbnQnO1xuXG4vLyBTY2hlZHVsZVxuZXhwb3J0IHsgU2NoZWR1bGVGb3JtQ29tcG9uZW50IH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy9zY2hlZHVsZS9zY2hlZHVsZS1mb3JtL3NjaGVkdWxlLWZvcm0uY29tcG9uZW50JztcbmV4cG9ydCB7IFNjaGVkdWxlRGV0YWlsQ29tcG9uZW50IH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy9zY2hlZHVsZS9zY2hlZHVsZS1kZXRhaWwvc2NoZWR1bGUtZGV0YWlsLmNvbXBvbmVudCc7XG5leHBvcnQgeyBTY2hlZHVsZVNlcnZpY2UgfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL3NjaGVkdWxlL3NlcnZpY2VzL3NjaGVkdWxlLnNlcnZpY2UnO1xuZXhwb3J0IHsgU2NoZWR1bGVUeXBlU2VydmljZSB9IGZyb20gJy4vbGliL2NvbXBvbmVudHMvc2NoZWR1bGUvc2VydmljZXMvc2NoZWR1bGUtdHlwZS5zZXJ2aWNlJztcbmV4cG9ydCB0eXBlIHsgU2NoZWR1bGUsIFNjaGVkdWxlUmVjdXJyZW5jZUl0ZW0sIFJlY3VycmVuY2VEYXRlc1JlY29yZCwgU2NoZWR1bGVQYXJ0aWNpcGFudCB9IGZyb20gJy4vbGliL2NvbXBvbmVudHMvc2NoZWR1bGUvZW50aXRpZXMvc2NoZWR1bGUuaW50ZXJmYWNlJztcbmV4cG9ydCB0eXBlIHsgU2NoZWR1bGVUeXBlIH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy9zY2hlZHVsZS9lbnRpdGllcy9zY2hlZHVsZS10eXBlLmludGVyZmFjZSc7XG5leHBvcnQgdHlwZSB7IFBhcnRpY2lwYW50TG9va3VwLCBQYXJ0aWNpcGFudE9yaWdpbiwgU2NoZWR1bGVTdW1tYXJ5QnlEYXRlIH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy9zY2hlZHVsZS9lbnRpdGllcy9wYXJ0aWNpcGFudC1sb29rdXAuaW50ZXJmYWNlJztcbmV4cG9ydCB7IFNjaGVkdWxlUHJpb3JpdHksIFNjaGVkdWxlUmVjdXJyZW5jZSwgV2Vla0RheSwgTW9udGgsIFJlcGVhdFdoZW4sIE9yZGluYWxXZWVrRGF5LCBQYXJ0aWNpcGFudENvbmZpcm1hdGlvbiwgU3RhdHVzIH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy9zY2hlZHVsZS9lbnVtcy9zY2hlZHVsZS5lbnVtcyc7XG4iXX0=