@aakash58/chatbot 1.1.14 → 1.1.16
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/aakash58-chatbot.mjs +88 -51
- package/fesm2022/aakash58-chatbot.mjs.map +1 -1
- package/index.d.ts +6 -10
- package/package.json +1 -1
|
@@ -164,11 +164,12 @@ const DOOHBOT_ADVANCED_CONFIG = new InjectionToken('DOOHBOT_ADVANCED_CONFIG', {
|
|
|
164
164
|
class DoohbotInput {
|
|
165
165
|
username = '';
|
|
166
166
|
password = '';
|
|
167
|
+
rememberMe = false;
|
|
167
168
|
authToken;
|
|
168
169
|
license = '';
|
|
169
170
|
licenseCode = '';
|
|
170
171
|
licenseKey = '';
|
|
171
|
-
|
|
172
|
+
licenseFilePath = '';
|
|
172
173
|
apiBaseUrl = '';
|
|
173
174
|
apiSegment = '';
|
|
174
175
|
tenancyName = '';
|
|
@@ -209,8 +210,11 @@ class CoreConfigService {
|
|
|
209
210
|
get licenseKey() {
|
|
210
211
|
return this.configSignal().licenseKey || '';
|
|
211
212
|
}
|
|
212
|
-
get
|
|
213
|
-
return this.configSignal().
|
|
213
|
+
get licenseFilePath() {
|
|
214
|
+
return this.configSignal().licenseFilePath || '';
|
|
215
|
+
}
|
|
216
|
+
get rememberMe() {
|
|
217
|
+
return this.configSignal().rememberMe !== undefined ? this.configSignal().rememberMe : false;
|
|
214
218
|
}
|
|
215
219
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: CoreConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
216
220
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: CoreConfigService, providedIn: 'root' });
|
|
@@ -617,7 +621,7 @@ class AuthService {
|
|
|
617
621
|
}
|
|
618
622
|
// If still no token, exit gracefully
|
|
619
623
|
if (!credentials.accessToken) {
|
|
620
|
-
Logger.log('[Auth] No federated token found in storage.
|
|
624
|
+
Logger.log('[Auth] No federated token found in storage.');
|
|
621
625
|
return false;
|
|
622
626
|
}
|
|
623
627
|
this.isLoggingIn.set(true);
|
|
@@ -1434,6 +1438,7 @@ class MessageListComponent {
|
|
|
1434
1438
|
userAvatarUrl = '';
|
|
1435
1439
|
userName = '';
|
|
1436
1440
|
isAuthenticated = false;
|
|
1441
|
+
isLoggingIn = false;
|
|
1437
1442
|
trackByMessageId = (index, item) => item.id ?? index;
|
|
1438
1443
|
suggestionClick = new EventEmitter();
|
|
1439
1444
|
loginClick = new EventEmitter();
|
|
@@ -1489,11 +1494,11 @@ class MessageListComponent {
|
|
|
1489
1494
|
}
|
|
1490
1495
|
}
|
|
1491
1496
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MessageListComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
1492
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: MessageListComponent, isStandalone: true, selector: "app-message-list", inputs: { messages: "messages", isBotTyping: "isBotTyping", isStreaming: "isStreaming", appLogoUrl: "appLogoUrl", appSubtitle: "appSubtitle", welcomeDesc: "welcomeDesc", predefinedMessages: "predefinedMessages", botAvatarUrl: "botAvatarUrl", userAvatarUrl: "userAvatarUrl", userName: "userName", isAuthenticated: "isAuthenticated", trackByMessageId: "trackByMessageId" }, outputs: { suggestionClick: "suggestionClick", loginClick: "loginClick" }, viewQueries: [{ propertyName: "chatMessagesContainer", first: true, predicate: ["chatMessages"], descendants: true }], ngImport: i0, template: "<div #chatMessages class=\"chat-messages\">\r\n <!-- Welcome Screen -->\r\n @if (messages.length === 0 && !isBotTyping) {\r\n <div class=\"welcome-screen\">\r\n <img [src]=\"appLogoUrl\" alt=\"Welcome\" class=\"welcome-image\" style=\"border-radius: 0px\" />\r\n\r\n <!-- Only show greeting if authenticated -->\r\n @if (userName && isAuthenticated) {\r\n <div class=\"marquee-container\">\r\n <h2 class=\"welcome-greeting marquee-text\">\r\n Hi, {{ userName }}!\r\n <span class=\"emoji\">{{ appEmoji.waveHand }}</span>\r\n </h2>\r\n </div>\r\n }\r\n\r\n <h3>{{ appSubtitle }}</h3>\r\n <p>{{ welcomeDesc }}</p>\r\n\r\n <!-- Predefined messages chips: Only show when authenticated -->\r\n @if (isAuthenticated) {\r\n <app-chips [messages]=\"predefinedMessages\" [disabled]=\"isBotTyping\"\r\n (chipClick)=\"onPredefinedClick($event)\"></app-chips>\r\n }\r\n\r\n @if (!isAuthenticated) {\r\n <div class=\"auth-buttons-container\" style=\"margin-top: 20px\">\r\n <button (click)=\"loginClick.emit()\" class=\"auth-btn primary\">Login</button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Chat Messages -->\r\n <div *ngFor=\"let message of messages; trackBy: trackByMessageId\" class=\"message\"\r\n [class.user]=\"message.sender === 'user'\" [class.assistant]=\"message.sender === 'assistant'\"\r\n [class.streaming]=\"!message.completed && message.sender === 'assistant'\">\r\n <!-- Avatar (Assistant on Left) -->\r\n @if (message.sender === 'assistant') {\r\n <div class=\"avatar-container\">\r\n <img class=\"assistant-avatar\" [src]=\"botAvatarUrl\" alt=\"Bot Avatar\" />\r\n </div>\r\n }\r\n\r\n <!-- Message Bubble -->\r\n <div class=\"message-content\">\r\n <!-- User chat bubble -->\r\n @if (message.sender === 'user') {\r\n <div class=\"user\">\r\n <span>{{ message.text }}</span>\r\n <button class=\"copy-btn\" (click)=\"copyMessage(message)\" title=\"Copy message\">\r\n <mat-icon>{{ message.copied ? 'check' : 'content_copy' }}</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Assistant chat bubble -->\r\n @if (message.sender === 'assistant') {\r\n <div class=\"assistant\">\r\n @if (isStreaming && !message.isHistory) {\r\n <!-- Show text/chunks if they exist -->\r\n @if (message.chunks && message.chunks.length > 0) {\r\n @for (chunk of message.chunks; track $index; let i = $index) {\r\n <span class=\"markdown-content\" [attr.data-index]=\"i\" [innerHTML]=\"chunk | Markdown\">\r\n </span>\r\n }\r\n } @else if (!message.completed) {\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n }\r\n } @else {\r\n <!-- Non-streaming or history -->\r\n @if (message.text) {\r\n <span class=\"markdown-content\" [innerHTML]=\"message.text | Markdown\"></span>\r\n } @else if (!message.completed) {\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n }\r\n }\r\n @if (message.completed) {\r\n <button class=\"copy-btn\" (click)=\"copyMessage(message)\" title=\"Copy message\">\r\n <mat-icon>{{ message.copied ? 'check' : 'content_copy' }}</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Show time below message -->\r\n @if (message.sender === 'assistant' && message.completed) {\r\n <div class=\"message-time\">\r\n {{ message.timestamp | date: 'shortTime' }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Avatar (User on Right) -->\r\n @if (message.sender === 'user') {\r\n <div class=\"avatar-container\">\r\n <img class=\"user-avatar\" [src]=\"userAvatarUrl\" alt=\"User Avatar\" />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Standalone Typing Indicator (Fallback if no message added yet) -->\r\n @if (isBotTyping && messages[messages.length - 1]?.sender !== 'assistant') {\r\n <div class=\"message assistant\">\r\n <div class=\"avatar-container\">\r\n <img class=\"assistant-avatar\" [src]=\"botAvatarUrl\" alt=\"Bot Avatar\" />\r\n </div>\r\n <div class=\"message-content\">\r\n <div class=\"assistant\">\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>", styles: [":host{display:flex;flex-direction:column;flex-grow:1;overflow:hidden}.login-btn{padding:10px 15px;border:none;border-radius:20px;background-color:var(--primary-color);color:var(--user-text-color);font-size:1em;cursor:pointer;transition:background-color .2s ease}.chat-messages{flex-grow:1;padding:20px;overflow-y:auto;display:flex;flex-direction:column;gap:10px;scroll-behavior:smooth}.chat-messages::-webkit-scrollbar{width:6px}.chat-messages::-webkit-scrollbar-track{background:transparent}.chat-messages::-webkit-scrollbar-thumb{background:rgba(var(--black-rgb),.1);border-radius:10px}.chat-messages::-webkit-scrollbar-thumb:hover{background:rgba(var(--black-rgb),.1)}.chat-messages .message{display:flex;align-items:flex-end;gap:8px;margin-bottom:6px}.chat-messages .message.user{justify-content:flex-end}.chat-messages .message.assistant{justify-content:flex-start}.chat-messages .message .avatar-container{display:flex;flex-direction:column;align-items:center;gap:4px}.chat-messages .message .avatar-container .user-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--border-color)}.chat-messages .message .avatar-container .assistant-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--border-color);filter:var(--avatar-filter)}.chat-messages .message .avatar-container .user-avatar.placeholder{filter:var(--avatar-filter)}.chat-messages .message .avatar-container .user-name{font-size:10px;color:var(--secondary-text-color);font-family:var(--font-family);text-align:center;max-width:60px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chat-messages .message .message-content{max-width:80%;word-wrap:break-word;white-space:pre-wrap;font-family:var(--font-family);-webkit-user-select:text;user-select:text;cursor:text}.chat-messages .message .message-content .user{background-color:var(--primary-color);color:var(--user-text-color);align-self:flex-end;border-radius:20px;padding:10px 15px;position:relative;-webkit-user-select:text;user-select:text;cursor:text}.chat-messages .message .message-content .user .copy-btn{position:absolute;top:8px;right:8px;opacity:0;transition:opacity .2s ease;background:var(--background-color);border:none;cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;pointer-events:auto}.chat-messages .message .message-content .user .copy-btn mat-icon{font-size:16px;width:16px;height:16px;color:var(--bot-text-color)}.chat-messages .message .message-content .user .copy-btn:hover{background:var(--background-color)}.chat-messages .message .message-content .user:hover .copy-btn{opacity:1}.chat-messages .message .message-content .assistant{background-color:var(--bot-message-color);color:var(--bot-text-color);align-self:flex-start;border-radius:20px;padding:10px 15px;position:relative;-webkit-user-select:text;user-select:text;cursor:text;animation:fadeIn .4s ease-out forwards}.chat-messages .message .message-content .assistant .copy-btn{position:absolute;top:8px;right:8px;opacity:0;transition:opacity .2s ease;background:var(--background-color);border:none;cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;pointer-events:auto}.chat-messages .message .message-content .assistant .copy-btn mat-icon{font-size:16px;width:16px;height:16px;color:var(--bot-text-color)}.chat-messages .message .message-content .assistant .copy-btn:hover{background:var(--background-color)}.chat-messages .message .message-content .assistant:hover .copy-btn{opacity:1}.chat-messages .message .message-content .assistant .markdown-content h1,.chat-messages .message .message-content .assistant .markdown-content h2,.chat-messages .message .message-content .assistant .markdown-content h3{margin-top:10px;margin-bottom:5px;font-weight:600;line-height:1.25}.chat-messages .message .message-content .assistant .markdown-content h1{font-size:1.25em}.chat-messages .message .message-content .assistant .markdown-content h2{font-size:1.1em}.chat-messages .message .message-content .assistant .markdown-content h3{font-size:1em}.chat-messages .message .message-content .assistant .markdown-content p{margin-bottom:8px;line-height:1.5}.chat-messages .message .message-content .assistant .markdown-content ul,.chat-messages .message .message-content .assistant .markdown-content ol{margin-top:0;margin-bottom:8px;padding-left:20px}.chat-messages .message .message-content .assistant .markdown-content li{margin-bottom:4px;line-height:1.5}.chat-messages .message .message-content .assistant .markdown-content strong{font-weight:700}.chat-messages .message .message-content .assistant .markdown-content em{font-style:italic}.chat-messages .message .message-content .message-time{font-size:12px;color:var(--secondary-text-color);text-align:end;margin-top:6px;line-height:1;-webkit-user-select:none;user-select:none;cursor:default}.chat-messages .message .message-content ::selection{background-color:color-mix(in srgb,var(--primary-color),white 60%)}.chat-messages .message .message-content ::-moz-selection{background-color:color-mix(in srgb,var(--primary-color),white 60%)}.welcome-screen{display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;height:100%;padding:0 20px}.welcome-image{width:80px;margin-bottom:30px}.marquee-container{margin-bottom:12px}.welcome-greeting{margin:0;color:var(--text-color);font-family:var(--font-family);font-size:24px;font-weight:600;background:var(--text-color);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;display:inline-block}.welcome-greeting .emoji{background:none;-webkit-background-clip:initial;-webkit-text-fill-color:initial;background-clip:initial}.welcome-screen h3{margin:0 0 5px;color:var(--text-color);font-family:var(--font-family);font-size:18px}.welcome-screen p{margin:0 0 30px;color:var(--secondary-text-color);font-size:14px;font-family:var(--font-family)}@keyframes fadeIn{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.message-wrapper.streaming .assistant-avatar{box-shadow:0 0 8px rgba(var(--black-rgb),.1);transition:all .3s ease}.typing-indicator{display:flex;align-items:center;justify-content:flex-start;gap:4px;min-height:20px}.typing-indicator span{width:8px;height:8px;background-color:var(--typing-indicator-color);border-radius:50%;animation:blink 1.4s infinite both}.typing-indicator span:nth-child(2){animation-delay:.2s}.typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes blink{0%,80%,to{opacity:.2}40%{opacity:1}}.auth-buttons-container{display:flex;flex-direction:row;justify-content:center;gap:10px}.auth-btn{padding:8px 16px;border-radius:20px;font-size:.9em;font-weight:500;cursor:pointer;transition:all .2s ease}.auth-btn.primary{background:var(--primary-color);color:var(--white);border:1px solid var(--primary-color)}.auth-btn.primary:hover{opacity:.9}.auth-btn.primary:disabled{opacity:.5;cursor:not-allowed}.auth-btn.secondary{background:transparent;color:var(--primary-color);border:1px solid var(--primary-color)}.auth-btn.secondary:hover{opacity:.9}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: Chips, selector: "app-chips", inputs: ["messages", "disabled"], outputs: ["chipClick"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i1$2.DatePipe, name: "date" }, { kind: "pipe", type: MarkdownPipe, name: "Markdown" }] });
|
|
1497
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: MessageListComponent, isStandalone: true, selector: "app-message-list", inputs: { messages: "messages", isBotTyping: "isBotTyping", isStreaming: "isStreaming", appLogoUrl: "appLogoUrl", appSubtitle: "appSubtitle", welcomeDesc: "welcomeDesc", predefinedMessages: "predefinedMessages", botAvatarUrl: "botAvatarUrl", userAvatarUrl: "userAvatarUrl", userName: "userName", isAuthenticated: "isAuthenticated", isLoggingIn: "isLoggingIn", trackByMessageId: "trackByMessageId" }, outputs: { suggestionClick: "suggestionClick", loginClick: "loginClick" }, viewQueries: [{ propertyName: "chatMessagesContainer", first: true, predicate: ["chatMessages"], descendants: true }], ngImport: i0, template: "<div #chatMessages class=\"chat-messages\">\r\n <!-- Welcome Screen -->\r\n @if (messages.length === 0 && !isBotTyping) {\r\n <div class=\"welcome-screen\">\r\n <img [src]=\"appLogoUrl\" alt=\"Welcome\" class=\"welcome-image\" style=\"border-radius: 0px\" />\r\n\r\n <!-- Only show greeting if authenticated -->\r\n @if (userName && isAuthenticated) {\r\n <div class=\"marquee-container\">\r\n <h2 class=\"welcome-greeting marquee-text\">\r\n Hi, {{ userName }}!\r\n <span class=\"emoji\">{{ appEmoji.waveHand }}</span>\r\n </h2>\r\n </div>\r\n }\r\n\r\n <h3>{{ appSubtitle }}</h3>\r\n <p>{{ welcomeDesc }}</p>\r\n\r\n <!-- Predefined messages chips: Only show when authenticated -->\r\n @if (isAuthenticated) {\r\n <app-chips\r\n [messages]=\"predefinedMessages\"\r\n [disabled]=\"isBotTyping\"\r\n (chipClick)=\"onPredefinedClick($event)\"\r\n ></app-chips>\r\n }\r\n\r\n @if (!isAuthenticated) {\r\n <div class=\"auth-buttons-container\" style=\"margin-top: 20px\">\r\n <button (click)=\"loginClick.emit()\" class=\"auth-btn primary\" [disabled]=\"isLoggingIn\">\r\n {{ isLoggingIn ? 'Logging in...' : 'Login' }}\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Chat Messages -->\r\n <div\r\n *ngFor=\"let message of messages; trackBy: trackByMessageId\"\r\n class=\"message\"\r\n [class.user]=\"message.sender === 'user'\"\r\n [class.assistant]=\"message.sender === 'assistant'\"\r\n [class.streaming]=\"!message.completed && message.sender === 'assistant'\"\r\n >\r\n <!-- Avatar (Assistant on Left) -->\r\n @if (message.sender === 'assistant') {\r\n <div class=\"avatar-container\">\r\n <img class=\"assistant-avatar\" [src]=\"botAvatarUrl\" alt=\"Bot Avatar\" />\r\n </div>\r\n }\r\n\r\n <!-- Message Bubble -->\r\n <div class=\"message-content\">\r\n <!-- User chat bubble -->\r\n @if (message.sender === 'user') {\r\n <div class=\"user\">\r\n <span>{{ message.text }}</span>\r\n <button class=\"copy-btn\" (click)=\"copyMessage(message)\" title=\"Copy message\">\r\n <mat-icon>{{ message.copied ? 'check' : 'content_copy' }}</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Assistant chat bubble -->\r\n @if (message.sender === 'assistant') {\r\n <div class=\"assistant\">\r\n @if (isStreaming && !message.isHistory) {\r\n <!-- Show text/chunks if they exist -->\r\n @if (message.chunks && message.chunks.length > 0) {\r\n @for (chunk of message.chunks; track $index; let i = $index) {\r\n <span class=\"markdown-content\" [attr.data-index]=\"i\" [innerHTML]=\"chunk | Markdown\">\r\n </span>\r\n }\r\n } @else if (!message.completed) {\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n }\r\n } @else {\r\n <!-- Non-streaming or history -->\r\n @if (message.text) {\r\n <span class=\"markdown-content\" [innerHTML]=\"message.text | Markdown\"></span>\r\n } @else if (!message.completed) {\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n }\r\n }\r\n @if (message.completed) {\r\n <button class=\"copy-btn\" (click)=\"copyMessage(message)\" title=\"Copy message\">\r\n <mat-icon>{{ message.copied ? 'check' : 'content_copy' }}</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Show time below message -->\r\n @if (message.sender === 'assistant' && message.completed) {\r\n <div class=\"message-time\">\r\n {{ message.timestamp | date: 'shortTime' }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Avatar (User on Right) -->\r\n @if (message.sender === 'user') {\r\n <div class=\"avatar-container\">\r\n <img class=\"user-avatar\" [src]=\"userAvatarUrl\" alt=\"User Avatar\" />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Standalone Typing Indicator (Fallback if no message added yet) -->\r\n @if (isBotTyping && messages[messages.length - 1]?.sender !== 'assistant') {\r\n <div class=\"message assistant\">\r\n <div class=\"avatar-container\">\r\n <img class=\"assistant-avatar\" [src]=\"botAvatarUrl\" alt=\"Bot Avatar\" />\r\n </div>\r\n <div class=\"message-content\">\r\n <div class=\"assistant\">\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;flex-grow:1;overflow:hidden}.login-btn{padding:10px 15px;border:none;border-radius:20px;background-color:var(--primary-color);color:var(--user-text-color);font-size:1em;cursor:pointer;transition:background-color .2s ease}.chat-messages{flex-grow:1;padding:20px;overflow-y:auto;display:flex;flex-direction:column;gap:10px;scroll-behavior:smooth}.chat-messages::-webkit-scrollbar{width:6px}.chat-messages::-webkit-scrollbar-track{background:transparent}.chat-messages::-webkit-scrollbar-thumb{background:rgba(var(--black-rgb),.1);border-radius:10px}.chat-messages::-webkit-scrollbar-thumb:hover{background:rgba(var(--black-rgb),.1)}.chat-messages .message{display:flex;align-items:flex-end;gap:8px;margin-bottom:6px}.chat-messages .message.user{justify-content:flex-end}.chat-messages .message.assistant{justify-content:flex-start}.chat-messages .message .avatar-container{display:flex;flex-direction:column;align-items:center;gap:4px}.chat-messages .message .avatar-container .user-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--border-color)}.chat-messages .message .avatar-container .assistant-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--border-color);filter:var(--avatar-filter)}.chat-messages .message .avatar-container .user-avatar.placeholder{filter:var(--avatar-filter)}.chat-messages .message .avatar-container .user-name{font-size:10px;color:var(--secondary-text-color);font-family:var(--font-family);text-align:center;max-width:60px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chat-messages .message .message-content{max-width:80%;word-wrap:break-word;white-space:pre-wrap;font-family:var(--font-family);-webkit-user-select:text;user-select:text;cursor:text}.chat-messages .message .message-content .user{background-color:var(--primary-color);color:var(--user-text-color);align-self:flex-end;border-radius:20px;padding:10px 15px;position:relative;-webkit-user-select:text;user-select:text;cursor:text}.chat-messages .message .message-content .user .copy-btn{position:absolute;top:8px;right:8px;opacity:0;transition:opacity .2s ease;background:var(--background-color);border:none;cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;pointer-events:auto}.chat-messages .message .message-content .user .copy-btn mat-icon{font-size:16px;width:16px;height:16px;color:var(--bot-text-color)}.chat-messages .message .message-content .user .copy-btn:hover{background:var(--background-color)}.chat-messages .message .message-content .user:hover .copy-btn{opacity:1}.chat-messages .message .message-content .assistant{background-color:var(--bot-message-color);color:var(--bot-text-color);align-self:flex-start;border-radius:20px;padding:10px 15px;position:relative;-webkit-user-select:text;user-select:text;cursor:text;animation:fadeIn .4s ease-out forwards}.chat-messages .message .message-content .assistant .copy-btn{position:absolute;top:8px;right:8px;opacity:0;transition:opacity .2s ease;background:var(--background-color);border:none;cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;pointer-events:auto}.chat-messages .message .message-content .assistant .copy-btn mat-icon{font-size:16px;width:16px;height:16px;color:var(--bot-text-color)}.chat-messages .message .message-content .assistant .copy-btn:hover{background:var(--background-color)}.chat-messages .message .message-content .assistant:hover .copy-btn{opacity:1}.chat-messages .message .message-content .assistant .markdown-content h1,.chat-messages .message .message-content .assistant .markdown-content h2,.chat-messages .message .message-content .assistant .markdown-content h3{margin-top:10px;margin-bottom:5px;font-weight:600;line-height:1.25}.chat-messages .message .message-content .assistant .markdown-content h1{font-size:1.25em}.chat-messages .message .message-content .assistant .markdown-content h2{font-size:1.1em}.chat-messages .message .message-content .assistant .markdown-content h3{font-size:1em}.chat-messages .message .message-content .assistant .markdown-content p{margin-bottom:8px;line-height:1.5}.chat-messages .message .message-content .assistant .markdown-content ul,.chat-messages .message .message-content .assistant .markdown-content ol{margin-top:0;margin-bottom:8px;padding-left:20px}.chat-messages .message .message-content .assistant .markdown-content li{margin-bottom:4px;line-height:1.5}.chat-messages .message .message-content .assistant .markdown-content strong{font-weight:700}.chat-messages .message .message-content .assistant .markdown-content em{font-style:italic}.chat-messages .message .message-content .message-time{font-size:12px;color:var(--secondary-text-color);text-align:end;margin-top:6px;line-height:1;-webkit-user-select:none;user-select:none;cursor:default}.chat-messages .message .message-content ::selection{background-color:color-mix(in srgb,var(--primary-color),white 60%)}.chat-messages .message .message-content ::-moz-selection{background-color:color-mix(in srgb,var(--primary-color),white 60%)}.welcome-screen{display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;height:100%;padding:0 20px}.welcome-image{width:80px;margin-bottom:30px}.marquee-container{margin-bottom:12px}.welcome-greeting{margin:0;color:var(--text-color);font-family:var(--font-family);font-size:24px;font-weight:600;background:var(--text-color);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;display:inline-block}.welcome-greeting .emoji{background:none;-webkit-background-clip:initial;-webkit-text-fill-color:initial;background-clip:initial}.welcome-screen h3{margin:0 0 5px;color:var(--text-color);font-family:var(--font-family);font-size:18px}.welcome-screen p{margin:0 0 30px;color:var(--secondary-text-color);font-size:14px;font-family:var(--font-family)}@keyframes fadeIn{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.message-wrapper.streaming .assistant-avatar{box-shadow:0 0 8px rgba(var(--black-rgb),.1);transition:all .3s ease}.typing-indicator{display:flex;align-items:center;justify-content:flex-start;gap:4px;min-height:20px}.typing-indicator span{width:8px;height:8px;background-color:var(--typing-indicator-color);border-radius:50%;animation:blink 1.4s infinite both}.typing-indicator span:nth-child(2){animation-delay:.2s}.typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes blink{0%,80%,to{opacity:.2}40%{opacity:1}}.auth-buttons-container{display:flex;flex-direction:row;justify-content:center;gap:10px}.auth-btn{padding:8px 16px;border-radius:20px;font-size:.9em;font-weight:500;cursor:pointer;transition:all .2s ease}.auth-btn.primary{background:var(--primary-color);color:var(--white);border:1px solid var(--primary-color)}.auth-btn.primary:hover{opacity:.9}.auth-btn.primary:disabled{opacity:.5;cursor:not-allowed}.auth-btn.secondary{background:transparent;color:var(--primary-color);border:1px solid var(--primary-color)}.auth-btn.secondary:hover{opacity:.9}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: Chips, selector: "app-chips", inputs: ["messages", "disabled"], outputs: ["chipClick"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i1$2.DatePipe, name: "date" }, { kind: "pipe", type: MarkdownPipe, name: "Markdown" }] });
|
|
1493
1498
|
}
|
|
1494
1499
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MessageListComponent, decorators: [{
|
|
1495
1500
|
type: Component,
|
|
1496
|
-
args: [{ selector: 'app-message-list', standalone: true, imports: [CommonModule, Chips, MarkdownPipe, MatIconModule], template: "<div #chatMessages class=\"chat-messages\">\r\n <!-- Welcome Screen -->\r\n @if (messages.length === 0 && !isBotTyping) {\r\n <div class=\"welcome-screen\">\r\n <img [src]=\"appLogoUrl\" alt=\"Welcome\" class=\"welcome-image\" style=\"border-radius: 0px\" />\r\n\r\n <!-- Only show greeting if authenticated -->\r\n @if (userName && isAuthenticated) {\r\n <div class=\"marquee-container\">\r\n <h2 class=\"welcome-greeting marquee-text\">\r\n Hi, {{ userName }}!\r\n <span class=\"emoji\">{{ appEmoji.waveHand }}</span>\r\n </h2>\r\n </div>\r\n }\r\n\r\n <h3>{{ appSubtitle }}</h3>\r\n <p>{{ welcomeDesc }}</p>\r\n\r\n <!-- Predefined messages chips: Only show when authenticated -->\r\n @if (isAuthenticated) {\r\n <app-chips [messages]=\"predefinedMessages\" [disabled]=\"isBotTyping\"\r\n (chipClick)=\"onPredefinedClick($event)\"></app-chips>\r\n }\r\n\r\n @if (!isAuthenticated) {\r\n <div class=\"auth-buttons-container\" style=\"margin-top: 20px\">\r\n <button (click)=\"loginClick.emit()\" class=\"auth-btn primary\">Login</button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Chat Messages -->\r\n <div *ngFor=\"let message of messages; trackBy: trackByMessageId\" class=\"message\"\r\n [class.user]=\"message.sender === 'user'\" [class.assistant]=\"message.sender === 'assistant'\"\r\n [class.streaming]=\"!message.completed && message.sender === 'assistant'\">\r\n <!-- Avatar (Assistant on Left) -->\r\n @if (message.sender === 'assistant') {\r\n <div class=\"avatar-container\">\r\n <img class=\"assistant-avatar\" [src]=\"botAvatarUrl\" alt=\"Bot Avatar\" />\r\n </div>\r\n }\r\n\r\n <!-- Message Bubble -->\r\n <div class=\"message-content\">\r\n <!-- User chat bubble -->\r\n @if (message.sender === 'user') {\r\n <div class=\"user\">\r\n <span>{{ message.text }}</span>\r\n <button class=\"copy-btn\" (click)=\"copyMessage(message)\" title=\"Copy message\">\r\n <mat-icon>{{ message.copied ? 'check' : 'content_copy' }}</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Assistant chat bubble -->\r\n @if (message.sender === 'assistant') {\r\n <div class=\"assistant\">\r\n @if (isStreaming && !message.isHistory) {\r\n <!-- Show text/chunks if they exist -->\r\n @if (message.chunks && message.chunks.length > 0) {\r\n @for (chunk of message.chunks; track $index; let i = $index) {\r\n <span class=\"markdown-content\" [attr.data-index]=\"i\" [innerHTML]=\"chunk | Markdown\">\r\n </span>\r\n }\r\n } @else if (!message.completed) {\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n }\r\n } @else {\r\n <!-- Non-streaming or history -->\r\n @if (message.text) {\r\n <span class=\"markdown-content\" [innerHTML]=\"message.text | Markdown\"></span>\r\n } @else if (!message.completed) {\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n }\r\n }\r\n @if (message.completed) {\r\n <button class=\"copy-btn\" (click)=\"copyMessage(message)\" title=\"Copy message\">\r\n <mat-icon>{{ message.copied ? 'check' : 'content_copy' }}</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Show time below message -->\r\n @if (message.sender === 'assistant' && message.completed) {\r\n <div class=\"message-time\">\r\n {{ message.timestamp | date: 'shortTime' }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Avatar (User on Right) -->\r\n @if (message.sender === 'user') {\r\n <div class=\"avatar-container\">\r\n <img class=\"user-avatar\" [src]=\"userAvatarUrl\" alt=\"User Avatar\" />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Standalone Typing Indicator (Fallback if no message added yet) -->\r\n @if (isBotTyping && messages[messages.length - 1]?.sender !== 'assistant') {\r\n <div class=\"message assistant\">\r\n <div class=\"avatar-container\">\r\n <img class=\"assistant-avatar\" [src]=\"botAvatarUrl\" alt=\"Bot Avatar\" />\r\n </div>\r\n <div class=\"message-content\">\r\n <div class=\"assistant\">\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>", styles: [":host{display:flex;flex-direction:column;flex-grow:1;overflow:hidden}.login-btn{padding:10px 15px;border:none;border-radius:20px;background-color:var(--primary-color);color:var(--user-text-color);font-size:1em;cursor:pointer;transition:background-color .2s ease}.chat-messages{flex-grow:1;padding:20px;overflow-y:auto;display:flex;flex-direction:column;gap:10px;scroll-behavior:smooth}.chat-messages::-webkit-scrollbar{width:6px}.chat-messages::-webkit-scrollbar-track{background:transparent}.chat-messages::-webkit-scrollbar-thumb{background:rgba(var(--black-rgb),.1);border-radius:10px}.chat-messages::-webkit-scrollbar-thumb:hover{background:rgba(var(--black-rgb),.1)}.chat-messages .message{display:flex;align-items:flex-end;gap:8px;margin-bottom:6px}.chat-messages .message.user{justify-content:flex-end}.chat-messages .message.assistant{justify-content:flex-start}.chat-messages .message .avatar-container{display:flex;flex-direction:column;align-items:center;gap:4px}.chat-messages .message .avatar-container .user-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--border-color)}.chat-messages .message .avatar-container .assistant-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--border-color);filter:var(--avatar-filter)}.chat-messages .message .avatar-container .user-avatar.placeholder{filter:var(--avatar-filter)}.chat-messages .message .avatar-container .user-name{font-size:10px;color:var(--secondary-text-color);font-family:var(--font-family);text-align:center;max-width:60px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chat-messages .message .message-content{max-width:80%;word-wrap:break-word;white-space:pre-wrap;font-family:var(--font-family);-webkit-user-select:text;user-select:text;cursor:text}.chat-messages .message .message-content .user{background-color:var(--primary-color);color:var(--user-text-color);align-self:flex-end;border-radius:20px;padding:10px 15px;position:relative;-webkit-user-select:text;user-select:text;cursor:text}.chat-messages .message .message-content .user .copy-btn{position:absolute;top:8px;right:8px;opacity:0;transition:opacity .2s ease;background:var(--background-color);border:none;cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;pointer-events:auto}.chat-messages .message .message-content .user .copy-btn mat-icon{font-size:16px;width:16px;height:16px;color:var(--bot-text-color)}.chat-messages .message .message-content .user .copy-btn:hover{background:var(--background-color)}.chat-messages .message .message-content .user:hover .copy-btn{opacity:1}.chat-messages .message .message-content .assistant{background-color:var(--bot-message-color);color:var(--bot-text-color);align-self:flex-start;border-radius:20px;padding:10px 15px;position:relative;-webkit-user-select:text;user-select:text;cursor:text;animation:fadeIn .4s ease-out forwards}.chat-messages .message .message-content .assistant .copy-btn{position:absolute;top:8px;right:8px;opacity:0;transition:opacity .2s ease;background:var(--background-color);border:none;cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;pointer-events:auto}.chat-messages .message .message-content .assistant .copy-btn mat-icon{font-size:16px;width:16px;height:16px;color:var(--bot-text-color)}.chat-messages .message .message-content .assistant .copy-btn:hover{background:var(--background-color)}.chat-messages .message .message-content .assistant:hover .copy-btn{opacity:1}.chat-messages .message .message-content .assistant .markdown-content h1,.chat-messages .message .message-content .assistant .markdown-content h2,.chat-messages .message .message-content .assistant .markdown-content h3{margin-top:10px;margin-bottom:5px;font-weight:600;line-height:1.25}.chat-messages .message .message-content .assistant .markdown-content h1{font-size:1.25em}.chat-messages .message .message-content .assistant .markdown-content h2{font-size:1.1em}.chat-messages .message .message-content .assistant .markdown-content h3{font-size:1em}.chat-messages .message .message-content .assistant .markdown-content p{margin-bottom:8px;line-height:1.5}.chat-messages .message .message-content .assistant .markdown-content ul,.chat-messages .message .message-content .assistant .markdown-content ol{margin-top:0;margin-bottom:8px;padding-left:20px}.chat-messages .message .message-content .assistant .markdown-content li{margin-bottom:4px;line-height:1.5}.chat-messages .message .message-content .assistant .markdown-content strong{font-weight:700}.chat-messages .message .message-content .assistant .markdown-content em{font-style:italic}.chat-messages .message .message-content .message-time{font-size:12px;color:var(--secondary-text-color);text-align:end;margin-top:6px;line-height:1;-webkit-user-select:none;user-select:none;cursor:default}.chat-messages .message .message-content ::selection{background-color:color-mix(in srgb,var(--primary-color),white 60%)}.chat-messages .message .message-content ::-moz-selection{background-color:color-mix(in srgb,var(--primary-color),white 60%)}.welcome-screen{display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;height:100%;padding:0 20px}.welcome-image{width:80px;margin-bottom:30px}.marquee-container{margin-bottom:12px}.welcome-greeting{margin:0;color:var(--text-color);font-family:var(--font-family);font-size:24px;font-weight:600;background:var(--text-color);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;display:inline-block}.welcome-greeting .emoji{background:none;-webkit-background-clip:initial;-webkit-text-fill-color:initial;background-clip:initial}.welcome-screen h3{margin:0 0 5px;color:var(--text-color);font-family:var(--font-family);font-size:18px}.welcome-screen p{margin:0 0 30px;color:var(--secondary-text-color);font-size:14px;font-family:var(--font-family)}@keyframes fadeIn{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.message-wrapper.streaming .assistant-avatar{box-shadow:0 0 8px rgba(var(--black-rgb),.1);transition:all .3s ease}.typing-indicator{display:flex;align-items:center;justify-content:flex-start;gap:4px;min-height:20px}.typing-indicator span{width:8px;height:8px;background-color:var(--typing-indicator-color);border-radius:50%;animation:blink 1.4s infinite both}.typing-indicator span:nth-child(2){animation-delay:.2s}.typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes blink{0%,80%,to{opacity:.2}40%{opacity:1}}.auth-buttons-container{display:flex;flex-direction:row;justify-content:center;gap:10px}.auth-btn{padding:8px 16px;border-radius:20px;font-size:.9em;font-weight:500;cursor:pointer;transition:all .2s ease}.auth-btn.primary{background:var(--primary-color);color:var(--white);border:1px solid var(--primary-color)}.auth-btn.primary:hover{opacity:.9}.auth-btn.primary:disabled{opacity:.5;cursor:not-allowed}.auth-btn.secondary{background:transparent;color:var(--primary-color);border:1px solid var(--primary-color)}.auth-btn.secondary:hover{opacity:.9}\n"] }]
|
|
1501
|
+
args: [{ selector: 'app-message-list', standalone: true, imports: [CommonModule, Chips, MarkdownPipe, MatIconModule], template: "<div #chatMessages class=\"chat-messages\">\r\n <!-- Welcome Screen -->\r\n @if (messages.length === 0 && !isBotTyping) {\r\n <div class=\"welcome-screen\">\r\n <img [src]=\"appLogoUrl\" alt=\"Welcome\" class=\"welcome-image\" style=\"border-radius: 0px\" />\r\n\r\n <!-- Only show greeting if authenticated -->\r\n @if (userName && isAuthenticated) {\r\n <div class=\"marquee-container\">\r\n <h2 class=\"welcome-greeting marquee-text\">\r\n Hi, {{ userName }}!\r\n <span class=\"emoji\">{{ appEmoji.waveHand }}</span>\r\n </h2>\r\n </div>\r\n }\r\n\r\n <h3>{{ appSubtitle }}</h3>\r\n <p>{{ welcomeDesc }}</p>\r\n\r\n <!-- Predefined messages chips: Only show when authenticated -->\r\n @if (isAuthenticated) {\r\n <app-chips\r\n [messages]=\"predefinedMessages\"\r\n [disabled]=\"isBotTyping\"\r\n (chipClick)=\"onPredefinedClick($event)\"\r\n ></app-chips>\r\n }\r\n\r\n @if (!isAuthenticated) {\r\n <div class=\"auth-buttons-container\" style=\"margin-top: 20px\">\r\n <button (click)=\"loginClick.emit()\" class=\"auth-btn primary\" [disabled]=\"isLoggingIn\">\r\n {{ isLoggingIn ? 'Logging in...' : 'Login' }}\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Chat Messages -->\r\n <div\r\n *ngFor=\"let message of messages; trackBy: trackByMessageId\"\r\n class=\"message\"\r\n [class.user]=\"message.sender === 'user'\"\r\n [class.assistant]=\"message.sender === 'assistant'\"\r\n [class.streaming]=\"!message.completed && message.sender === 'assistant'\"\r\n >\r\n <!-- Avatar (Assistant on Left) -->\r\n @if (message.sender === 'assistant') {\r\n <div class=\"avatar-container\">\r\n <img class=\"assistant-avatar\" [src]=\"botAvatarUrl\" alt=\"Bot Avatar\" />\r\n </div>\r\n }\r\n\r\n <!-- Message Bubble -->\r\n <div class=\"message-content\">\r\n <!-- User chat bubble -->\r\n @if (message.sender === 'user') {\r\n <div class=\"user\">\r\n <span>{{ message.text }}</span>\r\n <button class=\"copy-btn\" (click)=\"copyMessage(message)\" title=\"Copy message\">\r\n <mat-icon>{{ message.copied ? 'check' : 'content_copy' }}</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Assistant chat bubble -->\r\n @if (message.sender === 'assistant') {\r\n <div class=\"assistant\">\r\n @if (isStreaming && !message.isHistory) {\r\n <!-- Show text/chunks if they exist -->\r\n @if (message.chunks && message.chunks.length > 0) {\r\n @for (chunk of message.chunks; track $index; let i = $index) {\r\n <span class=\"markdown-content\" [attr.data-index]=\"i\" [innerHTML]=\"chunk | Markdown\">\r\n </span>\r\n }\r\n } @else if (!message.completed) {\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n }\r\n } @else {\r\n <!-- Non-streaming or history -->\r\n @if (message.text) {\r\n <span class=\"markdown-content\" [innerHTML]=\"message.text | Markdown\"></span>\r\n } @else if (!message.completed) {\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n }\r\n }\r\n @if (message.completed) {\r\n <button class=\"copy-btn\" (click)=\"copyMessage(message)\" title=\"Copy message\">\r\n <mat-icon>{{ message.copied ? 'check' : 'content_copy' }}</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Show time below message -->\r\n @if (message.sender === 'assistant' && message.completed) {\r\n <div class=\"message-time\">\r\n {{ message.timestamp | date: 'shortTime' }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Avatar (User on Right) -->\r\n @if (message.sender === 'user') {\r\n <div class=\"avatar-container\">\r\n <img class=\"user-avatar\" [src]=\"userAvatarUrl\" alt=\"User Avatar\" />\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Standalone Typing Indicator (Fallback if no message added yet) -->\r\n @if (isBotTyping && messages[messages.length - 1]?.sender !== 'assistant') {\r\n <div class=\"message assistant\">\r\n <div class=\"avatar-container\">\r\n <img class=\"assistant-avatar\" [src]=\"botAvatarUrl\" alt=\"Bot Avatar\" />\r\n </div>\r\n <div class=\"message-content\">\r\n <div class=\"assistant\">\r\n <div class=\"typing-indicator\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;flex-grow:1;overflow:hidden}.login-btn{padding:10px 15px;border:none;border-radius:20px;background-color:var(--primary-color);color:var(--user-text-color);font-size:1em;cursor:pointer;transition:background-color .2s ease}.chat-messages{flex-grow:1;padding:20px;overflow-y:auto;display:flex;flex-direction:column;gap:10px;scroll-behavior:smooth}.chat-messages::-webkit-scrollbar{width:6px}.chat-messages::-webkit-scrollbar-track{background:transparent}.chat-messages::-webkit-scrollbar-thumb{background:rgba(var(--black-rgb),.1);border-radius:10px}.chat-messages::-webkit-scrollbar-thumb:hover{background:rgba(var(--black-rgb),.1)}.chat-messages .message{display:flex;align-items:flex-end;gap:8px;margin-bottom:6px}.chat-messages .message.user{justify-content:flex-end}.chat-messages .message.assistant{justify-content:flex-start}.chat-messages .message .avatar-container{display:flex;flex-direction:column;align-items:center;gap:4px}.chat-messages .message .avatar-container .user-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--border-color)}.chat-messages .message .avatar-container .assistant-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--border-color);filter:var(--avatar-filter)}.chat-messages .message .avatar-container .user-avatar.placeholder{filter:var(--avatar-filter)}.chat-messages .message .avatar-container .user-name{font-size:10px;color:var(--secondary-text-color);font-family:var(--font-family);text-align:center;max-width:60px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chat-messages .message .message-content{max-width:80%;word-wrap:break-word;white-space:pre-wrap;font-family:var(--font-family);-webkit-user-select:text;user-select:text;cursor:text}.chat-messages .message .message-content .user{background-color:var(--primary-color);color:var(--user-text-color);align-self:flex-end;border-radius:20px;padding:10px 15px;position:relative;-webkit-user-select:text;user-select:text;cursor:text}.chat-messages .message .message-content .user .copy-btn{position:absolute;top:8px;right:8px;opacity:0;transition:opacity .2s ease;background:var(--background-color);border:none;cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;pointer-events:auto}.chat-messages .message .message-content .user .copy-btn mat-icon{font-size:16px;width:16px;height:16px;color:var(--bot-text-color)}.chat-messages .message .message-content .user .copy-btn:hover{background:var(--background-color)}.chat-messages .message .message-content .user:hover .copy-btn{opacity:1}.chat-messages .message .message-content .assistant{background-color:var(--bot-message-color);color:var(--bot-text-color);align-self:flex-start;border-radius:20px;padding:10px 15px;position:relative;-webkit-user-select:text;user-select:text;cursor:text;animation:fadeIn .4s ease-out forwards}.chat-messages .message .message-content .assistant .copy-btn{position:absolute;top:8px;right:8px;opacity:0;transition:opacity .2s ease;background:var(--background-color);border:none;cursor:pointer;padding:4px;border-radius:4px;display:flex;align-items:center;justify-content:center;pointer-events:auto}.chat-messages .message .message-content .assistant .copy-btn mat-icon{font-size:16px;width:16px;height:16px;color:var(--bot-text-color)}.chat-messages .message .message-content .assistant .copy-btn:hover{background:var(--background-color)}.chat-messages .message .message-content .assistant:hover .copy-btn{opacity:1}.chat-messages .message .message-content .assistant .markdown-content h1,.chat-messages .message .message-content .assistant .markdown-content h2,.chat-messages .message .message-content .assistant .markdown-content h3{margin-top:10px;margin-bottom:5px;font-weight:600;line-height:1.25}.chat-messages .message .message-content .assistant .markdown-content h1{font-size:1.25em}.chat-messages .message .message-content .assistant .markdown-content h2{font-size:1.1em}.chat-messages .message .message-content .assistant .markdown-content h3{font-size:1em}.chat-messages .message .message-content .assistant .markdown-content p{margin-bottom:8px;line-height:1.5}.chat-messages .message .message-content .assistant .markdown-content ul,.chat-messages .message .message-content .assistant .markdown-content ol{margin-top:0;margin-bottom:8px;padding-left:20px}.chat-messages .message .message-content .assistant .markdown-content li{margin-bottom:4px;line-height:1.5}.chat-messages .message .message-content .assistant .markdown-content strong{font-weight:700}.chat-messages .message .message-content .assistant .markdown-content em{font-style:italic}.chat-messages .message .message-content .message-time{font-size:12px;color:var(--secondary-text-color);text-align:end;margin-top:6px;line-height:1;-webkit-user-select:none;user-select:none;cursor:default}.chat-messages .message .message-content ::selection{background-color:color-mix(in srgb,var(--primary-color),white 60%)}.chat-messages .message .message-content ::-moz-selection{background-color:color-mix(in srgb,var(--primary-color),white 60%)}.welcome-screen{display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;height:100%;padding:0 20px}.welcome-image{width:80px;margin-bottom:30px}.marquee-container{margin-bottom:12px}.welcome-greeting{margin:0;color:var(--text-color);font-family:var(--font-family);font-size:24px;font-weight:600;background:var(--text-color);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;display:inline-block}.welcome-greeting .emoji{background:none;-webkit-background-clip:initial;-webkit-text-fill-color:initial;background-clip:initial}.welcome-screen h3{margin:0 0 5px;color:var(--text-color);font-family:var(--font-family);font-size:18px}.welcome-screen p{margin:0 0 30px;color:var(--secondary-text-color);font-size:14px;font-family:var(--font-family)}@keyframes fadeIn{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.message-wrapper.streaming .assistant-avatar{box-shadow:0 0 8px rgba(var(--black-rgb),.1);transition:all .3s ease}.typing-indicator{display:flex;align-items:center;justify-content:flex-start;gap:4px;min-height:20px}.typing-indicator span{width:8px;height:8px;background-color:var(--typing-indicator-color);border-radius:50%;animation:blink 1.4s infinite both}.typing-indicator span:nth-child(2){animation-delay:.2s}.typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes blink{0%,80%,to{opacity:.2}40%{opacity:1}}.auth-buttons-container{display:flex;flex-direction:row;justify-content:center;gap:10px}.auth-btn{padding:8px 16px;border-radius:20px;font-size:.9em;font-weight:500;cursor:pointer;transition:all .2s ease}.auth-btn.primary{background:var(--primary-color);color:var(--white);border:1px solid var(--primary-color)}.auth-btn.primary:hover{opacity:.9}.auth-btn.primary:disabled{opacity:.5;cursor:not-allowed}.auth-btn.secondary{background:transparent;color:var(--primary-color);border:1px solid var(--primary-color)}.auth-btn.secondary:hover{opacity:.9}\n"] }]
|
|
1497
1502
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { messages: [{
|
|
1498
1503
|
type: Input
|
|
1499
1504
|
}], isBotTyping: [{
|
|
@@ -1516,6 +1521,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
|
|
|
1516
1521
|
type: Input
|
|
1517
1522
|
}], isAuthenticated: [{
|
|
1518
1523
|
type: Input
|
|
1524
|
+
}], isLoggingIn: [{
|
|
1525
|
+
type: Input
|
|
1519
1526
|
}], trackByMessageId: [{
|
|
1520
1527
|
type: Input
|
|
1521
1528
|
}], suggestionClick: [{
|
|
@@ -4009,14 +4016,20 @@ class ChatWindowComponent {
|
|
|
4009
4016
|
if (this.isAuthenticated) {
|
|
4010
4017
|
this.showLoginForm = false;
|
|
4011
4018
|
}
|
|
4012
|
-
|
|
4019
|
+
}
|
|
4020
|
+
//? Handle logging in status changes
|
|
4021
|
+
if (changes['isLoggingIn']) {
|
|
4022
|
+
if (this.isLoggingIn) {
|
|
4023
|
+
this.showLoginForm = false;
|
|
4024
|
+
}
|
|
4025
|
+
else if (!this.isAuthenticated && !changes['isLoggingIn'].firstChange) {
|
|
4013
4026
|
this.showLoginForm = true;
|
|
4014
4027
|
}
|
|
4015
4028
|
}
|
|
4016
4029
|
}
|
|
4017
4030
|
ngOnDestroy() { }
|
|
4018
4031
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ChatWindowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4019
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: ChatWindowComponent, isStandalone: true, selector: "app-chat-window", inputs: { isChatOpen: "isChatOpen", enableDrag: "enableDrag", enableResize: "enableResize", isFullScreen: "isFullScreen", isStreaming: "isStreaming", isAuthenticated: "isAuthenticated", appTitle: "appTitle", appLogoUrl: "appLogoUrl", appTextLogoUrl: "appTextLogoUrl", appHeaderLogoUrl: "appHeaderLogoUrl", messages: "messages", isBotTyping: "isBotTyping", appSubtitle: "appSubtitle", welcomeDesc: "welcomeDesc", predefinedMessages: "predefinedMessages", suggestedMessages: "suggestedMessages", botAvatarUrl: "botAvatarUrl", userAvatarUrl: "userAvatarUrl", userName: "userName", trackByMessageId: "trackByMessageId", hintText: "hintText", messageError: "messageError", showSuggestionChips: "showSuggestionChips", isHistorySidebarOpen: "isHistorySidebarOpen", chatSessions: "chatSessions", chatHistoryUserName: "chatHistoryUserName", isLoggingIn: "isLoggingIn", authError: "authError", authSuccess: "authSuccess", isLoadingSessions: "isLoadingSessions", hasMoreSessions: "hasMoreSessions", isLoadingMoreSessions: "isLoadingMoreSessions", processingSessionId: "processingSessionId", themeConfig: "themeConfig", buttonStyle: "buttonStyle" }, outputs: { toggleChat: "toggleChat", toggleFullScreen: "toggleFullScreen", toggleHistorySidebar: "toggleHistorySidebar", openSettings: "openSettings", suggestionClick: "suggestionClick", send: "send", stop: "stop", clearMessageError: "clearMessageError", clearChat: "clearChat", sessionSelected: "sessionSelected", sessionDeleted: "sessionDeleted", loadMoreSessions: "loadMoreSessions", refreshSessions: "refreshSessions", sessionRenamed: "sessionRenamed", deleteAllSessions: "deleteAllSessions", loginSubmit: "loginSubmit", logout: "logout" }, usesOnChanges: true, ngImport: i0, template: "@if (isChatOpen) {\r\n<div draggableDialog [enableDrag]=\"enableDrag && !buttonStyle.includes('sidebar')\" [dragHandle]=\"'.chat-header'\"\r\n resizableDialog [enableResize]=\"enableResize && !buttonStyle.includes('sidebar')\" class=\"chat-window\"\r\n [class.fullscreen]=\"isFullScreen\" [class.layout-sidebar]=\"buttonStyle.includes('sidebar')\">\r\n <!-- chat window header -->\r\n <app-chat-header [appHeaderLogoUrl]=\"appHeaderLogoUrl\" [isAuthenticated]=\"isAuthenticated\"\r\n [isFullScreen]=\"isFullScreen\" [settingsMenu]=\"settingsMenu\" [isNewChatDisabled]=\"messages.length === 0\"\r\n (toggleChat)=\"onToggleChat()\" (toggleFullScreen)=\"onToggleFullScreen()\"\r\n (toggleHistorySidebar)=\"onToggleHistorySidebar()\" (clearChat)=\"onClearChat()\"></app-chat-header>\r\n\r\n <!-- Main Content Area: Toggle between Messages and History -->\r\n <div class=\"chat-content-wrapper\">\r\n <!-- Chat History View -->\r\n <app-chat-history-sidebar [class.sidebar-closed]=\"!isHistorySidebarOpen\" [sessions]=\"chatSessions\"\r\n [isOpen]=\"isHistorySidebarOpen\" [userName]=\"chatHistoryUserName\" [isLoading]=\"isLoadingSessions\"\r\n [hasMoreSessions]=\"hasMoreSessions\" [isLoadingMore]=\"isLoadingMoreSessions\"\r\n [processingSessionId]=\"processingSessionId\" (sessionSelected)=\"onSessionSelected($event)\"\r\n (sessionDeleted)=\"onSessionDeleted($event)\" (loadMore)=\"loadMoreSessions.emit()\"\r\n (refresh)=\"refreshSessions.emit()\" (sessionRenamed)=\"onSessionRenamed($event)\" (deleteAll)=\"onDeleteAllSessions()\"\r\n (closed)=\"onToggleHistorySidebar()\">\r\n </app-chat-history-sidebar>\r\n\r\n <!-- Messages View -->\r\n <div class=\"messages-view\" [class.hidden]=\"isHistorySidebarOpen && !isFullScreen\">\r\n <!-- Messages / Welcome Screen -->\r\n @if (!showLoginForm) {\r\n <app-message-list [messages]=\"messages\" [isBotTyping]=\"isBotTyping\" [isStreaming]=\"isStreaming\"\r\n [appLogoUrl]=\"appLogoUrl\" [appSubtitle]=\"appSubtitle\" [welcomeDesc]=\"welcomeDesc\"\r\n [predefinedMessages]=\"predefinedMessages\" [botAvatarUrl]=\"botAvatarUrl\" [userAvatarUrl]=\"userAvatarUrl\"\r\n [userName]=\"userName\" [isAuthenticated]=\"isAuthenticated\" [trackByMessageId]=\"trackByMessageId\"\r\n (suggestionClick)=\"onSuggestionClick($event)\" (loginClick)=\"onLoginClick()\"></app-message-list>\r\n }\r\n\r\n @if (showSuggestionChips && isAuthenticated) {\r\n <app-chips [messages]=\"suggestedMessages\" [disabled]=\"isBotTyping\"\r\n (chipClick)=\"onSuggestionClick($event)\"></app-chips>\r\n }\r\n\r\n <!-- Chat Input: Only show when authenticated -->\r\n @if (isAuthenticated) {\r\n <app-message-input [isBotTyping]=\"isBotTyping\" [hintText]=\"hintText\" (send)=\"onSend($event)\"\r\n (stop)=\"onStop()\"></app-message-input>\r\n }\r\n\r\n <!-- Login Form -->\r\n @if (showLoginForm && !isAuthenticated) {\r\n <app-login-form [isLoggingIn]=\"isLoggingIn\" (loginSubmit)=\"onLoginSubmit($event)\"\r\n (cancel)=\"onCancelLogin()\"></app-login-form>\r\n }\r\n\r\n <!-- Auth error snackbar (login/logout errors only) -->\r\n <!-- <app-snackbar\r\n *ngIf=\"authError\"\r\n [message]=\"authError\"\r\n (closed)=\"onClearAuthError()\"\r\n ></app-snackbar> -->\r\n\r\n\r\n <!-- footer -->\r\n <app-chat-footer></app-chat-footer>\r\n </div>\r\n </div>\r\n</div>\r\n}\r\n\r\n<mat-menu #settingsMenu=\"matMenu\">\r\n <!-- Personalization -->\r\n @if (isAuthenticated) {\r\n <button mat-menu-item (click)=\"onOpenPersonalization()\" style=\"display: flex; align-items: center\">\r\n <mat-icon>settings_suggest</mat-icon>\r\n <span>Personalization</span>\r\n </button>\r\n }\r\n\r\n\r\n <!-- Change Theme -->\r\n\r\n <!-- Full-Screen Button -->\r\n <button mat-menu-item (click)=\"onToggleFullScreen()\" style=\"display: flex; align-items: center\">\r\n <mat-icon>{{ isFullScreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>\r\n <span> {{ isFullScreen ? 'Minimize' : 'Expand' }}</span>\r\n </button>\r\n\r\n <!-- Clear Chat -->\r\n @if (isAuthenticated && messages.length > 0) {\r\n <button mat-menu-item (click)=\"onClearChat()\" style=\"display: flex; align-items: center\">\r\n <mat-icon> clear_all</mat-icon>\r\n <span>End Chat</span>\r\n </button>\r\n }\r\n\r\n <!-- Logout -->\r\n @if (isAuthenticated) {\r\n <button mat-menu-item (click)=\"onLogout()\" style=\"display: flex; align-items: center\">\r\n <mat-icon>logout</mat-icon>\r\n <span>Logout</span>\r\n </button>\r\n }\r\n</mat-menu>", styles: [".doohbot-container{position:fixed;bottom:20px;right:20px;z-index:1000}.chat-window{width:clamp(400px,30vw,450px);height:clamp(620px,70vh,660px);background-color:var(--background-color);border-radius:20px;border-color:var(--border-color);box-shadow:var(--border-shadow-color);display:flex;flex-direction:column;overflow:hidden;animation:slide-in .5s ease;position:fixed;right:20px;bottom:20px;-webkit-user-select:none;user-select:none}@media (max-width: 768px){.chat-window{width:95%;height:calc(100vh - 20px)}}.chat-window.layout-sidebar{height:100vh;top:0;bottom:0;right:0;width:clamp(400px,25vw,450px);border-radius:20px 0 0 20px;animation:slide-in-sidebar .4s ease-out}.chat-window.layout-sidebar.fullscreen{width:100vw;height:100vh;border-radius:0;transform:none;top:0;left:0}@keyframes slide-in-sidebar{0%{transform:translate(100%)}to{transform:translate(0)}}@media (max-width: 480px){.chat-window{width:90%;height:calc(100vh - 40px)}}.chat-window.fullscreen{width:100vw;height:100vh;border-radius:0;position:fixed;top:0;left:0;transform:none;-webkit-user-select:none;user-select:none}.chat-content-wrapper{flex:1;display:flex;flex-direction:column;overflow:hidden;position:relative}.chat-content-wrapper app-chat-history-sidebar{height:100%;width:100%;display:flex;flex-direction:column;flex:1;min-height:0}.chat-window.fullscreen .chat-content-wrapper{flex-direction:row}.chat-window.fullscreen app-chat-history-sidebar{width:260px;flex:0 0 260px;border-right:1px solid var(--border-color)}.chat-window.fullscreen app-chat-history-sidebar.sidebar-closed{display:none}.messages-view{display:flex;flex-direction:column;height:100%;width:100%}.messages-view.hidden{display:none}.messages-view app-message-list,.messages-view app-login-form{flex:1;min-height:0}.messages-view app-snackbar{flex-shrink:0;margin-top:auto}.messages-view app-message-input{flex-shrink:0}.theme-selector{display:flex;align-items:center;padding:4px 12px;gap:8px}.theme-btn{background:none;border:none;color:var(--white);transition:color .2s ease}.theme-btn.selected{color:var(--primary-color)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "component", type: Chips, selector: "app-chips", inputs: ["messages", "disabled"], outputs: ["chipClick"] }, { kind: "component", type: MessageListComponent, selector: "app-message-list", inputs: ["messages", "isBotTyping", "isStreaming", "appLogoUrl", "appSubtitle", "welcomeDesc", "predefinedMessages", "botAvatarUrl", "userAvatarUrl", "userName", "isAuthenticated", "trackByMessageId"], outputs: ["suggestionClick", "loginClick"] }, { kind: "component", type: MessageInputComponent, selector: "app-message-input", inputs: ["isBotTyping", "hintText"], outputs: ["send", "stop"] }, { kind: "directive", type: DraggableDialogDirective, selector: "[draggableDialog]", inputs: ["dragHandle", "enableDrag"] }, { kind: "directive", type: ResizableDialogDirective, selector: "[resizableDialog]", inputs: ["enableResize"] }, { kind: "component", type: ChatHistorySidebarComponent, selector: "app-chat-history-sidebar", inputs: ["sessions", "isOpen", "userName", "isLoading", "hasMoreSessions", "isLoadingMore", "processingSessionId"], outputs: ["sessionSelected", "sessionDeleted", "closed", "loadMore", "refresh", "sessionRenamed", "deleteAll"] }, { kind: "component", type: ChatHeaderComponent, selector: "app-chat-header", inputs: ["appHeaderLogoUrl", "isAuthenticated", "isFullScreen", "settingsMenu", "isNewChatDisabled"], outputs: ["toggleChat", "toggleFullScreen", "toggleHistorySidebar", "clearChat"] }, { kind: "component", type: LoginFormComponent, selector: "app-login-form", inputs: ["isLoggingIn"], outputs: ["loginSubmit", "cancel"] }, { kind: "component", type: ChatFooterComponent, selector: "app-chat-footer" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatDialogModule }] });
|
|
4032
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: ChatWindowComponent, isStandalone: true, selector: "app-chat-window", inputs: { isChatOpen: "isChatOpen", enableDrag: "enableDrag", enableResize: "enableResize", isFullScreen: "isFullScreen", isStreaming: "isStreaming", isAuthenticated: "isAuthenticated", appTitle: "appTitle", appLogoUrl: "appLogoUrl", appTextLogoUrl: "appTextLogoUrl", appHeaderLogoUrl: "appHeaderLogoUrl", messages: "messages", isBotTyping: "isBotTyping", appSubtitle: "appSubtitle", welcomeDesc: "welcomeDesc", predefinedMessages: "predefinedMessages", suggestedMessages: "suggestedMessages", botAvatarUrl: "botAvatarUrl", userAvatarUrl: "userAvatarUrl", userName: "userName", trackByMessageId: "trackByMessageId", hintText: "hintText", messageError: "messageError", showSuggestionChips: "showSuggestionChips", isHistorySidebarOpen: "isHistorySidebarOpen", chatSessions: "chatSessions", chatHistoryUserName: "chatHistoryUserName", isLoggingIn: "isLoggingIn", authError: "authError", authSuccess: "authSuccess", isLoadingSessions: "isLoadingSessions", hasMoreSessions: "hasMoreSessions", isLoadingMoreSessions: "isLoadingMoreSessions", processingSessionId: "processingSessionId", themeConfig: "themeConfig", buttonStyle: "buttonStyle" }, outputs: { toggleChat: "toggleChat", toggleFullScreen: "toggleFullScreen", toggleHistorySidebar: "toggleHistorySidebar", openSettings: "openSettings", suggestionClick: "suggestionClick", send: "send", stop: "stop", clearMessageError: "clearMessageError", clearChat: "clearChat", sessionSelected: "sessionSelected", sessionDeleted: "sessionDeleted", loadMoreSessions: "loadMoreSessions", refreshSessions: "refreshSessions", sessionRenamed: "sessionRenamed", deleteAllSessions: "deleteAllSessions", loginSubmit: "loginSubmit", logout: "logout" }, usesOnChanges: true, ngImport: i0, template: "@if (isChatOpen) {\r\n <div\r\n draggableDialog\r\n [enableDrag]=\"enableDrag && !buttonStyle.includes('sidebar')\"\r\n [dragHandle]=\"'.chat-header'\"\r\n resizableDialog\r\n [enableResize]=\"enableResize && !buttonStyle.includes('sidebar')\"\r\n class=\"chat-window\"\r\n [class.fullscreen]=\"isFullScreen\"\r\n [class.layout-sidebar]=\"buttonStyle.includes('sidebar')\"\r\n >\r\n <!-- chat window header -->\r\n <app-chat-header\r\n [appHeaderLogoUrl]=\"appHeaderLogoUrl\"\r\n [isAuthenticated]=\"isAuthenticated\"\r\n [isFullScreen]=\"isFullScreen\"\r\n [settingsMenu]=\"settingsMenu\"\r\n [isNewChatDisabled]=\"messages.length === 0\"\r\n (toggleChat)=\"onToggleChat()\"\r\n (toggleFullScreen)=\"onToggleFullScreen()\"\r\n (toggleHistorySidebar)=\"onToggleHistorySidebar()\"\r\n (clearChat)=\"onClearChat()\"\r\n ></app-chat-header>\r\n\r\n <!-- Main Content Area: Toggle between Messages and History -->\r\n <div class=\"chat-content-wrapper\">\r\n <!-- Chat History View -->\r\n <app-chat-history-sidebar\r\n [class.sidebar-closed]=\"!isHistorySidebarOpen\"\r\n [sessions]=\"chatSessions\"\r\n [isOpen]=\"isHistorySidebarOpen\"\r\n [userName]=\"chatHistoryUserName\"\r\n [isLoading]=\"isLoadingSessions\"\r\n [hasMoreSessions]=\"hasMoreSessions\"\r\n [isLoadingMore]=\"isLoadingMoreSessions\"\r\n [processingSessionId]=\"processingSessionId\"\r\n (sessionSelected)=\"onSessionSelected($event)\"\r\n (sessionDeleted)=\"onSessionDeleted($event)\"\r\n (loadMore)=\"loadMoreSessions.emit()\"\r\n (refresh)=\"refreshSessions.emit()\"\r\n (sessionRenamed)=\"onSessionRenamed($event)\"\r\n (deleteAll)=\"onDeleteAllSessions()\"\r\n (closed)=\"onToggleHistorySidebar()\"\r\n >\r\n </app-chat-history-sidebar>\r\n\r\n <!-- Messages View -->\r\n <div class=\"messages-view\" [class.hidden]=\"isHistorySidebarOpen && !isFullScreen\">\r\n <!-- Messages / Welcome Screen -->\r\n @if (!showLoginForm) {\r\n <app-message-list\r\n [messages]=\"messages\"\r\n [isBotTyping]=\"isBotTyping\"\r\n [isStreaming]=\"isStreaming\"\r\n [appLogoUrl]=\"appLogoUrl\"\r\n [appSubtitle]=\"appSubtitle\"\r\n [welcomeDesc]=\"welcomeDesc\"\r\n [predefinedMessages]=\"predefinedMessages\"\r\n [botAvatarUrl]=\"botAvatarUrl\"\r\n [userAvatarUrl]=\"userAvatarUrl\"\r\n [userName]=\"userName\"\r\n [isAuthenticated]=\"isAuthenticated\"\r\n [isLoggingIn]=\"isLoggingIn\"\r\n [trackByMessageId]=\"trackByMessageId\"\r\n (suggestionClick)=\"onSuggestionClick($event)\"\r\n (loginClick)=\"onLoginClick()\"\r\n ></app-message-list>\r\n }\r\n\r\n @if (showSuggestionChips && isAuthenticated) {\r\n <app-chips\r\n [messages]=\"suggestedMessages\"\r\n [disabled]=\"isBotTyping\"\r\n (chipClick)=\"onSuggestionClick($event)\"\r\n ></app-chips>\r\n }\r\n\r\n <!-- Chat Input: Only show when authenticated -->\r\n @if (isAuthenticated) {\r\n <app-message-input\r\n [isBotTyping]=\"isBotTyping\"\r\n [hintText]=\"hintText\"\r\n (send)=\"onSend($event)\"\r\n (stop)=\"onStop()\"\r\n ></app-message-input>\r\n }\r\n\r\n <!-- Login Form -->\r\n @if (showLoginForm && !isAuthenticated) {\r\n <app-login-form\r\n [isLoggingIn]=\"isLoggingIn\"\r\n (loginSubmit)=\"onLoginSubmit($event)\"\r\n (cancel)=\"onCancelLogin()\"\r\n ></app-login-form>\r\n }\r\n\r\n <!-- Auth error snackbar (login/logout errors only) -->\r\n <!-- <app-snackbar\r\n *ngIf=\"authError\"\r\n [message]=\"authError\"\r\n (closed)=\"onClearAuthError()\"\r\n ></app-snackbar> -->\r\n\r\n <!-- footer -->\r\n <app-chat-footer></app-chat-footer>\r\n </div>\r\n </div>\r\n </div>\r\n}\r\n\r\n<mat-menu #settingsMenu=\"matMenu\">\r\n <!-- Personalization -->\r\n @if (isAuthenticated) {\r\n <button\r\n mat-menu-item\r\n (click)=\"onOpenPersonalization()\"\r\n style=\"display: flex; align-items: center\"\r\n >\r\n <mat-icon>settings_suggest</mat-icon>\r\n <span>Personalization</span>\r\n </button>\r\n }\r\n\r\n <!-- Change Theme -->\r\n\r\n <!-- Full-Screen Button -->\r\n <button mat-menu-item (click)=\"onToggleFullScreen()\" style=\"display: flex; align-items: center\">\r\n <mat-icon>{{ isFullScreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>\r\n <span> {{ isFullScreen ? 'Minimize' : 'Expand' }}</span>\r\n </button>\r\n\r\n <!-- Clear Chat -->\r\n @if (isAuthenticated && messages.length > 0) {\r\n <button mat-menu-item (click)=\"onClearChat()\" style=\"display: flex; align-items: center\">\r\n <mat-icon> clear_all</mat-icon>\r\n <span>End Chat</span>\r\n </button>\r\n }\r\n\r\n <!-- Logout -->\r\n @if (isAuthenticated) {\r\n <button mat-menu-item (click)=\"onLogout()\" style=\"display: flex; align-items: center\">\r\n <mat-icon>logout</mat-icon>\r\n <span>Logout</span>\r\n </button>\r\n }\r\n</mat-menu>\r\n", styles: [".doohbot-container{position:fixed;bottom:20px;right:20px;z-index:1000}.chat-window{width:clamp(400px,30vw,450px);height:clamp(620px,70vh,660px);background-color:var(--background-color);border-radius:20px;border-color:var(--border-color);box-shadow:var(--border-shadow-color);display:flex;flex-direction:column;overflow:hidden;animation:slide-in .5s ease;position:fixed;right:20px;bottom:20px;-webkit-user-select:none;user-select:none}@media (max-width: 768px){.chat-window{width:95%;height:calc(100vh - 20px)}}.chat-window.layout-sidebar{height:100vh;top:0;bottom:0;right:0;width:clamp(400px,25vw,450px);border-radius:20px 0 0 20px;animation:slide-in-sidebar .4s ease-out}.chat-window.layout-sidebar.fullscreen{width:100vw;height:100vh;border-radius:0;transform:none;top:0;left:0}@keyframes slide-in-sidebar{0%{transform:translate(100%)}to{transform:translate(0)}}@media (max-width: 480px){.chat-window{width:90%;height:calc(100vh - 40px)}}.chat-window.fullscreen{width:100vw;height:100vh;border-radius:0;position:fixed;top:0;left:0;transform:none;-webkit-user-select:none;user-select:none}.chat-content-wrapper{flex:1;display:flex;flex-direction:column;overflow:hidden;position:relative}.chat-content-wrapper app-chat-history-sidebar{height:100%;width:100%;display:flex;flex-direction:column;flex:1;min-height:0}.chat-window.fullscreen .chat-content-wrapper{flex-direction:row}.chat-window.fullscreen app-chat-history-sidebar{width:260px;flex:0 0 260px;border-right:1px solid var(--border-color)}.chat-window.fullscreen app-chat-history-sidebar.sidebar-closed{display:none}.messages-view{display:flex;flex-direction:column;height:100%;width:100%}.messages-view.hidden{display:none}.messages-view app-message-list,.messages-view app-login-form{flex:1;min-height:0}.messages-view app-snackbar{flex-shrink:0;margin-top:auto}.messages-view app-message-input{flex-shrink:0}.theme-selector{display:flex;align-items:center;padding:4px 12px;gap:8px}.theme-btn{background:none;border:none;color:var(--white);transition:color .2s ease}.theme-btn.selected{color:var(--primary-color)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "component", type: Chips, selector: "app-chips", inputs: ["messages", "disabled"], outputs: ["chipClick"] }, { kind: "component", type: MessageListComponent, selector: "app-message-list", inputs: ["messages", "isBotTyping", "isStreaming", "appLogoUrl", "appSubtitle", "welcomeDesc", "predefinedMessages", "botAvatarUrl", "userAvatarUrl", "userName", "isAuthenticated", "isLoggingIn", "trackByMessageId"], outputs: ["suggestionClick", "loginClick"] }, { kind: "component", type: MessageInputComponent, selector: "app-message-input", inputs: ["isBotTyping", "hintText"], outputs: ["send", "stop"] }, { kind: "directive", type: DraggableDialogDirective, selector: "[draggableDialog]", inputs: ["dragHandle", "enableDrag"] }, { kind: "directive", type: ResizableDialogDirective, selector: "[resizableDialog]", inputs: ["enableResize"] }, { kind: "component", type: ChatHistorySidebarComponent, selector: "app-chat-history-sidebar", inputs: ["sessions", "isOpen", "userName", "isLoading", "hasMoreSessions", "isLoadingMore", "processingSessionId"], outputs: ["sessionSelected", "sessionDeleted", "closed", "loadMore", "refresh", "sessionRenamed", "deleteAll"] }, { kind: "component", type: ChatHeaderComponent, selector: "app-chat-header", inputs: ["appHeaderLogoUrl", "isAuthenticated", "isFullScreen", "settingsMenu", "isNewChatDisabled"], outputs: ["toggleChat", "toggleFullScreen", "toggleHistorySidebar", "clearChat"] }, { kind: "component", type: LoginFormComponent, selector: "app-login-form", inputs: ["isLoggingIn"], outputs: ["loginSubmit", "cancel"] }, { kind: "component", type: ChatFooterComponent, selector: "app-chat-footer" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatDialogModule }] });
|
|
4020
4033
|
}
|
|
4021
4034
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ChatWindowComponent, decorators: [{
|
|
4022
4035
|
type: Component,
|
|
@@ -4035,7 +4048,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
|
|
|
4035
4048
|
ChatFooterComponent,
|
|
4036
4049
|
FormsModule,
|
|
4037
4050
|
MatDialogModule,
|
|
4038
|
-
], template: "@if (isChatOpen) {\r\n<div
|
|
4051
|
+
], template: "@if (isChatOpen) {\r\n <div\r\n draggableDialog\r\n [enableDrag]=\"enableDrag && !buttonStyle.includes('sidebar')\"\r\n [dragHandle]=\"'.chat-header'\"\r\n resizableDialog\r\n [enableResize]=\"enableResize && !buttonStyle.includes('sidebar')\"\r\n class=\"chat-window\"\r\n [class.fullscreen]=\"isFullScreen\"\r\n [class.layout-sidebar]=\"buttonStyle.includes('sidebar')\"\r\n >\r\n <!-- chat window header -->\r\n <app-chat-header\r\n [appHeaderLogoUrl]=\"appHeaderLogoUrl\"\r\n [isAuthenticated]=\"isAuthenticated\"\r\n [isFullScreen]=\"isFullScreen\"\r\n [settingsMenu]=\"settingsMenu\"\r\n [isNewChatDisabled]=\"messages.length === 0\"\r\n (toggleChat)=\"onToggleChat()\"\r\n (toggleFullScreen)=\"onToggleFullScreen()\"\r\n (toggleHistorySidebar)=\"onToggleHistorySidebar()\"\r\n (clearChat)=\"onClearChat()\"\r\n ></app-chat-header>\r\n\r\n <!-- Main Content Area: Toggle between Messages and History -->\r\n <div class=\"chat-content-wrapper\">\r\n <!-- Chat History View -->\r\n <app-chat-history-sidebar\r\n [class.sidebar-closed]=\"!isHistorySidebarOpen\"\r\n [sessions]=\"chatSessions\"\r\n [isOpen]=\"isHistorySidebarOpen\"\r\n [userName]=\"chatHistoryUserName\"\r\n [isLoading]=\"isLoadingSessions\"\r\n [hasMoreSessions]=\"hasMoreSessions\"\r\n [isLoadingMore]=\"isLoadingMoreSessions\"\r\n [processingSessionId]=\"processingSessionId\"\r\n (sessionSelected)=\"onSessionSelected($event)\"\r\n (sessionDeleted)=\"onSessionDeleted($event)\"\r\n (loadMore)=\"loadMoreSessions.emit()\"\r\n (refresh)=\"refreshSessions.emit()\"\r\n (sessionRenamed)=\"onSessionRenamed($event)\"\r\n (deleteAll)=\"onDeleteAllSessions()\"\r\n (closed)=\"onToggleHistorySidebar()\"\r\n >\r\n </app-chat-history-sidebar>\r\n\r\n <!-- Messages View -->\r\n <div class=\"messages-view\" [class.hidden]=\"isHistorySidebarOpen && !isFullScreen\">\r\n <!-- Messages / Welcome Screen -->\r\n @if (!showLoginForm) {\r\n <app-message-list\r\n [messages]=\"messages\"\r\n [isBotTyping]=\"isBotTyping\"\r\n [isStreaming]=\"isStreaming\"\r\n [appLogoUrl]=\"appLogoUrl\"\r\n [appSubtitle]=\"appSubtitle\"\r\n [welcomeDesc]=\"welcomeDesc\"\r\n [predefinedMessages]=\"predefinedMessages\"\r\n [botAvatarUrl]=\"botAvatarUrl\"\r\n [userAvatarUrl]=\"userAvatarUrl\"\r\n [userName]=\"userName\"\r\n [isAuthenticated]=\"isAuthenticated\"\r\n [isLoggingIn]=\"isLoggingIn\"\r\n [trackByMessageId]=\"trackByMessageId\"\r\n (suggestionClick)=\"onSuggestionClick($event)\"\r\n (loginClick)=\"onLoginClick()\"\r\n ></app-message-list>\r\n }\r\n\r\n @if (showSuggestionChips && isAuthenticated) {\r\n <app-chips\r\n [messages]=\"suggestedMessages\"\r\n [disabled]=\"isBotTyping\"\r\n (chipClick)=\"onSuggestionClick($event)\"\r\n ></app-chips>\r\n }\r\n\r\n <!-- Chat Input: Only show when authenticated -->\r\n @if (isAuthenticated) {\r\n <app-message-input\r\n [isBotTyping]=\"isBotTyping\"\r\n [hintText]=\"hintText\"\r\n (send)=\"onSend($event)\"\r\n (stop)=\"onStop()\"\r\n ></app-message-input>\r\n }\r\n\r\n <!-- Login Form -->\r\n @if (showLoginForm && !isAuthenticated) {\r\n <app-login-form\r\n [isLoggingIn]=\"isLoggingIn\"\r\n (loginSubmit)=\"onLoginSubmit($event)\"\r\n (cancel)=\"onCancelLogin()\"\r\n ></app-login-form>\r\n }\r\n\r\n <!-- Auth error snackbar (login/logout errors only) -->\r\n <!-- <app-snackbar\r\n *ngIf=\"authError\"\r\n [message]=\"authError\"\r\n (closed)=\"onClearAuthError()\"\r\n ></app-snackbar> -->\r\n\r\n <!-- footer -->\r\n <app-chat-footer></app-chat-footer>\r\n </div>\r\n </div>\r\n </div>\r\n}\r\n\r\n<mat-menu #settingsMenu=\"matMenu\">\r\n <!-- Personalization -->\r\n @if (isAuthenticated) {\r\n <button\r\n mat-menu-item\r\n (click)=\"onOpenPersonalization()\"\r\n style=\"display: flex; align-items: center\"\r\n >\r\n <mat-icon>settings_suggest</mat-icon>\r\n <span>Personalization</span>\r\n </button>\r\n }\r\n\r\n <!-- Change Theme -->\r\n\r\n <!-- Full-Screen Button -->\r\n <button mat-menu-item (click)=\"onToggleFullScreen()\" style=\"display: flex; align-items: center\">\r\n <mat-icon>{{ isFullScreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>\r\n <span> {{ isFullScreen ? 'Minimize' : 'Expand' }}</span>\r\n </button>\r\n\r\n <!-- Clear Chat -->\r\n @if (isAuthenticated && messages.length > 0) {\r\n <button mat-menu-item (click)=\"onClearChat()\" style=\"display: flex; align-items: center\">\r\n <mat-icon> clear_all</mat-icon>\r\n <span>End Chat</span>\r\n </button>\r\n }\r\n\r\n <!-- Logout -->\r\n @if (isAuthenticated) {\r\n <button mat-menu-item (click)=\"onLogout()\" style=\"display: flex; align-items: center\">\r\n <mat-icon>logout</mat-icon>\r\n <span>Logout</span>\r\n </button>\r\n }\r\n</mat-menu>\r\n", styles: [".doohbot-container{position:fixed;bottom:20px;right:20px;z-index:1000}.chat-window{width:clamp(400px,30vw,450px);height:clamp(620px,70vh,660px);background-color:var(--background-color);border-radius:20px;border-color:var(--border-color);box-shadow:var(--border-shadow-color);display:flex;flex-direction:column;overflow:hidden;animation:slide-in .5s ease;position:fixed;right:20px;bottom:20px;-webkit-user-select:none;user-select:none}@media (max-width: 768px){.chat-window{width:95%;height:calc(100vh - 20px)}}.chat-window.layout-sidebar{height:100vh;top:0;bottom:0;right:0;width:clamp(400px,25vw,450px);border-radius:20px 0 0 20px;animation:slide-in-sidebar .4s ease-out}.chat-window.layout-sidebar.fullscreen{width:100vw;height:100vh;border-radius:0;transform:none;top:0;left:0}@keyframes slide-in-sidebar{0%{transform:translate(100%)}to{transform:translate(0)}}@media (max-width: 480px){.chat-window{width:90%;height:calc(100vh - 40px)}}.chat-window.fullscreen{width:100vw;height:100vh;border-radius:0;position:fixed;top:0;left:0;transform:none;-webkit-user-select:none;user-select:none}.chat-content-wrapper{flex:1;display:flex;flex-direction:column;overflow:hidden;position:relative}.chat-content-wrapper app-chat-history-sidebar{height:100%;width:100%;display:flex;flex-direction:column;flex:1;min-height:0}.chat-window.fullscreen .chat-content-wrapper{flex-direction:row}.chat-window.fullscreen app-chat-history-sidebar{width:260px;flex:0 0 260px;border-right:1px solid var(--border-color)}.chat-window.fullscreen app-chat-history-sidebar.sidebar-closed{display:none}.messages-view{display:flex;flex-direction:column;height:100%;width:100%}.messages-view.hidden{display:none}.messages-view app-message-list,.messages-view app-login-form{flex:1;min-height:0}.messages-view app-snackbar{flex-shrink:0;margin-top:auto}.messages-view app-message-input{flex-shrink:0}.theme-selector{display:flex;align-items:center;padding:4px 12px;gap:8px}.theme-btn{background:none;border:none;color:var(--white);transition:color .2s ease}.theme-btn.selected{color:var(--primary-color)}\n"] }]
|
|
4039
4052
|
}], ctorParameters: () => [], propDecorators: { isChatOpen: [{
|
|
4040
4053
|
type: Input
|
|
4041
4054
|
}], enableDrag: [{
|
|
@@ -4208,7 +4221,7 @@ class LicenseService {
|
|
|
4208
4221
|
*/
|
|
4209
4222
|
async loadLicenseFile(filePath) {
|
|
4210
4223
|
try {
|
|
4211
|
-
const finalPath = filePath || this.coreConfig.
|
|
4224
|
+
const finalPath = filePath || this.coreConfig.licenseFilePath;
|
|
4212
4225
|
const data = await firstValueFrom(this.http.get(finalPath));
|
|
4213
4226
|
if (!data) {
|
|
4214
4227
|
throw new Error('Empty license file');
|
|
@@ -4241,17 +4254,20 @@ class LicenseService {
|
|
|
4241
4254
|
Logger.log(`[LicenseService] Hierarchical Registry loaded for account: ${data.accountId}`);
|
|
4242
4255
|
}
|
|
4243
4256
|
processFlatMapLicense(data) {
|
|
4244
|
-
//
|
|
4245
|
-
//
|
|
4246
|
-
// Industry pattern: Flat maps in external files are often treated as "Environment Overrides"
|
|
4247
|
-
// For now, lets store it as a special registry that doesn't enforce accountId
|
|
4257
|
+
// Flat-map format: { "tenantName": "license_key" }
|
|
4258
|
+
// Convert each entry to: tenant:tenantName -> { doohbot: license_key }
|
|
4248
4259
|
const subjectMap = new Map();
|
|
4249
|
-
|
|
4260
|
+
Object.entries(data).forEach(([tenantName, licenseKey]) => {
|
|
4261
|
+
const tenantKey = `tenant:${tenantName}`;
|
|
4262
|
+
subjectMap.set(tenantKey, {
|
|
4263
|
+
doohbot: licenseKey,
|
|
4264
|
+
});
|
|
4265
|
+
});
|
|
4250
4266
|
this.registry.set({
|
|
4251
|
-
accountId: '
|
|
4267
|
+
accountId: 'FLAT_MAP_REGISTRY',
|
|
4252
4268
|
subjectLicenses: subjectMap,
|
|
4253
4269
|
});
|
|
4254
|
-
Logger.log(
|
|
4270
|
+
Logger.log(`[LicenseService] Flat-map registry loaded with ${subjectMap.size} tenant(s)`);
|
|
4255
4271
|
}
|
|
4256
4272
|
/**
|
|
4257
4273
|
* Resolve the correct license key based on context and requested package
|
|
@@ -4295,28 +4311,23 @@ class LicenseService {
|
|
|
4295
4311
|
* Resolves the license key for the current tenant.
|
|
4296
4312
|
*/
|
|
4297
4313
|
resolveTenantLicenseKey(tenancyName) {
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
Logger.info(`[LicenseService] Using tenancy name: ${finalTenancyName}`);
|
|
4302
|
-
tenancyName = finalTenancyName;
|
|
4303
|
-
}
|
|
4304
|
-
else {
|
|
4305
|
-
Logger.error('[LicenseService] Error resolving tenancy name from globals:');
|
|
4306
|
-
}
|
|
4307
|
-
if (!tenancyName) {
|
|
4308
|
-
Logger.warn('[LicenseService] Could not resolve tenancy name.');
|
|
4314
|
+
const activeTenancy = tenancyName || this.coreConfig.tenancyName;
|
|
4315
|
+
if (!activeTenancy) {
|
|
4316
|
+
Logger.warn('[LicenseService] No tenancy name found.');
|
|
4309
4317
|
return null;
|
|
4310
4318
|
}
|
|
4311
|
-
Logger.debug(`[LicenseService] Resolving license for tenant: ${tenancyName}`);
|
|
4312
|
-
// Lookup license key in registry
|
|
4313
4319
|
const reg = this.registry();
|
|
4314
|
-
if (!reg)
|
|
4320
|
+
if (!reg) {
|
|
4321
|
+
Logger.error('[LicenseService] License registry not loaded.');
|
|
4315
4322
|
return null;
|
|
4316
|
-
|
|
4323
|
+
}
|
|
4324
|
+
const tenantLicenses = reg.subjectLicenses.get(`tenant:${activeTenancy}`);
|
|
4317
4325
|
const licenseKey = tenantLicenses?.['doohbot'] || null;
|
|
4318
4326
|
if (licenseKey) {
|
|
4319
|
-
Logger.info(`[LicenseService]
|
|
4327
|
+
Logger.info(`[LicenseService] Resolved Doohbot license for: ${activeTenancy}`);
|
|
4328
|
+
}
|
|
4329
|
+
else {
|
|
4330
|
+
Logger.warn(`[LicenseService] No license key found for tenant: ${activeTenancy}`);
|
|
4320
4331
|
}
|
|
4321
4332
|
return licenseKey;
|
|
4322
4333
|
}
|
|
@@ -4413,8 +4424,12 @@ class Doohbot extends DoohbotInput {
|
|
|
4413
4424
|
}
|
|
4414
4425
|
//! ====================== LIFECYCLE HOOKS =====================
|
|
4415
4426
|
// Corrected structural integrity.
|
|
4416
|
-
ngOnInit() {
|
|
4427
|
+
async ngOnInit() {
|
|
4417
4428
|
Logger.log('Initializing Doohbot component...');
|
|
4429
|
+
// Load license registry from file and wait for it to complete
|
|
4430
|
+
if (this.config.licenseFilePath) {
|
|
4431
|
+
await this.licenseService.loadLicenseFile(this.config.licenseFilePath);
|
|
4432
|
+
}
|
|
4418
4433
|
// Run validation once on startup
|
|
4419
4434
|
this.authService.validateStoredToken().subscribe();
|
|
4420
4435
|
// React to auth status changes
|
|
@@ -4431,10 +4446,27 @@ class Doohbot extends DoohbotInput {
|
|
|
4431
4446
|
this.initializeUI();
|
|
4432
4447
|
}
|
|
4433
4448
|
else {
|
|
4434
|
-
//!
|
|
4435
|
-
this.
|
|
4449
|
+
//! STRICT VALIDATION: Only attempt federated login if we have the necessary configuration
|
|
4450
|
+
const hasAuthToken = !!this.config.authToken;
|
|
4451
|
+
// STRICT VALIDATION: We need both Token AND Valid License Context to proceed
|
|
4452
|
+
if (this.canAttemptFederatedLogin()) {
|
|
4453
|
+
Logger.log('Attempting federated login...');
|
|
4454
|
+
await this.onFederatedLogin();
|
|
4455
|
+
// If federated login failed, the login screen will be shown automatically
|
|
4456
|
+
// via the auth status subscription above
|
|
4457
|
+
}
|
|
4458
|
+
else {
|
|
4459
|
+
Logger.info('Insufficient config for federated login (Need Token + TenancyName/LicenseKey). Waiting for user login.');
|
|
4460
|
+
}
|
|
4436
4461
|
}
|
|
4437
4462
|
}
|
|
4463
|
+
canAttemptFederatedLogin() {
|
|
4464
|
+
const hasAuthToken = !!this.config.authToken;
|
|
4465
|
+
const hasDirectLicenseKey = !!this.config.licenseKey;
|
|
4466
|
+
const hasTenantLicenseLookup = !!this.config.tenancyName && !!this.config.licenseFilePath;
|
|
4467
|
+
const hasValidLicenseContext = hasDirectLicenseKey || hasTenantLicenseLookup;
|
|
4468
|
+
return hasAuthToken && hasValidLicenseContext;
|
|
4469
|
+
}
|
|
4438
4470
|
initializeUI() {
|
|
4439
4471
|
// Apply theme
|
|
4440
4472
|
this.themeService.applyCssVariables(this.elementRef.nativeElement, this.themeConfig);
|
|
@@ -4473,7 +4505,8 @@ class Doohbot extends DoohbotInput {
|
|
|
4473
4505
|
// Handle dynamic config updates for authentication
|
|
4474
4506
|
if (!this.isAuthenticated()) {
|
|
4475
4507
|
const token = this.config.authToken;
|
|
4476
|
-
|
|
4508
|
+
// Validate Config before attempting
|
|
4509
|
+
if (token && this.canAttemptFederatedLogin()) {
|
|
4477
4510
|
this.onFederatedLogin(token);
|
|
4478
4511
|
}
|
|
4479
4512
|
}
|
|
@@ -4491,12 +4524,12 @@ class Doohbot extends DoohbotInput {
|
|
|
4491
4524
|
//! ====================== AUTHENTICATION ==================
|
|
4492
4525
|
// Logic moved to ChatFacadeService.initialize()
|
|
4493
4526
|
onNormalLogin(credentials) {
|
|
4494
|
-
//
|
|
4527
|
+
// Use Tenancy Name from Config Input
|
|
4495
4528
|
let tenancy = this.config.tenancyName;
|
|
4496
|
-
|
|
4497
|
-
if (!tenancy) {
|
|
4498
|
-
|
|
4499
|
-
}
|
|
4529
|
+
//! fallback: Try to resolve from global utility (Auto-detect)
|
|
4530
|
+
// if (!tenancy) {
|
|
4531
|
+
// tenancy = window.AppPreBootstrap?.resolveTenancyName(window.AppConsts?.appBaseUrl);
|
|
4532
|
+
// }
|
|
4500
4533
|
// Resolve key from whatever tenancy we found
|
|
4501
4534
|
let autoKey = this.licenseService.resolveTenantLicenseKey(tenancy || undefined);
|
|
4502
4535
|
// Inject for strict validation
|
|
@@ -4507,8 +4540,8 @@ class Doohbot extends DoohbotInput {
|
|
|
4507
4540
|
}
|
|
4508
4541
|
this.authService.login(credentials, false);
|
|
4509
4542
|
}
|
|
4510
|
-
onFederatedLogin(token) {
|
|
4511
|
-
//
|
|
4543
|
+
async onFederatedLogin(token) {
|
|
4544
|
+
// Resolve Key from tenancy name
|
|
4512
4545
|
let autoKey = this.licenseService.resolveTenantLicenseKey();
|
|
4513
4546
|
// Fallback to Config Input
|
|
4514
4547
|
if (!autoKey) {
|
|
@@ -4519,10 +4552,14 @@ class Doohbot extends DoohbotInput {
|
|
|
4519
4552
|
}
|
|
4520
4553
|
const request = {
|
|
4521
4554
|
accessToken: token,
|
|
4522
|
-
rememberMe:
|
|
4555
|
+
rememberMe: this.config.rememberMe,
|
|
4523
4556
|
license: autoKey,
|
|
4524
4557
|
};
|
|
4525
|
-
|
|
4558
|
+
// Await the federated login result
|
|
4559
|
+
const success = await this.authService.federatedLogin(request);
|
|
4560
|
+
if (!success) {
|
|
4561
|
+
Logger.info('Federated login failed. User can proceed with normal login.');
|
|
4562
|
+
}
|
|
4526
4563
|
}
|
|
4527
4564
|
performLogout() {
|
|
4528
4565
|
DialogUtils.confirmLogout(this.dialogService).subscribe((confirmed) => {
|
|
@@ -4564,7 +4601,7 @@ class Doohbot extends DoohbotInput {
|
|
|
4564
4601
|
this.uiState.toggleHistorySidebar();
|
|
4565
4602
|
}
|
|
4566
4603
|
loadChatSession(session) {
|
|
4567
|
-
Logger.log(`
|
|
4604
|
+
Logger.log(`Session selected: ${session.id}. continuing session...`);
|
|
4568
4605
|
this.chatService.continueSession(session.id);
|
|
4569
4606
|
this.uiState.closeHistorySidebar();
|
|
4570
4607
|
}
|
|
@@ -4880,7 +4917,7 @@ class LicenseInterceptor {
|
|
|
4880
4917
|
// Resolve License Key for Authenticated Request
|
|
4881
4918
|
const context = authService.getLicensingContext();
|
|
4882
4919
|
// Post-login, context.tenantId should be company_id
|
|
4883
|
-
const licenseKey = licenseService.resolveLicenseKey(context, '
|
|
4920
|
+
const licenseKey = licenseService.resolveLicenseKey(context, '');
|
|
4884
4921
|
if (licenseKey) {
|
|
4885
4922
|
const clonedReq = req.clone({
|
|
4886
4923
|
headers: req.headers.set('x-license-key', licenseKey),
|
|
@@ -4913,9 +4950,9 @@ function provideDoohbot() {
|
|
|
4913
4950
|
}
|
|
4914
4951
|
|
|
4915
4952
|
// /*
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
//
|
|
4953
|
+
/* Public API Surface of chatbot
|
|
4954
|
+
*/
|
|
4955
|
+
// Main component
|
|
4919
4956
|
// // Services
|
|
4920
4957
|
// export * from './lib/core/services/doohbot-api.service';
|
|
4921
4958
|
// export * from './lib/core/auth/auth.service';
|