emanate-ai-chat-lib 0.1.4 → 0.1.6
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/bundles/emanate-ai-chat-lib.umd.js +1058 -109
- package/bundles/emanate-ai-chat-lib.umd.js.map +1 -1
- package/esm2015/lib/components/ai-chat.component.js +451 -100
- package/esm2015/lib/services/ai-agent.service.js +238 -8
- package/esm2015/lib/services/conversation-state.service.js +355 -0
- package/esm2015/public-api.js +2 -1
- package/fesm2015/emanate-ai-chat-lib.js +1037 -107
- package/fesm2015/emanate-ai-chat-lib.js.map +1 -1
- package/lib/components/ai-chat.component.d.ts +68 -2
- package/lib/services/ai-agent.service.d.ts +100 -0
- package/lib/services/conversation-state.service.d.ts +113 -0
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common'), require('@angular/forms'), require('@angular/common/http'), require('rxjs'), require('rxjs/operators'), require('@angular/platform-browser')) :
|
|
3
3
|
typeof define === 'function' && define.amd ? define('emanate-ai-chat-lib', ['exports', '@angular/core', '@angular/common', '@angular/forms', '@angular/common/http', 'rxjs', 'rxjs/operators', '@angular/platform-browser'], factory) :
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["emanate-ai-chat-lib"] = {}, global.ng.core, global.ng.common, global.ng.forms, global.ng.common.http, global.rxjs, global.rxjs.operators, global.ng.platformBrowser));
|
|
5
|
-
})(this, (function (exports, i0,
|
|
5
|
+
})(this, (function (exports, i0, i4, i5, i1, rxjs, operators, i3) { 'use strict';
|
|
6
6
|
|
|
7
7
|
function _interopNamespace(e) {
|
|
8
8
|
if (e && e.__esModule) return e;
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
var i0__namespace = /*#__PURE__*/_interopNamespace(i0);
|
|
26
|
-
var i3__namespace = /*#__PURE__*/_interopNamespace(i3);
|
|
27
26
|
var i4__namespace = /*#__PURE__*/_interopNamespace(i4);
|
|
27
|
+
var i5__namespace = /*#__PURE__*/_interopNamespace(i5);
|
|
28
28
|
var i1__namespace = /*#__PURE__*/_interopNamespace(i1);
|
|
29
|
-
var
|
|
29
|
+
var i3__namespace = /*#__PURE__*/_interopNamespace(i3);
|
|
30
30
|
|
|
31
31
|
var EmanateAiChatLibService = /** @class */ (function () {
|
|
32
32
|
function EmanateAiChatLibService() {
|
|
@@ -685,21 +685,36 @@
|
|
|
685
685
|
function AiAgentService(http) {
|
|
686
686
|
this.http = http;
|
|
687
687
|
this.defaultConfig = {
|
|
688
|
-
apiUrl: 'https://localhost:7237/api
|
|
688
|
+
apiUrl: 'https://localhost:7237/api',
|
|
689
689
|
timeout: 30000,
|
|
690
|
-
retries: 3
|
|
690
|
+
retries: 3,
|
|
691
|
+
enableConversationLifecycle: true // Enable by default
|
|
691
692
|
};
|
|
692
693
|
this.config = Object.assign({}, this.defaultConfig);
|
|
693
694
|
}
|
|
694
695
|
AiAgentService.prototype.configure = function (config) {
|
|
695
696
|
this.config = Object.assign(Object.assign({}, this.defaultConfig), config);
|
|
696
697
|
};
|
|
698
|
+
/**
|
|
699
|
+
* Get base URL for API endpoints
|
|
700
|
+
* Returns the configured base URL (default: https://localhost:7237/api)
|
|
701
|
+
*/
|
|
702
|
+
AiAgentService.prototype.getBaseUrl = function () {
|
|
703
|
+
return this.config.apiUrl || 'https://localhost:7237/api';
|
|
704
|
+
};
|
|
705
|
+
/**
|
|
706
|
+
* Get common HTTP headers with optional API key
|
|
707
|
+
*/
|
|
708
|
+
AiAgentService.prototype.getHeaders = function () {
|
|
709
|
+
return new i1.HttpHeaders(Object.assign({ 'Content-Type': 'application/json' }, (this.config.appKey && { 'x-api-key': this.config.appKey })));
|
|
710
|
+
};
|
|
697
711
|
AiAgentService.prototype.testConfiguration = function () {
|
|
698
712
|
// For development purposes, simulate a successful connection
|
|
699
713
|
// when the backend is not available
|
|
700
|
-
|
|
714
|
+
var url = this.getBaseUrl() + "/AIAgent/TestConfiguration";
|
|
715
|
+
console.log('Testing configuration at:', url);
|
|
701
716
|
var headers = new i1.HttpHeaders(Object.assign({ 'Content-Type': 'application/json' }, (this.config.appKey && { 'x-api-key': this.config.appKey })));
|
|
702
|
-
return this.http.get(
|
|
717
|
+
return this.http.get(url, { headers: headers })
|
|
703
718
|
.pipe(operators.timeout(this.config.timeout || 30000), operators.catchError(function (error) {
|
|
704
719
|
console.log('Backend API not available, running in demo mode:', error);
|
|
705
720
|
// Return a mock successful response for demo purposes
|
|
@@ -710,10 +725,9 @@
|
|
|
710
725
|
}));
|
|
711
726
|
};
|
|
712
727
|
AiAgentService.prototype.processInquiry = function (request) {
|
|
728
|
+
var url = this.getBaseUrl() + "/AIAgent/ProcessInquiry";
|
|
713
729
|
var headers = new i1.HttpHeaders(Object.assign({ 'Content-Type': 'application/json' }, (this.config.appKey && { 'x-api-key': this.config.appKey })));
|
|
714
|
-
|
|
715
|
-
var cleanedRequest = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (request.query && { query: request.query })), (request.conversationId && { conversationId: String(request.conversationId) })), (request.userId && { userId: request.userId })), (request.userName && { userName: request.userName })), (request.appSource && { appSource: request.appSource }));
|
|
716
|
-
return this.http.post(this.config.apiUrl + "/ProcessInquiry", cleanedRequest, { headers: headers })
|
|
730
|
+
return this.http.post(url, request, { headers: headers })
|
|
717
731
|
.pipe(operators.timeout(this.config.timeout || 30000), operators.catchError(function (error) {
|
|
718
732
|
console.log('ProcessInquiry API not available, returning demo response:', error);
|
|
719
733
|
// Return a mock response for demo purposes
|
|
@@ -753,6 +767,226 @@
|
|
|
753
767
|
error: errorMessage
|
|
754
768
|
});
|
|
755
769
|
};
|
|
770
|
+
// ============= Conversation Lifecycle API Methods =============
|
|
771
|
+
/**
|
|
772
|
+
* 1. Create a new conversation or reuse existing empty one
|
|
773
|
+
* Smart behavior (forceNew=false): Reuses existing empty conversation if available
|
|
774
|
+
* Explicit behavior (forceNew=true): Always creates a new conversation
|
|
775
|
+
* Should be called before sending the first message
|
|
776
|
+
* @param request - Conversation creation request
|
|
777
|
+
* @param forceNew - If true, always creates new conversation (default: false)
|
|
778
|
+
* @returns Observable of ConversationDto with conversationId
|
|
779
|
+
*/
|
|
780
|
+
AiAgentService.prototype.createConversation = function (request, forceNew) {
|
|
781
|
+
var _this = this;
|
|
782
|
+
if (forceNew === void 0) { forceNew = false; }
|
|
783
|
+
if (!this.config.enableConversationLifecycle) {
|
|
784
|
+
console.warn('Conversation lifecycle is disabled. Enable it in config.');
|
|
785
|
+
return rxjs.throwError({ error: 'Conversation lifecycle is disabled' });
|
|
786
|
+
}
|
|
787
|
+
var url = this.getBaseUrl() + "/conversations?forceNew=" + forceNew;
|
|
788
|
+
console.log('Creating/reusing conversation at:', url, '(forceNew:', forceNew, ')');
|
|
789
|
+
return this.http.post(url, request, { headers: this.getHeaders() })
|
|
790
|
+
.pipe(operators.timeout(this.config.timeout || 30000), operators.catchError(function (error) {
|
|
791
|
+
console.log('CreateConversation API not available, returning mock response:', error);
|
|
792
|
+
// Demo mode: Return mock conversation
|
|
793
|
+
var mockConversation = {
|
|
794
|
+
conversationId: _this.generateGuid(),
|
|
795
|
+
userId: request.userId,
|
|
796
|
+
userName: request.userName,
|
|
797
|
+
status: 'Active',
|
|
798
|
+
source: request.source || 'Web',
|
|
799
|
+
startedDate: new Date(),
|
|
800
|
+
lastActivityDate: new Date(),
|
|
801
|
+
messageCount: 0,
|
|
802
|
+
userMessageCount: 0,
|
|
803
|
+
aiMessageCount: 0,
|
|
804
|
+
isActive: true,
|
|
805
|
+
minutesUntilTimeout: 1440,
|
|
806
|
+
context: request.context
|
|
807
|
+
};
|
|
808
|
+
return new rxjs.Observable(function (observer) {
|
|
809
|
+
setTimeout(function () {
|
|
810
|
+
observer.next(mockConversation);
|
|
811
|
+
observer.complete();
|
|
812
|
+
}, 500);
|
|
813
|
+
});
|
|
814
|
+
}));
|
|
815
|
+
};
|
|
816
|
+
/**
|
|
817
|
+
* 2. Get conversation details by ID
|
|
818
|
+
* @param conversationId - The conversation ID to retrieve
|
|
819
|
+
*/
|
|
820
|
+
AiAgentService.prototype.getConversation = function (conversationId) {
|
|
821
|
+
if (!this.config.enableConversationLifecycle) {
|
|
822
|
+
return rxjs.throwError({ error: 'Conversation lifecycle is disabled' });
|
|
823
|
+
}
|
|
824
|
+
var url = this.getBaseUrl() + "/conversations/" + conversationId;
|
|
825
|
+
console.log('Getting conversation at:', url);
|
|
826
|
+
return this.http.get(url, { headers: this.getHeaders() })
|
|
827
|
+
.pipe(operators.timeout(this.config.timeout || 30000), operators.catchError(function (error) {
|
|
828
|
+
if (error.status === 404) {
|
|
829
|
+
console.error('Conversation not found:', conversationId);
|
|
830
|
+
return rxjs.throwError({ error: 'Conversation not found', status: 404 });
|
|
831
|
+
}
|
|
832
|
+
console.log('GetConversation API not available:', error);
|
|
833
|
+
return rxjs.throwError({ error: 'API not available (Demo Mode)', status: 503 });
|
|
834
|
+
}));
|
|
835
|
+
};
|
|
836
|
+
/**
|
|
837
|
+
* 3. Close a conversation (idempotent)
|
|
838
|
+
* @param conversationId - The conversation ID to close
|
|
839
|
+
* @param request - Optional close details (reason, rating, feedback)
|
|
840
|
+
*/
|
|
841
|
+
AiAgentService.prototype.closeConversation = function (conversationId, request) {
|
|
842
|
+
if (!this.config.enableConversationLifecycle) {
|
|
843
|
+
return rxjs.throwError({ error: 'Conversation lifecycle is disabled' });
|
|
844
|
+
}
|
|
845
|
+
var url = this.getBaseUrl() + "/conversations/" + conversationId + "/close";
|
|
846
|
+
console.log('Closing conversation at:', url);
|
|
847
|
+
return this.http.post(url, request || {}, { headers: this.getHeaders() })
|
|
848
|
+
.pipe(operators.timeout(this.config.timeout || 30000), operators.catchError(function (error) {
|
|
849
|
+
console.log('CloseConversation API not available:', error);
|
|
850
|
+
// Demo mode: Return mock closed conversation
|
|
851
|
+
var mockClosed = {
|
|
852
|
+
conversationId: conversationId,
|
|
853
|
+
userId: '',
|
|
854
|
+
status: 'Completed',
|
|
855
|
+
startedDate: new Date(),
|
|
856
|
+
lastActivityDate: new Date(),
|
|
857
|
+
endedDate: new Date(),
|
|
858
|
+
messageCount: 0,
|
|
859
|
+
userMessageCount: 0,
|
|
860
|
+
aiMessageCount: 0,
|
|
861
|
+
isActive: false,
|
|
862
|
+
minutesUntilTimeout: 0
|
|
863
|
+
};
|
|
864
|
+
return new rxjs.Observable(function (observer) {
|
|
865
|
+
setTimeout(function () {
|
|
866
|
+
observer.next(mockClosed);
|
|
867
|
+
observer.complete();
|
|
868
|
+
}, 500);
|
|
869
|
+
});
|
|
870
|
+
}));
|
|
871
|
+
};
|
|
872
|
+
/**
|
|
873
|
+
* 4. Abandon a conversation (e.g., browser close without explicit close)
|
|
874
|
+
* @param conversationId - The conversation ID to abandon
|
|
875
|
+
*/
|
|
876
|
+
AiAgentService.prototype.abandonConversation = function (conversationId) {
|
|
877
|
+
if (!this.config.enableConversationLifecycle) {
|
|
878
|
+
return rxjs.throwError({ error: 'Conversation lifecycle is disabled' });
|
|
879
|
+
}
|
|
880
|
+
var url = this.getBaseUrl() + "/conversations/" + conversationId + "/abandon";
|
|
881
|
+
console.log('Abandoning conversation at:', url);
|
|
882
|
+
return this.http.post(url, {}, { headers: this.getHeaders() })
|
|
883
|
+
.pipe(operators.timeout(this.config.timeout || 30000), operators.catchError(function (error) {
|
|
884
|
+
console.log('AbandonConversation API not available:', error);
|
|
885
|
+
// Silently succeed in demo mode
|
|
886
|
+
return new rxjs.Observable(function (observer) {
|
|
887
|
+
observer.next();
|
|
888
|
+
observer.complete();
|
|
889
|
+
});
|
|
890
|
+
}));
|
|
891
|
+
};
|
|
892
|
+
/**
|
|
893
|
+
* 5. Recover active conversations after page refresh
|
|
894
|
+
* @param userId - User ID to recover conversations for
|
|
895
|
+
*/
|
|
896
|
+
AiAgentService.prototype.recoverConversations = function (userId) {
|
|
897
|
+
if (!this.config.enableConversationLifecycle) {
|
|
898
|
+
return rxjs.throwError({ error: 'Conversation lifecycle is disabled' });
|
|
899
|
+
}
|
|
900
|
+
var url = this.getBaseUrl() + "/conversations/recover?userId=" + encodeURIComponent(userId);
|
|
901
|
+
console.log('Recovering conversations at:', url);
|
|
902
|
+
return this.http.get(url, { headers: this.getHeaders() })
|
|
903
|
+
.pipe(operators.timeout(this.config.timeout || 30000), operators.catchError(function (error) {
|
|
904
|
+
console.log('RecoverConversations API not available:', error);
|
|
905
|
+
// Demo mode: Return no active conversations
|
|
906
|
+
var mockRecovery = {
|
|
907
|
+
hasActiveConversations: false,
|
|
908
|
+
activeConversations: []
|
|
909
|
+
};
|
|
910
|
+
return new rxjs.Observable(function (observer) {
|
|
911
|
+
setTimeout(function () {
|
|
912
|
+
observer.next(mockRecovery);
|
|
913
|
+
observer.complete();
|
|
914
|
+
}, 500);
|
|
915
|
+
});
|
|
916
|
+
}));
|
|
917
|
+
};
|
|
918
|
+
/**
|
|
919
|
+
* 6. Resume a specific conversation with validation
|
|
920
|
+
* @param conversationId - The conversation ID to resume
|
|
921
|
+
* @param userId - User ID for ownership validation
|
|
922
|
+
*/
|
|
923
|
+
AiAgentService.prototype.resumeConversation = function (conversationId, userId) {
|
|
924
|
+
if (!this.config.enableConversationLifecycle) {
|
|
925
|
+
return rxjs.throwError({ error: 'Conversation lifecycle is disabled' });
|
|
926
|
+
}
|
|
927
|
+
var url = this.getBaseUrl() + "/conversations/" + conversationId + "/resume?userId=" + encodeURIComponent(userId);
|
|
928
|
+
console.log('Resuming conversation at:', url);
|
|
929
|
+
return this.http.post(url, {}, { headers: this.getHeaders() })
|
|
930
|
+
.pipe(operators.timeout(this.config.timeout || 30000), operators.catchError(function (error) {
|
|
931
|
+
// Pass through specific error codes
|
|
932
|
+
if (error.status === 410) {
|
|
933
|
+
console.error('Conversation expired (>24 hours):', conversationId);
|
|
934
|
+
return rxjs.throwError({ error: 'Conversation expired', status: 410 });
|
|
935
|
+
}
|
|
936
|
+
if (error.status === 403) {
|
|
937
|
+
console.error('User does not own conversation:', conversationId);
|
|
938
|
+
return rxjs.throwError({ error: 'Unauthorized', status: 403 });
|
|
939
|
+
}
|
|
940
|
+
if (error.status === 404) {
|
|
941
|
+
console.error('Conversation not found:', conversationId);
|
|
942
|
+
return rxjs.throwError({ error: 'Conversation not found', status: 404 });
|
|
943
|
+
}
|
|
944
|
+
console.log('ResumeConversation API not available:', error);
|
|
945
|
+
return rxjs.throwError({ error: 'API not available (Demo Mode)', status: 503 });
|
|
946
|
+
}));
|
|
947
|
+
};
|
|
948
|
+
/**
|
|
949
|
+
* 7. Get conversation history with pagination
|
|
950
|
+
* @param userId - User ID to get history for
|
|
951
|
+
* @param skip - Number of records to skip (default: 0)
|
|
952
|
+
* @param take - Number of records to return (default: 20, max: 100)
|
|
953
|
+
*/
|
|
954
|
+
AiAgentService.prototype.getConversationHistory = function (userId, skip, take) {
|
|
955
|
+
if (skip === void 0) { skip = 0; }
|
|
956
|
+
if (take === void 0) { take = 20; }
|
|
957
|
+
if (!this.config.enableConversationLifecycle) {
|
|
958
|
+
return rxjs.throwError({ error: 'Conversation lifecycle is disabled' });
|
|
959
|
+
}
|
|
960
|
+
var url = this.getBaseUrl() + "/conversations/history?userId=" + encodeURIComponent(userId) + "&skip=" + skip + "&take=" + take;
|
|
961
|
+
console.log('Getting conversation history at:', url);
|
|
962
|
+
return this.http.get(url, { headers: this.getHeaders() })
|
|
963
|
+
.pipe(operators.timeout(this.config.timeout || 30000), operators.catchError(function (error) {
|
|
964
|
+
console.log('GetConversationHistory API not available:', error);
|
|
965
|
+
// Demo mode: Return empty history
|
|
966
|
+
var mockHistory = {
|
|
967
|
+
conversations: [],
|
|
968
|
+
skip: skip,
|
|
969
|
+
take: take,
|
|
970
|
+
hasMore: false
|
|
971
|
+
};
|
|
972
|
+
return new rxjs.Observable(function (observer) {
|
|
973
|
+
setTimeout(function () {
|
|
974
|
+
observer.next(mockHistory);
|
|
975
|
+
observer.complete();
|
|
976
|
+
}, 500);
|
|
977
|
+
});
|
|
978
|
+
}));
|
|
979
|
+
};
|
|
980
|
+
/**
|
|
981
|
+
* Generate a GUID for mock conversation IDs
|
|
982
|
+
*/
|
|
983
|
+
AiAgentService.prototype.generateGuid = function () {
|
|
984
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
985
|
+
var r = Math.random() * 16 | 0;
|
|
986
|
+
var v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
987
|
+
return v.toString(16);
|
|
988
|
+
});
|
|
989
|
+
};
|
|
756
990
|
return AiAgentService;
|
|
757
991
|
}());
|
|
758
992
|
AiAgentService.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: AiAgentService, deps: [{ token: i1__namespace.HttpClient }], target: i0__namespace.ɵɵFactoryTarget.Injectable });
|
|
@@ -764,9 +998,367 @@
|
|
|
764
998
|
}]
|
|
765
999
|
}], ctorParameters: function () { return [{ type: i1__namespace.HttpClient }]; } });
|
|
766
1000
|
|
|
1001
|
+
var ConversationStateService = /** @class */ (function () {
|
|
1002
|
+
function ConversationStateService(aiAgentService) {
|
|
1003
|
+
this.aiAgentService = aiAgentService;
|
|
1004
|
+
this.defaultConfig = {
|
|
1005
|
+
storageType: 'session',
|
|
1006
|
+
storageKey: 'maestro-conversation-state',
|
|
1007
|
+
enableAutoRecovery: true,
|
|
1008
|
+
enablePageUnloadHandler: true,
|
|
1009
|
+
appSource: 'Emanate'
|
|
1010
|
+
};
|
|
1011
|
+
this.config = Object.assign({}, this.defaultConfig);
|
|
1012
|
+
// State observables
|
|
1013
|
+
this.conversationStateSubject = new rxjs.BehaviorSubject(this.getEmptyState());
|
|
1014
|
+
this.conversationState$ = this.conversationStateSubject.asObservable();
|
|
1015
|
+
// Track if page unload handler is registered
|
|
1016
|
+
this.pageUnloadHandlerRegistered = false;
|
|
1017
|
+
// Auto-initialize if configured
|
|
1018
|
+
if (this.config.enableAutoRecovery) {
|
|
1019
|
+
this.initializeFromStorage();
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Configure the conversation state service
|
|
1024
|
+
*/
|
|
1025
|
+
ConversationStateService.prototype.configure = function (config) {
|
|
1026
|
+
this.config = Object.assign(Object.assign({}, this.defaultConfig), config);
|
|
1027
|
+
// Re-initialize from storage with new config
|
|
1028
|
+
if (this.config.enableAutoRecovery) {
|
|
1029
|
+
this.initializeFromStorage();
|
|
1030
|
+
}
|
|
1031
|
+
// Setup page unload handler if enabled
|
|
1032
|
+
if (this.config.enablePageUnloadHandler && !this.pageUnloadHandlerRegistered) {
|
|
1033
|
+
this.setupPageUnloadHandler();
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
/**
|
|
1037
|
+
* Get the current conversation state
|
|
1038
|
+
*/
|
|
1039
|
+
ConversationStateService.prototype.getCurrentState = function () {
|
|
1040
|
+
return this.conversationStateSubject.value;
|
|
1041
|
+
};
|
|
1042
|
+
/**
|
|
1043
|
+
* Get the active conversation ID (if any)
|
|
1044
|
+
*/
|
|
1045
|
+
ConversationStateService.prototype.getActiveConversationId = function () {
|
|
1046
|
+
return this.conversationStateSubject.value.conversationId;
|
|
1047
|
+
};
|
|
1048
|
+
/**
|
|
1049
|
+
* Initialize state from storage
|
|
1050
|
+
*/
|
|
1051
|
+
ConversationStateService.prototype.initializeFromStorage = function () {
|
|
1052
|
+
var stored = this.getFromStorage();
|
|
1053
|
+
if (stored) {
|
|
1054
|
+
console.log('Initialized conversation state from storage:', stored.conversationId);
|
|
1055
|
+
this.conversationStateSubject.next(stored);
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
/**
|
|
1059
|
+
* Start a new conversation (with smart reuse by default)
|
|
1060
|
+
* By default (forceNew=false): Reuses existing empty conversation if available
|
|
1061
|
+
* With forceNew=true: Always creates a new conversation
|
|
1062
|
+
* Creates conversation on backend and stores state
|
|
1063
|
+
* @param userId - User ID
|
|
1064
|
+
* @param userName - User name
|
|
1065
|
+
* @param context - Optional conversation context
|
|
1066
|
+
* @param forceNew - If true, always creates new conversation (default: false)
|
|
1067
|
+
*/
|
|
1068
|
+
ConversationStateService.prototype.startNewConversation = function (userId, userName, context, forceNew) {
|
|
1069
|
+
var _this = this;
|
|
1070
|
+
if (forceNew === void 0) { forceNew = false; }
|
|
1071
|
+
var request = {
|
|
1072
|
+
userId: userId,
|
|
1073
|
+
userName: userName,
|
|
1074
|
+
source: this.config.appSource,
|
|
1075
|
+
context: context
|
|
1076
|
+
};
|
|
1077
|
+
return this.aiAgentService.createConversation(request, forceNew).pipe(operators.tap(function (conversation) {
|
|
1078
|
+
var state = {
|
|
1079
|
+
conversationId: conversation.conversationId,
|
|
1080
|
+
userId: conversation.userId,
|
|
1081
|
+
userName: conversation.userName || null,
|
|
1082
|
+
status: conversation.status,
|
|
1083
|
+
startedDate: conversation.startedDate,
|
|
1084
|
+
lastActivityDate: conversation.lastActivityDate,
|
|
1085
|
+
isActive: conversation.isActive
|
|
1086
|
+
};
|
|
1087
|
+
_this.setActiveConversation(state);
|
|
1088
|
+
console.log('New conversation started:', conversation.conversationId);
|
|
1089
|
+
}), operators.map(function (conversation) { return conversation.conversationId; }), operators.catchError(function (error) {
|
|
1090
|
+
console.error('Failed to start conversation:', error);
|
|
1091
|
+
return rxjs.throwError(error);
|
|
1092
|
+
}));
|
|
1093
|
+
};
|
|
1094
|
+
/**
|
|
1095
|
+
* Recover conversation state after page refresh
|
|
1096
|
+
* Attempts to resume the most recent active conversation
|
|
1097
|
+
*/
|
|
1098
|
+
ConversationStateService.prototype.recoverState = function (userId) {
|
|
1099
|
+
var _this = this;
|
|
1100
|
+
// First, check local storage for existing conversationId
|
|
1101
|
+
var storedState = this.getFromStorage();
|
|
1102
|
+
if (storedState && storedState.conversationId && storedState.userId === userId) {
|
|
1103
|
+
// Try to resume the stored conversation
|
|
1104
|
+
console.log('Attempting to resume stored conversation:', storedState.conversationId);
|
|
1105
|
+
return this.aiAgentService.resumeConversation(storedState.conversationId, userId).pipe(operators.tap(function (conversation) {
|
|
1106
|
+
var state = {
|
|
1107
|
+
conversationId: conversation.conversationId,
|
|
1108
|
+
userId: conversation.userId,
|
|
1109
|
+
userName: conversation.userName || null,
|
|
1110
|
+
status: conversation.status,
|
|
1111
|
+
startedDate: conversation.startedDate,
|
|
1112
|
+
lastActivityDate: conversation.lastActivityDate,
|
|
1113
|
+
isActive: conversation.isActive
|
|
1114
|
+
};
|
|
1115
|
+
_this.setActiveConversation(state);
|
|
1116
|
+
console.log('Successfully resumed conversation:', conversation.conversationId);
|
|
1117
|
+
}), operators.map(function (conversation) { return conversation.conversationId; }), operators.catchError(function (error) {
|
|
1118
|
+
console.error('Failed to resume stored conversation:', error);
|
|
1119
|
+
// If stored conversation is expired/invalid, clear it and try recovery
|
|
1120
|
+
_this.clearActiveConversation();
|
|
1121
|
+
return _this.recoverFromBackend(userId);
|
|
1122
|
+
}));
|
|
1123
|
+
}
|
|
1124
|
+
// No stored conversation, try backend recovery
|
|
1125
|
+
return this.recoverFromBackend(userId);
|
|
1126
|
+
};
|
|
1127
|
+
/**
|
|
1128
|
+
* Recover from backend using the recovery endpoint
|
|
1129
|
+
*/
|
|
1130
|
+
ConversationStateService.prototype.recoverFromBackend = function (userId) {
|
|
1131
|
+
var _this = this;
|
|
1132
|
+
return this.aiAgentService.recoverConversations(userId).pipe(operators.map(function (recovery) {
|
|
1133
|
+
if (recovery.hasActiveConversations && recovery.activeConversations.length > 0) {
|
|
1134
|
+
// Use suggested conversation or first active conversation
|
|
1135
|
+
var conversation = recovery.suggestedConversation
|
|
1136
|
+
? recovery.activeConversations.find(function (c) { var _a; return c.conversationId === ((_a = recovery.suggestedConversation) === null || _a === void 0 ? void 0 : _a.conversationId); })
|
|
1137
|
+
: recovery.activeConversations[0];
|
|
1138
|
+
if (conversation) {
|
|
1139
|
+
var state = {
|
|
1140
|
+
conversationId: conversation.conversationId,
|
|
1141
|
+
userId: conversation.userId,
|
|
1142
|
+
userName: conversation.userName || null,
|
|
1143
|
+
status: conversation.status,
|
|
1144
|
+
startedDate: conversation.startedDate,
|
|
1145
|
+
lastActivityDate: conversation.lastActivityDate,
|
|
1146
|
+
isActive: conversation.isActive
|
|
1147
|
+
};
|
|
1148
|
+
_this.setActiveConversation(state);
|
|
1149
|
+
console.log('Recovered conversation from backend:', conversation.conversationId);
|
|
1150
|
+
return conversation.conversationId;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
console.log('No active conversations found for recovery');
|
|
1154
|
+
return null;
|
|
1155
|
+
}), operators.catchError(function (error) {
|
|
1156
|
+
console.error('Failed to recover conversations from backend:', error);
|
|
1157
|
+
return rxjs.of(null);
|
|
1158
|
+
}));
|
|
1159
|
+
};
|
|
1160
|
+
/**
|
|
1161
|
+
* Explicitly start a new conversation session
|
|
1162
|
+
* Always creates a new conversation (forceNew=true)
|
|
1163
|
+
* Use this when user clicks "New Conversation" button
|
|
1164
|
+
* @param userId - User ID
|
|
1165
|
+
* @param userName - User name
|
|
1166
|
+
* @param context - Optional conversation context
|
|
1167
|
+
*/
|
|
1168
|
+
ConversationStateService.prototype.startNewConversationExplicit = function (userId, userName, context) {
|
|
1169
|
+
console.log('User explicitly requested new conversation');
|
|
1170
|
+
return this.startNewConversation(userId, userName, context, true);
|
|
1171
|
+
};
|
|
1172
|
+
/**
|
|
1173
|
+
* Check if current conversation is empty (no messages, no context)
|
|
1174
|
+
* An empty conversation can be safely reused
|
|
1175
|
+
*/
|
|
1176
|
+
ConversationStateService.prototype.isCurrentConversationEmpty = function () {
|
|
1177
|
+
var currentState = this.getCurrentState();
|
|
1178
|
+
// Consider conversation empty if it has no conversationId or is not active
|
|
1179
|
+
// Note: We can't check message count from state service alone
|
|
1180
|
+
// This should be checked by the component level
|
|
1181
|
+
return !currentState.conversationId || !currentState.isActive;
|
|
1182
|
+
};
|
|
1183
|
+
/**
|
|
1184
|
+
* Close the current conversation
|
|
1185
|
+
*/
|
|
1186
|
+
ConversationStateService.prototype.closeCurrentConversation = function (reason, satisfactionRating, userFeedback) {
|
|
1187
|
+
var _this = this;
|
|
1188
|
+
var currentState = this.getCurrentState();
|
|
1189
|
+
if (!currentState.conversationId) {
|
|
1190
|
+
console.warn('No active conversation to close');
|
|
1191
|
+
return rxjs.of(void 0);
|
|
1192
|
+
}
|
|
1193
|
+
return this.aiAgentService.closeConversation(currentState.conversationId, { reason: reason, satisfactionRating: satisfactionRating, userFeedback: userFeedback }).pipe(operators.tap(function () {
|
|
1194
|
+
console.log('Conversation closed:', currentState.conversationId);
|
|
1195
|
+
_this.clearActiveConversation();
|
|
1196
|
+
}), operators.map(function () { return void 0; }), operators.catchError(function (error) {
|
|
1197
|
+
console.error('Failed to close conversation:', error);
|
|
1198
|
+
// Still clear local state even if backend call fails
|
|
1199
|
+
_this.clearActiveConversation();
|
|
1200
|
+
return rxjs.throwError(error);
|
|
1201
|
+
}));
|
|
1202
|
+
};
|
|
1203
|
+
/**
|
|
1204
|
+
* Abandon the current conversation (e.g., on page unload)
|
|
1205
|
+
*/
|
|
1206
|
+
ConversationStateService.prototype.abandonCurrentConversation = function () {
|
|
1207
|
+
var currentState = this.getCurrentState();
|
|
1208
|
+
if (!currentState.conversationId) {
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
console.log('Abandoning conversation:', currentState.conversationId);
|
|
1212
|
+
// Use sendBeacon for reliable fire-and-forget on page unload
|
|
1213
|
+
var baseUrl = this.aiAgentService['getBaseUrl']();
|
|
1214
|
+
var url = baseUrl + "/conversations/" + currentState.conversationId + "/abandon";
|
|
1215
|
+
try {
|
|
1216
|
+
// Use sendBeacon if available (more reliable during page unload)
|
|
1217
|
+
if (navigator.sendBeacon) {
|
|
1218
|
+
var blob = new Blob([JSON.stringify({})], { type: 'application/json' });
|
|
1219
|
+
navigator.sendBeacon(url, blob);
|
|
1220
|
+
}
|
|
1221
|
+
else {
|
|
1222
|
+
// Fallback: synchronous XHR (not recommended but necessary for older browsers)
|
|
1223
|
+
var xhr = new XMLHttpRequest();
|
|
1224
|
+
xhr.open('POST', url, false); // Synchronous
|
|
1225
|
+
xhr.setRequestHeader('Content-Type', 'application/json');
|
|
1226
|
+
xhr.send(JSON.stringify({}));
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
catch (error) {
|
|
1230
|
+
console.error('Failed to abandon conversation:', error);
|
|
1231
|
+
}
|
|
1232
|
+
finally {
|
|
1233
|
+
this.clearActiveConversation();
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
/**
|
|
1237
|
+
* Set active conversation state and persist to storage
|
|
1238
|
+
*/
|
|
1239
|
+
ConversationStateService.prototype.setActiveConversation = function (state) {
|
|
1240
|
+
this.conversationStateSubject.next(state);
|
|
1241
|
+
this.saveToStorage(state);
|
|
1242
|
+
};
|
|
1243
|
+
/**
|
|
1244
|
+
* Clear active conversation state
|
|
1245
|
+
*/
|
|
1246
|
+
ConversationStateService.prototype.clearActiveConversation = function () {
|
|
1247
|
+
console.log('Clearing active conversation state');
|
|
1248
|
+
this.conversationStateSubject.next(this.getEmptyState());
|
|
1249
|
+
this.removeFromStorage();
|
|
1250
|
+
};
|
|
1251
|
+
/**
|
|
1252
|
+
* Update last activity timestamp
|
|
1253
|
+
*/
|
|
1254
|
+
ConversationStateService.prototype.updateLastActivity = function () {
|
|
1255
|
+
var currentState = this.getCurrentState();
|
|
1256
|
+
if (currentState.conversationId) {
|
|
1257
|
+
var updatedState = Object.assign(Object.assign({}, currentState), { lastActivityDate: new Date() });
|
|
1258
|
+
this.setActiveConversation(updatedState);
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
/**
|
|
1262
|
+
* Setup page unload handler to abandon conversation
|
|
1263
|
+
*/
|
|
1264
|
+
ConversationStateService.prototype.setupPageUnloadHandler = function () {
|
|
1265
|
+
var _this = this;
|
|
1266
|
+
if (this.pageUnloadHandlerRegistered) {
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
window.addEventListener('beforeunload', function () {
|
|
1270
|
+
_this.abandonCurrentConversation();
|
|
1271
|
+
});
|
|
1272
|
+
this.pageUnloadHandlerRegistered = true;
|
|
1273
|
+
console.log('Page unload handler registered for conversation abandonment');
|
|
1274
|
+
};
|
|
1275
|
+
/**
|
|
1276
|
+
* Storage operations
|
|
1277
|
+
*/
|
|
1278
|
+
ConversationStateService.prototype.getStorage = function () {
|
|
1279
|
+
switch (this.config.storageType) {
|
|
1280
|
+
case 'session':
|
|
1281
|
+
return sessionStorage;
|
|
1282
|
+
case 'local':
|
|
1283
|
+
return localStorage;
|
|
1284
|
+
case 'none':
|
|
1285
|
+
return null;
|
|
1286
|
+
default:
|
|
1287
|
+
return sessionStorage;
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
ConversationStateService.prototype.saveToStorage = function (state) {
|
|
1291
|
+
var storage = this.getStorage();
|
|
1292
|
+
if (storage && state.conversationId) {
|
|
1293
|
+
try {
|
|
1294
|
+
storage.setItem(this.config.storageKey, JSON.stringify(state));
|
|
1295
|
+
}
|
|
1296
|
+
catch (error) {
|
|
1297
|
+
console.error('Failed to save conversation state to storage:', error);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
ConversationStateService.prototype.getFromStorage = function () {
|
|
1302
|
+
var storage = this.getStorage();
|
|
1303
|
+
if (!storage) {
|
|
1304
|
+
return null;
|
|
1305
|
+
}
|
|
1306
|
+
try {
|
|
1307
|
+
var stored = storage.getItem(this.config.storageKey);
|
|
1308
|
+
if (stored) {
|
|
1309
|
+
var state = JSON.parse(stored);
|
|
1310
|
+
// Parse date strings back to Date objects
|
|
1311
|
+
if (state.startedDate) {
|
|
1312
|
+
state.startedDate = new Date(state.startedDate);
|
|
1313
|
+
}
|
|
1314
|
+
if (state.lastActivityDate) {
|
|
1315
|
+
state.lastActivityDate = new Date(state.lastActivityDate);
|
|
1316
|
+
}
|
|
1317
|
+
return state;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
catch (error) {
|
|
1321
|
+
console.error('Failed to retrieve conversation state from storage:', error);
|
|
1322
|
+
}
|
|
1323
|
+
return null;
|
|
1324
|
+
};
|
|
1325
|
+
ConversationStateService.prototype.removeFromStorage = function () {
|
|
1326
|
+
var storage = this.getStorage();
|
|
1327
|
+
if (storage) {
|
|
1328
|
+
try {
|
|
1329
|
+
storage.removeItem(this.config.storageKey);
|
|
1330
|
+
}
|
|
1331
|
+
catch (error) {
|
|
1332
|
+
console.error('Failed to remove conversation state from storage:', error);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
ConversationStateService.prototype.getEmptyState = function () {
|
|
1337
|
+
return {
|
|
1338
|
+
conversationId: null,
|
|
1339
|
+
userId: null,
|
|
1340
|
+
userName: null,
|
|
1341
|
+
status: null,
|
|
1342
|
+
startedDate: null,
|
|
1343
|
+
lastActivityDate: null,
|
|
1344
|
+
isActive: false
|
|
1345
|
+
};
|
|
1346
|
+
};
|
|
1347
|
+
return ConversationStateService;
|
|
1348
|
+
}());
|
|
1349
|
+
ConversationStateService.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: ConversationStateService, deps: [{ token: AiAgentService }], target: i0__namespace.ɵɵFactoryTarget.Injectable });
|
|
1350
|
+
ConversationStateService.ɵprov = i0__namespace.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: ConversationStateService, providedIn: 'root' });
|
|
1351
|
+
i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: ConversationStateService, decorators: [{
|
|
1352
|
+
type: i0.Injectable,
|
|
1353
|
+
args: [{
|
|
1354
|
+
providedIn: 'root'
|
|
1355
|
+
}]
|
|
1356
|
+
}], ctorParameters: function () { return [{ type: AiAgentService }]; } });
|
|
1357
|
+
|
|
767
1358
|
var AiChatComponent = /** @class */ (function () {
|
|
768
|
-
function AiChatComponent(aiAgentService, sanitizer) {
|
|
1359
|
+
function AiChatComponent(aiAgentService, conversationStateService, sanitizer) {
|
|
769
1360
|
this.aiAgentService = aiAgentService;
|
|
1361
|
+
this.conversationStateService = conversationStateService;
|
|
770
1362
|
this.sanitizer = sanitizer;
|
|
771
1363
|
this.title = 'Chat with Maestro';
|
|
772
1364
|
this.placeholder = 'Type your inquiry here... (Press Enter to send, Shift+Enter for new line)';
|
|
@@ -777,7 +1369,14 @@
|
|
|
777
1369
|
this.enableAnimations = true; // Enable/disable all animations
|
|
778
1370
|
this.animationSpeed = 'normal'; // Animation speed control
|
|
779
1371
|
this.enableStatusTransitions = true; // Enable/disable status indicator transitions
|
|
1372
|
+
this.enableConversationLifecycle = true; // Enable new conversation lifecycle features
|
|
1373
|
+
this.enableAutoConversationRecovery = true; // Auto-recover conversation on page load
|
|
1374
|
+
this.showCloseButton = false; // Show close conversation button in header (disabled by default)
|
|
1375
|
+
this.showNewConversationButton = true; // Show new conversation button in header
|
|
1376
|
+
this.confirmNewConversation = true; // Show confirmation dialog before starting new conversation
|
|
1377
|
+
this.newConversationConfirmMessage = 'Are you sure you want to start a new conversation? Your current chat will be saved in history.'; // Confirmation message for new conversation
|
|
780
1378
|
this.messageReceived = new i0.EventEmitter();
|
|
1379
|
+
this.conversationStarted = new i0.EventEmitter(); // Emits conversationId when new conversation starts
|
|
781
1380
|
this.messageSent = new i0.EventEmitter();
|
|
782
1381
|
this.fileUploaded = new i0.EventEmitter();
|
|
783
1382
|
this.sizeChanged = new i0.EventEmitter();
|
|
@@ -812,9 +1411,14 @@
|
|
|
812
1411
|
// Icon configuration
|
|
813
1412
|
this.icons = getIconSet('feather');
|
|
814
1413
|
this.sanitizedIcons = {};
|
|
1414
|
+
// Lazy initialization flag
|
|
1415
|
+
this.conversationInitialized = false;
|
|
1416
|
+
// Confirmation modal state
|
|
1417
|
+
this.showConfirmationModal = false;
|
|
815
1418
|
this.destroy$ = new rxjs.Subject();
|
|
816
1419
|
}
|
|
817
1420
|
AiChatComponent.prototype.ngOnInit = function () {
|
|
1421
|
+
var _this = this;
|
|
818
1422
|
this.updateStatus('Initializing...');
|
|
819
1423
|
console.log('AI Chat Component initialized');
|
|
820
1424
|
// Load icon set
|
|
@@ -824,24 +1428,47 @@
|
|
|
824
1428
|
if (this.apiUrl || this.appKey) {
|
|
825
1429
|
this.aiAgentService.configure({
|
|
826
1430
|
apiUrl: this.apiUrl,
|
|
827
|
-
appKey: this.appKey
|
|
1431
|
+
appKey: this.appKey,
|
|
1432
|
+
enableConversationLifecycle: this.enableConversationLifecycle
|
|
828
1433
|
});
|
|
829
1434
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
this.
|
|
1435
|
+
// Configure conversation state service
|
|
1436
|
+
if (this.enableConversationLifecycle) {
|
|
1437
|
+
this.conversationStateService.configure({
|
|
1438
|
+
enableAutoRecovery: this.enableAutoConversationRecovery,
|
|
1439
|
+
enablePageUnloadHandler: true,
|
|
1440
|
+
appSource: this.appSource || 'Web'
|
|
1441
|
+
});
|
|
1442
|
+
// Subscribe to conversation state changes
|
|
1443
|
+
this.conversationStateService.conversationState$
|
|
1444
|
+
.pipe(operators.takeUntil(this.destroy$))
|
|
1445
|
+
.subscribe(function (state) {
|
|
1446
|
+
if (state.conversationId && state.conversationId !== _this.conversationId) {
|
|
1447
|
+
console.log('Conversation state updated:', state.conversationId);
|
|
1448
|
+
_this.conversationId = state.conversationId;
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
// Attempt to recover or start new conversation
|
|
1452
|
+
this.initializeConversation();
|
|
1453
|
+
}
|
|
1454
|
+
else {
|
|
1455
|
+
// Legacy mode: just test configuration
|
|
1456
|
+
try {
|
|
1457
|
+
// Load historical messages if provided
|
|
1458
|
+
if (this.historicalMessages && this.historicalMessages.length > 0) {
|
|
1459
|
+
this.loadHistoricalMessages(this.historicalMessages);
|
|
1460
|
+
}
|
|
1461
|
+
else {
|
|
1462
|
+
this.addWelcomeMessage();
|
|
1463
|
+
}
|
|
1464
|
+
this.testConfiguration();
|
|
1465
|
+
this.loadSavedSize();
|
|
1466
|
+
this.applySizePreset(this.currentSize);
|
|
834
1467
|
}
|
|
835
|
-
|
|
836
|
-
|
|
1468
|
+
catch (error) {
|
|
1469
|
+
console.error('Error during component initialization:', error);
|
|
1470
|
+
this.updateStatus('Initialization error');
|
|
837
1471
|
}
|
|
838
|
-
this.testConfiguration();
|
|
839
|
-
this.loadSavedSize();
|
|
840
|
-
this.applySizePreset(this.currentSize);
|
|
841
|
-
}
|
|
842
|
-
catch (error) {
|
|
843
|
-
console.error('Error during component initialization:', error);
|
|
844
|
-
this.updateStatus('Initialization error');
|
|
845
1472
|
}
|
|
846
1473
|
};
|
|
847
1474
|
AiChatComponent.prototype.ngOnChanges = function (changes) {
|
|
@@ -879,6 +1506,15 @@
|
|
|
879
1506
|
this.destroy$.next();
|
|
880
1507
|
this.destroy$.complete();
|
|
881
1508
|
};
|
|
1509
|
+
/**
|
|
1510
|
+
* Handle keyboard events (ESC key to close modal)
|
|
1511
|
+
*/
|
|
1512
|
+
AiChatComponent.prototype.handleEscapeKey = function (event) {
|
|
1513
|
+
if (this.showConfirmationModal) {
|
|
1514
|
+
event.preventDefault();
|
|
1515
|
+
this.cancelConfirmation();
|
|
1516
|
+
}
|
|
1517
|
+
};
|
|
882
1518
|
/**
|
|
883
1519
|
* Get animation duration in milliseconds based on speed setting
|
|
884
1520
|
*/
|
|
@@ -921,6 +1557,234 @@
|
|
|
921
1557
|
}
|
|
922
1558
|
});
|
|
923
1559
|
};
|
|
1560
|
+
/**
|
|
1561
|
+
* Initialize conversation - silent check only (lazy initialization)
|
|
1562
|
+
* Only reads from storage without making API calls or creating conversations
|
|
1563
|
+
* Actual conversation creation is deferred until first message send
|
|
1564
|
+
*/
|
|
1565
|
+
AiChatComponent.prototype.initializeConversation = function () {
|
|
1566
|
+
if (!this.userId) {
|
|
1567
|
+
console.warn('Cannot initialize conversation without userId');
|
|
1568
|
+
this.updateStatus('Ready');
|
|
1569
|
+
this.addWelcomeMessage();
|
|
1570
|
+
this.loadSavedSize();
|
|
1571
|
+
this.applySizePreset(this.currentSize);
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
// Silent check: only read from SessionStorage, no API calls
|
|
1575
|
+
var storedState = this.conversationStateService.getCurrentState();
|
|
1576
|
+
if (storedState && storedState.conversationId && storedState.userId === this.userId) {
|
|
1577
|
+
// Found conversation in storage - will validate on first message send
|
|
1578
|
+
this.conversationId = storedState.conversationId;
|
|
1579
|
+
this.conversationInitialized = true; // Mark as initialized (will validate on use)
|
|
1580
|
+
console.log('Found conversation in storage (will validate on first message):', this.conversationId);
|
|
1581
|
+
this.updateStatus('Ready');
|
|
1582
|
+
// Load historical messages if provided
|
|
1583
|
+
if (this.historicalMessages && this.historicalMessages.length > 0) {
|
|
1584
|
+
this.loadHistoricalMessages(this.historicalMessages);
|
|
1585
|
+
}
|
|
1586
|
+
else {
|
|
1587
|
+
this.addWelcomeMessage();
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
else if (this.conversationId) {
|
|
1591
|
+
// ConversationId provided as input, use it
|
|
1592
|
+
this.conversationInitialized = true;
|
|
1593
|
+
this.updateStatus('Ready');
|
|
1594
|
+
if (this.historicalMessages && this.historicalMessages.length > 0) {
|
|
1595
|
+
this.loadHistoricalMessages(this.historicalMessages);
|
|
1596
|
+
}
|
|
1597
|
+
else {
|
|
1598
|
+
this.addWelcomeMessage();
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
else {
|
|
1602
|
+
// No conversation yet - will be created on first message send
|
|
1603
|
+
console.log('No conversation found - will create on first message');
|
|
1604
|
+
this.updateStatus('Ready');
|
|
1605
|
+
this.addWelcomeMessage();
|
|
1606
|
+
}
|
|
1607
|
+
this.loadSavedSize();
|
|
1608
|
+
this.applySizePreset(this.currentSize);
|
|
1609
|
+
};
|
|
1610
|
+
/**
|
|
1611
|
+
* Ensure conversation is initialized before sending a message
|
|
1612
|
+
* Implements lazy initialization pattern
|
|
1613
|
+
* @returns Observable<boolean> indicating success
|
|
1614
|
+
*/
|
|
1615
|
+
AiChatComponent.prototype.ensureConversationInitialized = function () {
|
|
1616
|
+
var _this = this;
|
|
1617
|
+
// If already initialized with valid conversationId, we're good
|
|
1618
|
+
if (this.conversationInitialized && this.conversationId) {
|
|
1619
|
+
// Validate the conversation still exists if from storage
|
|
1620
|
+
if (this.enableAutoConversationRecovery) {
|
|
1621
|
+
return this.aiAgentService.resumeConversation(this.conversationId, this.userId).pipe(operators.map(function () {
|
|
1622
|
+
console.log('Conversation validated:', _this.conversationId);
|
|
1623
|
+
return true;
|
|
1624
|
+
}), operators.catchError(function (error) {
|
|
1625
|
+
console.warn('Stored conversation invalid, creating new one:', error);
|
|
1626
|
+
// Stored conversation expired/invalid, create new
|
|
1627
|
+
_this.conversationInitialized = false;
|
|
1628
|
+
_this.conversationId = undefined;
|
|
1629
|
+
_this.conversationStateService.clearActiveConversation();
|
|
1630
|
+
return _this.createNewConversationForMessage();
|
|
1631
|
+
}));
|
|
1632
|
+
}
|
|
1633
|
+
return rxjs.of(true);
|
|
1634
|
+
}
|
|
1635
|
+
// Not initialized yet - try to recover or create new
|
|
1636
|
+
if (!this.userId || !this.userName) {
|
|
1637
|
+
console.error('Cannot initialize conversation: userId or userName missing');
|
|
1638
|
+
return rxjs.of(false);
|
|
1639
|
+
}
|
|
1640
|
+
// Try recovery first if enabled
|
|
1641
|
+
if (this.enableAutoConversationRecovery) {
|
|
1642
|
+
return this.conversationStateService.recoverState(this.userId).pipe(operators.switchMap(function (recoveredId) {
|
|
1643
|
+
if (recoveredId) {
|
|
1644
|
+
console.log('Recovered conversation for first message:', recoveredId);
|
|
1645
|
+
_this.conversationId = recoveredId;
|
|
1646
|
+
_this.conversationInitialized = true;
|
|
1647
|
+
_this.conversationStarted.emit(recoveredId);
|
|
1648
|
+
_this.updateStatus('AI Agent is ready');
|
|
1649
|
+
return rxjs.of(true);
|
|
1650
|
+
}
|
|
1651
|
+
else {
|
|
1652
|
+
// No conversation to recover, create new
|
|
1653
|
+
return _this.createNewConversationForMessage();
|
|
1654
|
+
}
|
|
1655
|
+
}), operators.catchError(function (error) {
|
|
1656
|
+
console.error('Recovery failed, creating new conversation:', error);
|
|
1657
|
+
return _this.createNewConversationForMessage();
|
|
1658
|
+
}));
|
|
1659
|
+
}
|
|
1660
|
+
// Recovery disabled, just create new
|
|
1661
|
+
return this.createNewConversationForMessage();
|
|
1662
|
+
};
|
|
1663
|
+
/**
|
|
1664
|
+
* Helper method to create new conversation for message send
|
|
1665
|
+
*/
|
|
1666
|
+
AiChatComponent.prototype.createNewConversationForMessage = function () {
|
|
1667
|
+
var _this = this;
|
|
1668
|
+
this.updateStatus('Creating conversation...');
|
|
1669
|
+
return this.conversationStateService.startNewConversation(this.userId, this.userName).pipe(operators.map(function (conversationId) {
|
|
1670
|
+
console.log('Created conversation for first message:', conversationId);
|
|
1671
|
+
_this.conversationId = conversationId;
|
|
1672
|
+
_this.conversationInitialized = true;
|
|
1673
|
+
_this.conversationStarted.emit(conversationId);
|
|
1674
|
+
_this.updateStatus('AI Agent is ready');
|
|
1675
|
+
return true;
|
|
1676
|
+
}), operators.catchError(function (error) {
|
|
1677
|
+
console.error('Failed to create conversation:', error);
|
|
1678
|
+
_this.updateStatus('Failed to create conversation');
|
|
1679
|
+
return rxjs.of(false);
|
|
1680
|
+
}));
|
|
1681
|
+
};
|
|
1682
|
+
/**
|
|
1683
|
+
* Start a new conversation
|
|
1684
|
+
*/
|
|
1685
|
+
AiChatComponent.prototype.startNewConversation = function () {
|
|
1686
|
+
var _this = this;
|
|
1687
|
+
if (!this.userId || !this.userName) {
|
|
1688
|
+
console.warn('Cannot start conversation without userId and userName');
|
|
1689
|
+
this.updateStatus('User info required');
|
|
1690
|
+
this.addWelcomeMessage();
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
this.updateStatus('Starting new conversation...');
|
|
1694
|
+
this.conversationStateService.startNewConversation(this.userId, this.userName)
|
|
1695
|
+
.pipe(operators.takeUntil(this.destroy$))
|
|
1696
|
+
.subscribe({
|
|
1697
|
+
next: function (conversationId) {
|
|
1698
|
+
console.log('Started new conversation:', conversationId);
|
|
1699
|
+
_this.conversationId = conversationId;
|
|
1700
|
+
_this.conversationInitialized = true;
|
|
1701
|
+
_this.conversationStarted.emit(conversationId); // Emit event
|
|
1702
|
+
_this.updateStatus('AI Agent is ready');
|
|
1703
|
+
// Only add welcome message if no historical messages
|
|
1704
|
+
if (!_this.historicalMessages || _this.historicalMessages.length === 0) {
|
|
1705
|
+
_this.addWelcomeMessage();
|
|
1706
|
+
}
|
|
1707
|
+
else {
|
|
1708
|
+
_this.loadHistoricalMessages(_this.historicalMessages);
|
|
1709
|
+
}
|
|
1710
|
+
},
|
|
1711
|
+
error: function (error) {
|
|
1712
|
+
console.error('Failed to start conversation:', error);
|
|
1713
|
+
_this.updateStatus('Failed to start conversation (Demo Mode)');
|
|
1714
|
+
// Still show UI in demo mode
|
|
1715
|
+
_this.addWelcomeMessage();
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
};
|
|
1719
|
+
/**
|
|
1720
|
+
* Explicitly start a new conversation session
|
|
1721
|
+
* Public method for parent components or user-triggered actions
|
|
1722
|
+
* Always creates a new conversation (forceNew=true)
|
|
1723
|
+
*/
|
|
1724
|
+
AiChatComponent.prototype.startNewConversationExplicit = function () {
|
|
1725
|
+
if (!this.userId || !this.userName) {
|
|
1726
|
+
console.error('Cannot start new conversation: userId or userName not provided');
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
// Check if current conversation has messages
|
|
1730
|
+
var hasMessages = this.messages.length > 0;
|
|
1731
|
+
// Show confirmation dialog if enabled and there are messages
|
|
1732
|
+
if (this.confirmNewConversation && hasMessages) {
|
|
1733
|
+
this.showConfirmationModal = true;
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
// Proceed directly if no confirmation needed
|
|
1737
|
+
this.proceedWithNewConversation();
|
|
1738
|
+
};
|
|
1739
|
+
/**
|
|
1740
|
+
* Confirm new conversation from modal
|
|
1741
|
+
*/
|
|
1742
|
+
AiChatComponent.prototype.onConfirmNewConversation = function () {
|
|
1743
|
+
this.showConfirmationModal = false;
|
|
1744
|
+
this.proceedWithNewConversation();
|
|
1745
|
+
};
|
|
1746
|
+
/**
|
|
1747
|
+
* Cancel new conversation from modal
|
|
1748
|
+
*/
|
|
1749
|
+
AiChatComponent.prototype.cancelConfirmation = function () {
|
|
1750
|
+
this.showConfirmationModal = false;
|
|
1751
|
+
console.log('User cancelled new conversation');
|
|
1752
|
+
};
|
|
1753
|
+
/**
|
|
1754
|
+
* Proceed with creating new conversation
|
|
1755
|
+
* @private
|
|
1756
|
+
*/
|
|
1757
|
+
AiChatComponent.prototype.proceedWithNewConversation = function () {
|
|
1758
|
+
var _this = this;
|
|
1759
|
+
// Safety check (should have been validated by startNewConversationExplicit)
|
|
1760
|
+
if (!this.userId || !this.userName) {
|
|
1761
|
+
console.error('Cannot proceed: userId or userName not available');
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
var hasMessages = this.messages.length > 0;
|
|
1765
|
+
if (hasMessages) {
|
|
1766
|
+
console.log('Starting new conversation (current has', this.messages.length, 'messages)');
|
|
1767
|
+
}
|
|
1768
|
+
this.updateStatus('Starting new conversation...');
|
|
1769
|
+
this.conversationStateService.startNewConversationExplicit(this.userId, this.userName)
|
|
1770
|
+
.pipe(operators.takeUntil(this.destroy$))
|
|
1771
|
+
.subscribe({
|
|
1772
|
+
next: function (conversationId) {
|
|
1773
|
+
_this.conversationId = conversationId;
|
|
1774
|
+
_this.conversationInitialized = true;
|
|
1775
|
+
_this.conversationStarted.emit(conversationId); // Emit event
|
|
1776
|
+
_this.updateStatus('New conversation started');
|
|
1777
|
+
// Clear existing messages and start fresh
|
|
1778
|
+
_this.messages = [];
|
|
1779
|
+
_this.addWelcomeMessage();
|
|
1780
|
+
console.log('Explicitly started new conversation:', conversationId);
|
|
1781
|
+
},
|
|
1782
|
+
error: function (error) {
|
|
1783
|
+
console.error('Failed to start new conversation:', error);
|
|
1784
|
+
_this.updateStatus('Failed to start new conversation');
|
|
1785
|
+
}
|
|
1786
|
+
});
|
|
1787
|
+
};
|
|
924
1788
|
AiChatComponent.prototype.testConfiguration = function () {
|
|
925
1789
|
var _this = this;
|
|
926
1790
|
this.updateStatus('Testing connection...');
|
|
@@ -1027,13 +1891,19 @@
|
|
|
1027
1891
|
if (!this.currentInquiry.trim() || this.isLoading) {
|
|
1028
1892
|
return;
|
|
1029
1893
|
}
|
|
1894
|
+
var inquiry = this.currentInquiry.trim();
|
|
1895
|
+
this.currentInquiry = '';
|
|
1896
|
+
this.authorName = '';
|
|
1897
|
+
this.intent = '';
|
|
1898
|
+
this.isLoading = true;
|
|
1899
|
+
// Add user message
|
|
1030
1900
|
var userMessage = {
|
|
1031
1901
|
messageId: this.generateMessageId(),
|
|
1032
1902
|
conversationId: this.conversationId,
|
|
1033
1903
|
isUser: true,
|
|
1034
1904
|
sender: this.userName || 'User',
|
|
1035
1905
|
intent: this.intent,
|
|
1036
|
-
content:
|
|
1906
|
+
content: inquiry,
|
|
1037
1907
|
timestamp: new Date()
|
|
1038
1908
|
};
|
|
1039
1909
|
this.messages.push(userMessage);
|
|
@@ -1047,97 +1917,158 @@
|
|
|
1047
1917
|
isLoading: true
|
|
1048
1918
|
};
|
|
1049
1919
|
this.messages.push(loadingMessage);
|
|
1050
|
-
|
|
1051
|
-
this.
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
this.aiAgentService.processInquiry(request)
|
|
1057
|
-
.pipe(operators.takeUntil(this.destroy$))
|
|
1058
|
-
.subscribe({
|
|
1059
|
-
next: function (response) {
|
|
1060
|
-
_this.isLoading = false;
|
|
1061
|
-
// Update conversationId if provided in response
|
|
1062
|
-
if (response.conversationId && !_this.conversationId) {
|
|
1063
|
-
_this.conversationId = response.conversationId;
|
|
1920
|
+
// Ensure conversation is initialized before sending (lazy initialization)
|
|
1921
|
+
if (this.enableConversationLifecycle) {
|
|
1922
|
+
this.ensureConversationInitialized()
|
|
1923
|
+
.pipe(operators.switchMap(function (initialized) {
|
|
1924
|
+
if (!initialized) {
|
|
1925
|
+
throw new Error('Failed to initialize conversation');
|
|
1064
1926
|
}
|
|
1065
|
-
//
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1927
|
+
// Update user message with conversationId
|
|
1928
|
+
userMessage.conversationId = _this.conversationId;
|
|
1929
|
+
var request = Object.assign(Object.assign(Object.assign(Object.assign({ query: inquiry }, (_this.conversationId !== undefined && _this.conversationId !== null ? { conversationId: String(_this.conversationId) } : {})), (_this.userId !== undefined && _this.userId !== null ? { userId: _this.userId } : {})), (_this.userName !== undefined && _this.userName !== null ? { userName: _this.userName } : {})), (_this.appSource !== undefined && _this.appSource !== null ? { appSource: _this.appSource } : {}));
|
|
1930
|
+
return _this.aiAgentService.processInquiry(request);
|
|
1931
|
+
}), operators.takeUntil(this.destroy$))
|
|
1932
|
+
.subscribe({
|
|
1933
|
+
next: function (response) { return _this.handleInquiryResponse(response, inquiry, userMessage); },
|
|
1934
|
+
error: function (error) { return _this.handleInquiryError(error, inquiry, userMessage); }
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
else {
|
|
1938
|
+
// Legacy mode without conversation lifecycle
|
|
1939
|
+
var request = Object.assign(Object.assign(Object.assign(Object.assign({ query: inquiry }, (this.conversationId !== undefined && this.conversationId !== null ? { conversationId: String(this.conversationId) } : {})), (this.userId !== undefined && this.userId !== null ? { userId: this.userId } : {})), (this.userName !== undefined && this.userName !== null ? { userName: this.userName } : {})), (this.appSource !== undefined && this.appSource !== null ? { appSource: this.appSource } : {}));
|
|
1940
|
+
this.aiAgentService.processInquiry(request)
|
|
1941
|
+
.pipe(operators.takeUntil(this.destroy$))
|
|
1942
|
+
.subscribe({
|
|
1943
|
+
next: function (response) { return _this.handleInquiryResponse(response, inquiry, userMessage); },
|
|
1944
|
+
error: function (error) { return _this.handleInquiryError(error, inquiry, userMessage); }
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
};
|
|
1948
|
+
/**
|
|
1949
|
+
* Handle successful inquiry response
|
|
1950
|
+
*/
|
|
1951
|
+
AiChatComponent.prototype.handleInquiryResponse = function (response, inquiry, userMessage) {
|
|
1952
|
+
var _this = this;
|
|
1953
|
+
this.isLoading = false;
|
|
1954
|
+
// Update last activity timestamp
|
|
1955
|
+
if (this.enableConversationLifecycle) {
|
|
1956
|
+
this.conversationStateService.updateLastActivity();
|
|
1957
|
+
}
|
|
1958
|
+
// Update conversationId if provided in response
|
|
1959
|
+
if (response.conversationId && !this.conversationId) {
|
|
1960
|
+
this.conversationId = response.conversationId;
|
|
1961
|
+
}
|
|
1962
|
+
// Update user message with conversationId
|
|
1963
|
+
if (userMessage && this.conversationId) {
|
|
1964
|
+
userMessage.conversationId = this.conversationId;
|
|
1965
|
+
}
|
|
1966
|
+
// Remove loading message
|
|
1967
|
+
this.messages = this.messages.filter(function (msg) { return !msg.isLoading; });
|
|
1968
|
+
if (response.answer || (response.contents && response.contents.length > 0)) {
|
|
1969
|
+
if (response.answer) {
|
|
1970
|
+
var botMessage = {
|
|
1971
|
+
messageId: this.generateMessageId(),
|
|
1972
|
+
conversationId: this.conversationId,
|
|
1973
|
+
isUser: false,
|
|
1974
|
+
sender: 'Maestro',
|
|
1975
|
+
intent: response.searchIntent || inquiry,
|
|
1976
|
+
content: this.formatMessageContent(response.answer),
|
|
1977
|
+
authorName: response.contents && response.contents.length > 0 ? response.contents[0].authorName : undefined,
|
|
1978
|
+
timestamp: new Date()
|
|
1979
|
+
};
|
|
1980
|
+
this.messages.push(botMessage);
|
|
1981
|
+
this.messageReceived.emit(botMessage);
|
|
1982
|
+
}
|
|
1983
|
+
else if (response.contents && response.contents.length > 0) {
|
|
1984
|
+
// Push each content as a separate message
|
|
1985
|
+
response.contents.forEach(function (contentItem) {
|
|
1986
|
+
if (contentItem.content) {
|
|
1069
1987
|
var botMessage = {
|
|
1070
1988
|
messageId: _this.generateMessageId(),
|
|
1071
1989
|
conversationId: _this.conversationId,
|
|
1072
1990
|
isUser: false,
|
|
1073
|
-
sender: 'Maestro',
|
|
1991
|
+
sender: contentItem.authorName || 'Maestro',
|
|
1074
1992
|
intent: response.searchIntent || inquiry,
|
|
1075
|
-
content: _this.formatMessageContent(
|
|
1076
|
-
authorName:
|
|
1993
|
+
content: _this.formatMessageContent(contentItem.content || 'No content available'),
|
|
1994
|
+
authorName: contentItem.authorName,
|
|
1077
1995
|
timestamp: new Date()
|
|
1078
1996
|
};
|
|
1079
1997
|
_this.messages.push(botMessage);
|
|
1080
1998
|
_this.messageReceived.emit(botMessage);
|
|
1081
1999
|
}
|
|
1082
|
-
|
|
1083
|
-
// Push each content as a separate message
|
|
1084
|
-
response.contents.forEach(function (contentItem) {
|
|
1085
|
-
if (contentItem.content) {
|
|
1086
|
-
var botMessage = {
|
|
1087
|
-
messageId: _this.generateMessageId(),
|
|
1088
|
-
conversationId: _this.conversationId,
|
|
1089
|
-
isUser: false,
|
|
1090
|
-
sender: contentItem.authorName || 'Maestro',
|
|
1091
|
-
intent: response.searchIntent || inquiry,
|
|
1092
|
-
content: _this.formatMessageContent(contentItem.content || 'No content available'),
|
|
1093
|
-
authorName: contentItem.authorName,
|
|
1094
|
-
timestamp: new Date()
|
|
1095
|
-
};
|
|
1096
|
-
_this.messages.push(botMessage);
|
|
1097
|
-
_this.messageReceived.emit(botMessage);
|
|
1098
|
-
}
|
|
1099
|
-
});
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
else {
|
|
1103
|
-
var errorMessage = {
|
|
1104
|
-
messageId: _this.generateMessageId(),
|
|
1105
|
-
conversationId: _this.conversationId,
|
|
1106
|
-
isUser: false,
|
|
1107
|
-
sender: 'Maestro',
|
|
1108
|
-
intent: inquiry,
|
|
1109
|
-
content: "Sorry, I didn't receive a valid response.",
|
|
1110
|
-
authorName: _this.authorName,
|
|
1111
|
-
timestamp: new Date()
|
|
1112
|
-
};
|
|
1113
|
-
_this.messages.push(errorMessage);
|
|
1114
|
-
_this.messageReceived.emit(errorMessage);
|
|
1115
|
-
}
|
|
1116
|
-
},
|
|
1117
|
-
error: function (error) {
|
|
1118
|
-
_this.isLoading = false;
|
|
1119
|
-
// Remove loading message
|
|
1120
|
-
_this.messages = _this.messages.filter(function (msg) { return !msg.isLoading; });
|
|
1121
|
-
var errorMessage = {
|
|
1122
|
-
messageId: _this.generateMessageId(),
|
|
1123
|
-
conversationId: _this.conversationId,
|
|
1124
|
-
isUser: false,
|
|
1125
|
-
sender: 'Maestro',
|
|
1126
|
-
intent: inquiry,
|
|
1127
|
-
content: 'Sorry, I encountered an error processing your request. Please try again.',
|
|
1128
|
-
authorName: _this.authorName,
|
|
1129
|
-
timestamp: new Date()
|
|
1130
|
-
};
|
|
1131
|
-
_this.messages.push(errorMessage);
|
|
1132
|
-
_this.messageReceived.emit(errorMessage);
|
|
1133
|
-
console.error('Error processing inquiry:', error);
|
|
2000
|
+
});
|
|
1134
2001
|
}
|
|
1135
|
-
}
|
|
2002
|
+
}
|
|
2003
|
+
else {
|
|
2004
|
+
var errorMessage = {
|
|
2005
|
+
messageId: this.generateMessageId(),
|
|
2006
|
+
conversationId: this.conversationId,
|
|
2007
|
+
isUser: false,
|
|
2008
|
+
sender: 'Maestro',
|
|
2009
|
+
intent: inquiry,
|
|
2010
|
+
content: "Sorry, I didn't receive a valid response.",
|
|
2011
|
+
authorName: this.authorName,
|
|
2012
|
+
timestamp: new Date()
|
|
2013
|
+
};
|
|
2014
|
+
this.messages.push(errorMessage);
|
|
2015
|
+
this.messageReceived.emit(errorMessage);
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
/**
|
|
2019
|
+
* Handle inquiry error
|
|
2020
|
+
*/
|
|
2021
|
+
AiChatComponent.prototype.handleInquiryError = function (error, inquiry, userMessage) {
|
|
2022
|
+
this.isLoading = false;
|
|
2023
|
+
// Remove loading message
|
|
2024
|
+
this.messages = this.messages.filter(function (msg) { return !msg.isLoading; });
|
|
2025
|
+
var errorMessage = {
|
|
2026
|
+
messageId: this.generateMessageId(),
|
|
2027
|
+
conversationId: this.conversationId,
|
|
2028
|
+
isUser: false,
|
|
2029
|
+
sender: 'Maestro',
|
|
2030
|
+
intent: inquiry,
|
|
2031
|
+
content: 'Sorry, I encountered an error processing your request. Please try again.',
|
|
2032
|
+
authorName: this.authorName,
|
|
2033
|
+
timestamp: new Date()
|
|
2034
|
+
};
|
|
2035
|
+
this.messages.push(errorMessage);
|
|
2036
|
+
this.messageReceived.emit(errorMessage);
|
|
2037
|
+
console.error('Error processing inquiry:', error);
|
|
1136
2038
|
};
|
|
1137
2039
|
AiChatComponent.prototype.clearChat = function () {
|
|
1138
2040
|
this.messages = [];
|
|
1139
2041
|
this.addWelcomeMessage();
|
|
1140
2042
|
};
|
|
2043
|
+
/**
|
|
2044
|
+
* Close the current conversation
|
|
2045
|
+
*/
|
|
2046
|
+
AiChatComponent.prototype.closeConversation = function (reason, rating, feedback) {
|
|
2047
|
+
var _this = this;
|
|
2048
|
+
if (!this.enableConversationLifecycle) {
|
|
2049
|
+
console.warn('Conversation lifecycle is disabled');
|
|
2050
|
+
return;
|
|
2051
|
+
}
|
|
2052
|
+
this.updateStatus('Closing conversation...');
|
|
2053
|
+
this.conversationStateService.closeCurrentConversation(reason, rating, feedback)
|
|
2054
|
+
.pipe(operators.takeUntil(this.destroy$))
|
|
2055
|
+
.subscribe({
|
|
2056
|
+
next: function () {
|
|
2057
|
+
console.log('Conversation closed successfully');
|
|
2058
|
+
_this.conversationId = undefined;
|
|
2059
|
+
_this.messages = [];
|
|
2060
|
+
_this.updateStatus('Conversation closed');
|
|
2061
|
+
// Optionally start a new conversation immediately
|
|
2062
|
+
if (_this.userId && _this.userName) {
|
|
2063
|
+
setTimeout(function () { return _this.startNewConversation(); }, 1000);
|
|
2064
|
+
}
|
|
2065
|
+
},
|
|
2066
|
+
error: function (error) {
|
|
2067
|
+
console.error('Failed to close conversation:', error);
|
|
2068
|
+
_this.updateStatus('Failed to close conversation');
|
|
2069
|
+
}
|
|
2070
|
+
});
|
|
2071
|
+
};
|
|
1141
2072
|
AiChatComponent.prototype.onKeyPress = function (event) {
|
|
1142
2073
|
if (event.key === 'Enter' && !event.shiftKey) {
|
|
1143
2074
|
event.preventDefault();
|
|
@@ -1459,8 +2390,8 @@
|
|
|
1459
2390
|
};
|
|
1460
2391
|
return AiChatComponent;
|
|
1461
2392
|
}());
|
|
1462
|
-
AiChatComponent.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: AiChatComponent, deps: [{ token: AiAgentService }, { token:
|
|
1463
|
-
AiChatComponent.ɵcmp = i0__namespace.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.17", type: AiChatComponent, selector: "emanate-ai-chat", inputs: { title: "title", placeholder: "placeholder", showDebugInfo: "showDebugInfo", apiUrl: "apiUrl", appKey: "appKey", appSource: "appSource", userId: "userId", firstName: "firstName", userName: "userName", templateDesign: "templateDesign", welcomeMessage: "welcomeMessage", historicalMessages: "historicalMessages", conversationId: "conversationId", enableImageUpload: "enableImageUpload", enableFileUpload: "enableFileUpload", iconSet: "iconSet", customIcons: "customIcons", enableAnimations: "enableAnimations", animationSpeed: "animationSpeed", enableStatusTransitions: "enableStatusTransitions" }, outputs: { messageReceived: "messageReceived", messageSent: "messageSent", fileUploaded: "fileUploaded", sizeChanged: "sizeChanged" }, usesOnChanges: true, ngImport: i0__namespace, template: "<div class=\"ai-chat-container\" \r\n [ngClass]=\"getThemeClass()\" \r\n [ngStyle]=\"getContainerStyle()\"\r\n [class.fullscreen]=\"isFullscreen\"\r\n [class.resizing]=\"isResizing\"\r\n [class]=\"getAnimationClass()\">\r\n \r\n <!-- Resize Handles -->\r\n <div class=\"resize-handle resize-handle-right\" \r\n (mousedown)=\"startResize($event, 'width')\"\r\n *ngIf=\"!isFullscreen\"\r\n title=\"Drag to resize width\">\r\n </div>\r\n \r\n <div class=\"resize-handle resize-handle-corner\" \r\n (mousedown)=\"startResize($event, 'both')\"\r\n *ngIf=\"!isFullscreen\"\r\n title=\"Drag to resize\">\r\n </div>\r\n \r\n <!-- Debug Information (only shown when showDebugInfo is true) -->\r\n <div *ngIf=\"showDebugInfo\" class=\"debug-info\">\r\n <h3>DEBUG: AI Chat Component Loaded</h3>\r\n <p>Status: {{ configurationStatus || 'Loading...' }}</p>\r\n <p>Messages count: {{ messages.length || 0 }}</p>\r\n <p>Theme: {{ templateDesign || 'default' }}</p>\r\n <p>Size: {{ currentSize }} ({{ containerWidth }}x{{ containerHeight }})</p>\r\n </div>\r\n\r\n <div class=\"chat-header\">\r\n <div class=\"header-left\">\r\n <h2>{{ title }}</h2>\r\n <!-- Icon-only status indicator -->\r\n <div class=\"status-indicator-icon\" \r\n [ngClass]=\"{'status-ready': configurationStatus === 'AI Agent is ready', \r\n 'status-error': configurationStatus !== 'AI Agent is ready' && configurationStatus !== 'Initializing...' && configurationStatus !== 'Testing connection...',\r\n 'status-loading': configurationStatus === 'Initializing...' || configurationStatus === 'Testing connection...',\r\n 'transition-enabled': enableStatusTransitions}\"\r\n [title]=\"configurationStatus\">\r\n </div>\r\n </div>\r\n \r\n <div class=\"header-controls\">\r\n <!-- Size Preset Buttons -->\r\n <div class=\"size-controls\" *ngIf=\"!isFullscreen\">\r\n <button class=\"control-button size-button\" \r\n (click)=\"applySizePreset('compact')\"\r\n [class.active]=\"currentSize === 'compact'\"\r\n title=\"Compact View\"\r\n attr.aria-label=\"Compact View\">\r\n <span class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.compactView\"></span>\r\n </button>\r\n <button class=\"control-button size-button\" \r\n (click)=\"applySizePreset('default')\"\r\n [class.active]=\"currentSize === 'default'\"\r\n title=\"Default View\"\r\n attr.aria-label=\"Default View\">\r\n <span class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.defaultView\"></span>\r\n </button>\r\n <button class=\"control-button size-button\" \r\n (click)=\"applySizePreset('expanded')\"\r\n [class.active]=\"currentSize === 'expanded'\"\r\n title=\"Expanded View\"\r\n attr.aria-label=\"Expanded View\">\r\n <span class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.expandedView\"></span>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"chat-messages\" #messagesContainer>\r\n <div *ngFor=\"let message of messages; let i = index; trackBy: trackByIndex\" class=\"message-wrapper\">\r\n <!-- Message -->\r\n <div class=\"message\" \r\n [ngClass]=\"{'user-message': message.isUser, 'ai-message': !message.isUser, 'loading-message': message.isLoading}\">\r\n \r\n <div class=\"message-content\">\r\n <div [innerHTML]=\"message.content\"></div>\r\n <div class=\"message-timestamp\">{{ getFormattedTime(message.timestamp) }}</div>\r\n </div>\r\n \r\n <div class=\"message-avatar\">\r\n <span *ngIf=\"message.isUser\" class=\"avatar-icon\" [innerHTML]=\"sanitizedIcons.userAvatar\"></span>\r\n <span *ngIf=\"!message.isUser && !message.isLoading\" class=\"avatar-icon\" [innerHTML]=\"sanitizedIcons.aiAvatar\"></span>\r\n <span *ngIf=\"message.isLoading\" class=\"avatar-icon loading-spinner\" [innerHTML]=\"sanitizedIcons.loadingAvatar\"></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"chat-input\">\r\n <!-- Attachments Preview -->\r\n <div class=\"attachments-preview\" *ngIf=\"selectedFiles.length > 0\">\r\n <div class=\"attachment-item\" *ngFor=\"let file of selectedFiles\">\r\n <svg class=\"attachment-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\"></path>\r\n </svg>\r\n <span class=\"attachment-name\">{{ file.name }}</span>\r\n <span class=\"attachment-size\">{{ getFileSizeFormatted(file.size) }}</span>\r\n <button class=\"remove-attachment\" (click)=\"removeAttachment(file.id)\" title=\"Remove\" attr.aria-label=\"Remove attachment\">\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n \r\n <div class=\"input-container\">\r\n <!-- File Upload Inputs (Hidden) -->\r\n <input type=\"file\" \r\n id=\"image-upload-input\" \r\n accept=\"image/*\" \r\n multiple \r\n (change)=\"onImageUpload($event)\"\r\n style=\"display: none;\"\r\n [disabled]=\"!enableImageUpload\">\r\n \r\n <input type=\"file\" \r\n id=\"file-upload-input\" \r\n accept=\".pdf,.doc,.docx,.txt,.csv\" \r\n multiple \r\n (change)=\"onFileUpload($event)\"\r\n style=\"display: none;\"\r\n [disabled]=\"!enableFileUpload\">\r\n \r\n <!-- Upload Buttons (Conditionally rendered) -->\r\n <div class=\"upload-buttons\" *ngIf=\"enableImageUpload || enableFileUpload\">\r\n <button *ngIf=\"enableImageUpload\" \r\n class=\"upload-button image-upload\" \r\n (click)=\"triggerImageUpload()\"\r\n [disabled]=\"isLoading\"\r\n title=\"Upload Image\"\r\n attr.aria-label=\"Upload Image\">\r\n <span class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.imageUpload\"></span>\r\n </button>\r\n \r\n <button *ngIf=\"enableFileUpload\" \r\n class=\"upload-button file-upload\" \r\n (click)=\"triggerFileUpload()\"\r\n [disabled]=\"isLoading\"\r\n title=\"Attach File\"\r\n attr.aria-label=\"Attach File\">\r\n <span class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.fileUpload\"></span>\r\n </button>\r\n </div>\r\n \r\n <textarea \r\n [(ngModel)]=\"currentInquiry\" \r\n (keypress)=\"onKeyPress($event)\"\r\n [placeholder]=\"placeholder\"\r\n rows=\"2\"\r\n [disabled]=\"isLoading\"\r\n class=\"inquiry-textarea\"\r\n attr.aria-label=\"Type your message\"></textarea>\r\n \r\n <button \r\n (click)=\"sendInquiry()\" \r\n [disabled]=\"!currentInquiry.trim() || isLoading\"\r\n class=\"send-button\"\r\n title=\"Send Message\"\r\n attr.aria-label=\"Send Message\">\r\n <span *ngIf=\"!isLoading\" class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.send\"></span>\r\n <span *ngIf=\"isLoading\" class=\"icon-svg loading-icon\" [innerHTML]=\"sanitizedIcons.sendLoading\"></span>\r\n <span class=\"text\">{{ isLoading ? 'Sending...' : 'Send' }}</span>\r\n </button>\r\n </div>\r\n </div>\r\n</div>", styles: ["@charset \"UTF-8\";.ai-chat-container{display:flex;flex-direction:column;height:100vh;max-width:800px;margin:0 auto;background:#ffffff;border-radius:12px;overflow:hidden;position:relative;transition:all .25s cubic-bezier(.4,0,.2,1);box-shadow:0 2px 8px #00000014,0 4px 16px #0000000a;--icon-primary: #667eea;--icon-secondary: #6b7280;--icon-avatar-user: #667eea;--icon-avatar-ai: #764ba2;--icon-header: #ffffff;--icon-upload: #667eea;--icon-send: #ffffff;--icon-active: #667eea;--icon-disabled: #d1d5db;--icon-hover: #5568d3}.ai-chat-container.fullscreen{position:fixed!important;top:0!important;left:0!important;right:0!important;bottom:0!important;width:100vw!important;height:100vh!important;max-width:100vw!important;max-height:100vh!important;border-radius:0;z-index:99999;margin:0}.ai-chat-container.resizing{-webkit-user-select:none;user-select:none;cursor:nwse-resize;transition:none}.resize-handle{position:absolute;background:rgba(102,126,234,.3);transition:background .2s ease;z-index:10}.resize-handle:hover{background:rgba(102,126,234,.6)}.resize-handle.resize-handle-right{top:0;right:-4px;width:8px;height:100%;cursor:ew-resize}.resize-handle.resize-handle-corner{bottom:-4px;right:-4px;width:20px;height:20px;cursor:nwse-resize;border-radius:0 0 8px}.resize-handle.resize-handle-corner:after{content:\"\\22f0\";position:absolute;bottom:2px;right:2px;font-size:12px;color:#fffc}.debug-info{background:yellow;padding:10px;border:1px solid orange}.debug-info h3{margin:0;font-size:1.2rem}.debug-info p{margin:5px 0}.icon-svg{display:inline-block;width:20px;height:20px;color:var(--icon-secondary)}.icon-svg svg{width:100%;height:100%;stroke-width:2;transition:all .2s ease}.avatar-icon{display:inline-block;width:28px;height:28px}.avatar-icon svg{width:100%;height:100%}.chat-header{background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:#fff;padding:1rem 1.25rem;display:flex;justify-content:space-between;align-items:center;grid-gap:1rem;gap:1rem;flex-wrap:wrap;box-shadow:0 2px 4px #0000001a}.chat-header .header-left{display:flex;align-items:center;grid-gap:.75rem;gap:.75rem;flex:1;min-width:200px}.chat-header .header-left h2{margin:0;font-size:1.25rem;font-weight:600;letter-spacing:-.01em}.chat-header .header-controls{display:flex;align-items:center;grid-gap:.5rem;gap:.5rem}.chat-header .header-controls .icon-svg{color:var(--icon-header)}.chat-header .status-indicator-icon{width:10px;height:10px;border-radius:50%;background:rgba(255,255,255,.4);transition:all .3s ease;box-shadow:0 0 0 2px #fff3;cursor:help}.chat-header .status-indicator-icon.transition-enabled{transition:background-color .4s cubic-bezier(.4,0,.2,1),box-shadow .4s cubic-bezier(.4,0,.2,1),transform .3s cubic-bezier(.4,0,.2,1)}.chat-header .status-indicator-icon.status-ready{background:#2ecc71;box-shadow:0 0 0 2px #2ecc714d,0 0 8px #2ecc7166}.chat-header .status-indicator-icon.status-ready.transition-enabled{animation:statusReady .5s cubic-bezier(.4,0,.2,1)}.chat-header .status-indicator-icon.status-error{background:#e74c3c;box-shadow:0 0 0 2px #e74c3c4d,0 0 8px #e74c3c66}.chat-header .status-indicator-icon.status-error.transition-enabled{animation:statusError .5s cubic-bezier(.4,0,.2,1)}.chat-header .status-indicator-icon.status-loading{background:#f39c12;box-shadow:0 0 0 2px #f39c124d;animation:pulse 1.5s ease-in-out infinite}.chat-header .status-indicator-icon.status-loading.transition-enabled{animation:pulse 1.5s ease-in-out infinite,statusLoading .5s cubic-bezier(.4,0,.2,1)}@keyframes pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.6;transform:scale(1.1)}}@keyframes statusReady{0%{transform:scale(.8);opacity:0}50%{transform:scale(1.2)}to{transform:scale(1);opacity:1}}@keyframes statusError{0%{transform:scale(.8);opacity:0}50%{transform:scale(1.15)}to{transform:scale(1);opacity:1}}@keyframes statusLoading{0%{transform:scale(.8) rotate(0);opacity:0}to{transform:scale(1) rotate(360deg);opacity:1}}.ai-chat-container{--animation-duration: .25s;--animation-easing: cubic-bezier(.4, 0, .2, 1)}.ai-chat-container.animation-speed-slow{--animation-duration: .5s}.ai-chat-container.animation-speed-slow *{transition-duration:.5s!important}.ai-chat-container.animation-speed-slow .status-indicator-icon.transition-enabled{transition-duration:.6s!important}.ai-chat-container.animation-speed-slow .message{animation-duration:.6s!important}.ai-chat-container.animation-speed-fast{--animation-duration: .15s}.ai-chat-container.animation-speed-fast *{transition-duration:.15s!important}.ai-chat-container.animation-speed-fast .status-indicator-icon.transition-enabled{transition-duration:.2s!important}.ai-chat-container.animation-speed-fast .message{animation-duration:.2s!important}.ai-chat-container.no-animations{--animation-duration: 0ms}.ai-chat-container.no-animations *,.ai-chat-container.no-animations *:before,.ai-chat-container.no-animations *:after{animation-duration:0ms!important;animation-delay:0ms!important;transition-duration:0ms!important;transition-delay:0ms!important}.ai-chat-container.no-animations .status-indicator-icon{animation:none!important;transition:none!important}.size-controls{display:flex;grid-gap:.5rem;gap:.5rem;background:transparent;padding:0;border-radius:0;backdrop-filter:none}.control-button{background:transparent;border:none;border-radius:0;width:28px;height:28px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s cubic-bezier(.4,0,.2,1);color:#fff;padding:0}.control-button .icon-svg{width:18px;height:18px;color:var(--icon-header)}.control-button:hover:not(:disabled){opacity:.8;transform:scale(1.1)}.control-button:active:not(:disabled){transform:scale(.95)}.control-button.active{opacity:1;transform:scale(1.15)}.control-button:disabled{opacity:.4;cursor:not-allowed}.fullscreen-button{background:transparent}.fullscreen-button:hover:not(:disabled){opacity:.8;transform:scale(1.1)}.clear-button{background:transparent}.clear-button:hover:not(:disabled){opacity:.8;transform:scale(1.1);color:#fcc}.chat-messages{flex:1;overflow-y:auto;overflow-x:hidden;padding:1.25rem;display:flex;flex-direction:column;grid-gap:.875rem;gap:.875rem;scroll-behavior:smooth;background:#f8f9fa}.chat-messages::-webkit-scrollbar{width:6px}.chat-messages::-webkit-scrollbar-track{background:transparent}.chat-messages::-webkit-scrollbar-thumb{background:rgba(0,0,0,.15);border-radius:3px}.chat-messages::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.25)}.message-wrapper{display:flex;flex-direction:column}.message{display:flex;max-width:75%;animation:fadeInSlide .25s cubic-bezier(.4,0,.2,1)}.message.user-message{align-self:flex-end;flex-direction:row-reverse}.message.user-message .message-content{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-radius:18px 18px 4px;box-shadow:0 1px 2px #0000001a}.message.user-message .avatar-icon{color:var(--icon-avatar-user)}.message.ai-message{align-self:flex-start}.message.ai-message .message-content{background:white;color:#1a1a1a;border-radius:18px 18px 18px 4px;border:1px solid #e8e8e8;box-shadow:0 1px 2px #0000000d}.message.ai-message .avatar-icon{color:var(--icon-avatar-ai)}.message.loading-message{opacity:.7}.message.loading-message .message-content{font-style:italic}@keyframes fadeInSlide{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.message-content{padding:.75rem 1rem;margin:0 .5rem;word-wrap:break-word;max-width:100%;line-height:1.5;font-size:.9375rem}.message-timestamp{font-size:.6875rem;opacity:.65;margin-top:.375rem;text-align:right;font-weight:500}.message-avatar{width:36px;height:36px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.message-avatar .avatar-icon{opacity:.9}.loading-spinner{animation:spin 1.2s linear infinite;opacity:.7}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.chat-input{background:white;padding:1rem 1.25rem;border-top:1px solid #e8e8e8;display:flex;flex-direction:column;grid-gap:.75rem;gap:.75rem;box-shadow:0 -2px 8px #0000000a}.input-container{display:flex;grid-gap:.5rem;gap:.5rem;align-items:flex-end}.upload-buttons{display:flex;grid-gap:.5rem;gap:.5rem;flex-direction:column}.upload-button{background:#f5f5f5;border:1px solid #e0e0e0;border-radius:10px;width:42px;height:42px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s cubic-bezier(.4,0,.2,1)}.upload-button .icon-svg{width:20px;height:20px;color:var(--icon-upload)}.upload-button:hover:not(:disabled){background:var(--icon-upload);border-color:var(--icon-upload);transform:translateY(-1px);box-shadow:0 2px 8px #667eea40}.upload-button:hover:not(:disabled) .icon-svg{color:#fff}.upload-button:active:not(:disabled){transform:translateY(0)}.upload-button:disabled{opacity:.4;cursor:not-allowed;background:#fafafa}.attachments-preview{display:flex;flex-wrap:wrap;grid-gap:.5rem;gap:.5rem;padding:.75rem;background:#f8f9fa;border-radius:10px;border:1px solid #e8e8e8}.attachment-item{display:flex;align-items:center;grid-gap:.625rem;gap:.625rem;background:white;border:1px solid #e0e0e0;border-radius:8px;padding:.625rem .875rem;font-size:.875rem;transition:all .2s ease}.attachment-item:hover{border-color:#667eea;box-shadow:0 2px 4px #667eea1a}.attachment-item .attachment-icon{width:20px;height:20px;color:#667eea;flex-shrink:0}.attachment-item .attachment-name{font-weight:500;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#1a1a1a}.attachment-item .attachment-size{color:#666;font-size:.75rem;font-weight:500}.attachment-item .remove-attachment{background:transparent;border:none;color:#999;cursor:pointer;padding:.25rem;margin-left:.25rem;transition:all .2s ease;display:flex;align-items:center;justify-content:center;border-radius:4px}.attachment-item .remove-attachment svg{width:16px;height:16px}.attachment-item .remove-attachment:hover{color:#e74c3c;background:rgba(231,76,60,.1)}.inquiry-textarea{flex:1;border:1.5px solid #e0e0e0;border-radius:22px;padding:.875rem 1.125rem;font-family:inherit;font-size:.9375rem;line-height:1.5;resize:none;outline:none;transition:all .2s cubic-bezier(.4,0,.2,1);background:#fafafa}.inquiry-textarea:focus{border-color:#667eea;background:white;box-shadow:0 0 0 3px #667eea14}.inquiry-textarea:disabled{background:#f5f5f5;cursor:not-allowed;opacity:.6}.inquiry-textarea::placeholder{color:#999}.send-button{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border:none;border-radius:50px;padding:.875rem 1.75rem;font-size:.9375rem;font-weight:600;cursor:pointer;display:flex;align-items:center;grid-gap:.5rem;gap:.5rem;transition:all .2s cubic-bezier(.4,0,.2,1);min-width:110px;justify-content:center;box-shadow:0 2px 8px #667eea40}.send-button .icon-svg{width:18px;height:18px;color:var(--icon-send)}.send-button .loading-icon{animation:spin 1.2s linear infinite}.send-button:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 4px 16px #667eea66}.send-button:active:not(:disabled){transform:translateY(0)}.send-button:disabled{background:#d0d0d0;cursor:not-allowed;box-shadow:none;opacity:.6}@media (max-width: 768px){.ai-chat-container{max-width:100%;border-radius:0;height:100vh}.ai-chat-container:not(.fullscreen){width:100%!important}.resize-handle{display:none}.size-controls{display:none}.chat-header{padding:.875rem 1rem}.chat-header .header-left h2{font-size:1.125rem}.chat-header .status-indicator-icon{width:8px;height:8px}.control-button{width:36px;height:36px}.control-button .icon-svg{width:18px;height:18px}.message{max-width:85%}.message-content{font-size:.875rem}.upload-buttons{flex-direction:row}.input-container{flex-wrap:wrap}.send-button{min-width:90px;padding:.75rem 1.25rem}.send-button .text{display:none}.chat-input{padding:.875rem 1rem}}@media (max-width: 480px){.chat-header{flex-direction:column;align-items:flex-start;grid-gap:.625rem;gap:.625rem}.chat-header .header-controls{width:100%;justify-content:space-between}.control-button{width:34px;height:34px}.control-button .icon-svg{width:16px;height:16px}.message-content{padding:.625rem .875rem;font-size:.875rem}.attachments-preview{flex-direction:column}.attachment-item{width:100%}.attachment-item .attachment-name{max-width:120px}.send-button{padding:.75rem 1rem;min-width:80px}}.control-button:focus,.upload-button:focus,.send-button:focus,.clear-button:focus{outline:3px solid #667eea;outline-offset:2px}@media (prefers-contrast: high){.control-button,.upload-button,.send-button{border:2px solid currentColor}.message-content{border:2px solid currentColor}}@media (prefers-reduced-motion: reduce){*{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}.theme-dark{background:#2c2c2c;--icon-primary: #818cf8;--icon-secondary: #9ca3af;--icon-avatar-user: #818cf8;--icon-avatar-ai: #a78bfa;--icon-header: #e5e7eb;--icon-upload: #818cf8;--icon-send: #ffffff;--icon-active: #818cf8;--icon-disabled: #4b5563;--icon-hover: #6366f1}.theme-dark .chat-header{background:linear-gradient(135deg,#1a1a1a,#333)}.theme-dark .chat-messages{background:#2c2c2c}.theme-dark .ai-message .message-content{background:#3a3a3a;color:#fff;border-color:#555}.theme-dark .chat-input{background:#3a3a3a;border-color:#555}.theme-dark .inquiry-textarea{background:#2c2c2c;color:#fff;border-color:#555}.theme-dark .inquiry-textarea:focus{border-color:#667eea}.theme-dark .attachments-preview{background:#3a3a3a;border-color:#555}.theme-dark .attachment-item{background:#2c2c2c;border-color:#555;color:#fff}.theme-dark .upload-button{background:rgba(255,255,255,.1);border-color:#fff3}.theme-dark .upload-button:hover:not(:disabled){background:rgba(255,255,255,.2)}.theme-blue{background:#e3f2fd;--icon-primary: #1976d2;--icon-secondary: #546e7a;--icon-avatar-user: #1976d2;--icon-avatar-ai: #0288d1;--icon-header: #ffffff;--icon-upload: #1976d2;--icon-send: #ffffff;--icon-active: #1976d2;--icon-disabled: #b0bec5;--icon-hover: #1565c0}.theme-blue .chat-header{background:linear-gradient(135deg,#1976d2,#42a5f5)}.theme-blue .user-message .message-content{background:linear-gradient(135deg,#1976d2,#42a5f5)}.theme-blue .ai-message .message-content{border-color:#bbdefb}.theme-blue .inquiry-textarea:focus{border-color:#1976d2}.theme-blue .send-button{background:linear-gradient(135deg,#1976d2,#42a5f5)}.theme-blue .upload-button{background:rgba(25,118,210,.1);border-color:#1976d24d}.theme-blue .upload-button:hover:not(:disabled){background:rgba(25,118,210,.2)}.theme-green{background:#e8f5e8;--icon-primary: #388e3c;--icon-secondary: #546e7a;--icon-avatar-user: #388e3c;--icon-avatar-ai: #2e7d32;--icon-header: #ffffff;--icon-upload: #388e3c;--icon-send: #ffffff;--icon-active: #388e3c;--icon-disabled: #b0bec5;--icon-hover: #2e7d32}.theme-green .chat-header{background:linear-gradient(135deg,#388e3c,#66bb6a)}.theme-green .user-message .message-content{background:linear-gradient(135deg,#388e3c,#66bb6a)}.theme-green .ai-message .message-content{border-color:#c8e6c9}.theme-green .inquiry-textarea:focus{border-color:#388e3c}.theme-green .send-button{background:linear-gradient(135deg,#388e3c,#66bb6a)}.theme-green .upload-button{background:rgba(56,142,60,.1);border-color:#388e3c4d}.theme-green .upload-button:hover:not(:disabled){background:rgba(56,142,60,.2)}.theme-purple{background:#f3e5f5;--icon-primary: #7b1fa2;--icon-secondary: #6a1b9a;--icon-avatar-user: #7b1fa2;--icon-avatar-ai: #8e24aa;--icon-header: #ffffff;--icon-upload: #7b1fa2;--icon-send: #ffffff;--icon-active: #7b1fa2;--icon-disabled: #ce93d8;--icon-hover: #6a1b9a}.theme-purple .chat-header{background:linear-gradient(135deg,#7b1fa2,#ab47bc)}.theme-purple .user-message .message-content{background:linear-gradient(135deg,#7b1fa2,#ab47bc)}.theme-purple .ai-message .message-content{border-color:#e1bee7}.theme-purple .inquiry-textarea:focus{border-color:#7b1fa2}.theme-purple .send-button{background:linear-gradient(135deg,#7b1fa2,#ab47bc)}.theme-purple .upload-button{background:rgba(123,31,162,.1);border-color:#7b1fa24d}.theme-purple .upload-button:hover:not(:disabled){background:rgba(123,31,162,.2)}.theme-minimal{background:#fafafa;--icon-primary: #333333;--icon-secondary: #757575;--icon-avatar-user: #424242;--icon-avatar-ai: #616161;--icon-header: #333333;--icon-upload: #424242;--icon-send: #ffffff;--icon-active: #333333;--icon-disabled: #bdbdbd;--icon-hover: #212121}.theme-minimal .chat-header{background:#fff;color:#333;border-bottom:1px solid #e0e0e0}.theme-minimal .user-message .message-content{background:#007bff;color:#fff;border-radius:18px}.theme-minimal .ai-message .message-content{background:#f8f9fa;color:#333;border:1px solid #dee2e6}.theme-minimal .send-button{background:#007bff;border-radius:4px}.theme-minimal .upload-button{background:rgba(0,123,255,.1);border-color:#007bff4d}.theme-minimal .upload-button:hover:not(:disabled){background:rgba(0,123,255,.2)}.theme-corporate{background:#f8f9fa}.theme-corporate .chat-header{background:linear-gradient(135deg,#495057,#6c757d)}.theme-corporate .user-message .message-content{background:#495057}.theme-corporate .ai-message .message-content{background:#fff;border-color:#dee2e6}.theme-corporate .inquiry-textarea:focus{border-color:#495057}.theme-corporate .send-button{background:#495057}.theme-corporate .upload-button{background:rgba(73,80,87,.1);border-color:#4950574d}.theme-corporate .upload-button:hover:not(:disabled){background:rgba(73,80,87,.2)}.theme-red{background:#ffebee}.theme-red .chat-header{background:linear-gradient(135deg,#d32f2f,#f44336)}.theme-red .user-message .message-content{background:linear-gradient(135deg,#d32f2f,#f44336)}.theme-red .ai-message .message-content{border-color:#ffcdd2}.theme-red .inquiry-textarea:focus{border-color:#d32f2f}.theme-red .send-button{background:linear-gradient(135deg,#d32f2f,#f44336)}.theme-red .upload-button{background:rgba(211,47,47,.1);border-color:#d32f2f4d}.theme-red .upload-button:hover:not(:disabled){background:rgba(211,47,47,.2)}.theme-yellow{background:#fffde7}.theme-yellow .chat-header{background:linear-gradient(135deg,#f57f17,#ffb300)}.theme-yellow .user-message .message-content{background:linear-gradient(135deg,#f57f17,#ffb300)}.theme-yellow .ai-message .message-content{border-color:#fff9c4}.theme-yellow .inquiry-textarea:focus{border-color:#f57f17}.theme-yellow .send-button{background:linear-gradient(135deg,#f57f17,#ffb300)}.theme-yellow .upload-button{background:rgba(245,127,23,.1);border-color:#f57f174d}.theme-yellow .upload-button:hover:not(:disabled){background:rgba(245,127,23,.2)}.theme-orange{background:#fff3e0}.theme-orange .chat-header{background:linear-gradient(135deg,#e65100,#ff9800)}.theme-orange .user-message .message-content{background:linear-gradient(135deg,#e65100,#ff9800)}.theme-orange .ai-message .message-content{border-color:#ffe0b2}.theme-orange .inquiry-textarea:focus{border-color:#e65100}.theme-orange .send-button{background:linear-gradient(135deg,#e65100,#ff9800)}.theme-orange .upload-button{background:rgba(230,81,0,.1);border-color:#e651004d}.theme-orange .upload-button:hover:not(:disabled){background:rgba(230,81,0,.2)}\n"], directives: [{ type: i3__namespace.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i3__namespace.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i3__namespace.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3__namespace.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i4__namespace.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i4__namespace.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i4__namespace.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
2393
|
+
AiChatComponent.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: AiChatComponent, deps: [{ token: AiAgentService }, { token: ConversationStateService }, { token: i3__namespace.DomSanitizer }], target: i0__namespace.ɵɵFactoryTarget.Component });
|
|
2394
|
+
AiChatComponent.ɵcmp = i0__namespace.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.17", type: AiChatComponent, selector: "emanate-ai-chat", inputs: { title: "title", placeholder: "placeholder", showDebugInfo: "showDebugInfo", apiUrl: "apiUrl", appKey: "appKey", appSource: "appSource", userId: "userId", firstName: "firstName", userName: "userName", templateDesign: "templateDesign", welcomeMessage: "welcomeMessage", historicalMessages: "historicalMessages", conversationId: "conversationId", enableImageUpload: "enableImageUpload", enableFileUpload: "enableFileUpload", iconSet: "iconSet", customIcons: "customIcons", enableAnimations: "enableAnimations", animationSpeed: "animationSpeed", enableStatusTransitions: "enableStatusTransitions", enableConversationLifecycle: "enableConversationLifecycle", enableAutoConversationRecovery: "enableAutoConversationRecovery", showCloseButton: "showCloseButton", showNewConversationButton: "showNewConversationButton", confirmNewConversation: "confirmNewConversation", newConversationConfirmMessage: "newConversationConfirmMessage" }, outputs: { messageReceived: "messageReceived", conversationStarted: "conversationStarted", messageSent: "messageSent", fileUploaded: "fileUploaded", sizeChanged: "sizeChanged" }, host: { listeners: { "document:keydown.escape": "handleEscapeKey($event)" } }, usesOnChanges: true, ngImport: i0__namespace, template: "<div class=\"ai-chat-container\" \r\n [ngClass]=\"getThemeClass()\" \r\n [ngStyle]=\"getContainerStyle()\"\r\n [class.fullscreen]=\"isFullscreen\"\r\n [class.resizing]=\"isResizing\"\r\n [class]=\"getAnimationClass()\">\r\n \r\n <!-- Resize Handles -->\r\n <div class=\"resize-handle resize-handle-right\" \r\n (mousedown)=\"startResize($event, 'width')\"\r\n *ngIf=\"!isFullscreen\"\r\n title=\"Drag to resize width\">\r\n </div>\r\n \r\n <div class=\"resize-handle resize-handle-corner\" \r\n (mousedown)=\"startResize($event, 'both')\"\r\n *ngIf=\"!isFullscreen\"\r\n title=\"Drag to resize\">\r\n </div>\r\n \r\n <!-- Debug Information (only shown when showDebugInfo is true) -->\r\n <div *ngIf=\"showDebugInfo\" class=\"debug-info\">\r\n <h3>DEBUG: AI Chat Component Loaded</h3>\r\n <p>Status: {{ configurationStatus || 'Loading...' }}</p>\r\n <p>Messages count: {{ messages.length || 0 }}</p>\r\n <p>Theme: {{ templateDesign || 'default' }}</p>\r\n <p>Size: {{ currentSize }} ({{ containerWidth }}x{{ containerHeight }})</p>\r\n </div>\r\n\r\n <div class=\"chat-header\">\r\n <div class=\"header-left\">\r\n <h2>{{ title }}</h2>\r\n <!-- Icon-only status indicator -->\r\n <div class=\"status-indicator-icon\" \r\n [ngClass]=\"{'status-ready': configurationStatus === 'AI Agent is ready' || configurationStatus === 'Ready', \r\n 'status-error': configurationStatus !== 'AI Agent is ready' && configurationStatus !== 'Ready' && configurationStatus !== 'Initializing...' && configurationStatus !== 'Testing connection...',\r\n 'status-loading': configurationStatus === 'Initializing...' || configurationStatus === 'Testing connection...',\r\n 'transition-enabled': enableStatusTransitions}\"\r\n [title]=\"configurationStatus\">\r\n </div>\r\n </div>\r\n \r\n <div class=\"header-controls\">\r\n <!-- New Conversation Button -->\r\n <button *ngIf=\"showNewConversationButton && enableConversationLifecycle && conversationId\" \r\n class=\"control-button new-conversation-button prominent-button\" \r\n (click)=\"startNewConversationExplicit()\"\r\n title=\"Start New Conversation\"\r\n attr.aria-label=\"Start New Conversation\">\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path>\r\n <line x1=\"9\" y1=\"10\" x2=\"15\" y2=\"10\"></line>\r\n <line x1=\"12\" y1=\"7\" x2=\"12\" y2=\"13\"></line>\r\n </svg>\r\n <span class=\"button-text\">New Chat</span>\r\n </button>\r\n \r\n <!-- Close Conversation Button -->\r\n <button *ngIf=\"showCloseButton && enableConversationLifecycle && conversationId\" \r\n class=\"control-button close-button\" \r\n (click)=\"closeConversation('UserCompleted')\"\r\n title=\"Close Conversation\"\r\n attr.aria-label=\"Close Conversation\">\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n </button>\r\n \r\n <!-- Size Preset Buttons -->\r\n <div class=\"size-controls\" *ngIf=\"!isFullscreen\">\r\n <button class=\"control-button size-button\" \r\n (click)=\"applySizePreset('compact')\"\r\n [class.active]=\"currentSize === 'compact'\"\r\n title=\"Compact View\"\r\n attr.aria-label=\"Compact View\">\r\n <span class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.compactView\"></span>\r\n </button>\r\n <button class=\"control-button size-button\" \r\n (click)=\"applySizePreset('default')\"\r\n [class.active]=\"currentSize === 'default'\"\r\n title=\"Default View\"\r\n attr.aria-label=\"Default View\">\r\n <span class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.defaultView\"></span>\r\n </button>\r\n <button class=\"control-button size-button\" \r\n (click)=\"applySizePreset('expanded')\"\r\n [class.active]=\"currentSize === 'expanded'\"\r\n title=\"Expanded View\"\r\n attr.aria-label=\"Expanded View\">\r\n <span class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.expandedView\"></span>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"chat-messages\" #messagesContainer>\r\n <div *ngFor=\"let message of messages; let i = index; trackBy: trackByIndex\" class=\"message-wrapper\">\r\n <!-- Message -->\r\n <div class=\"message\" \r\n [ngClass]=\"{'user-message': message.isUser, 'ai-message': !message.isUser, 'loading-message': message.isLoading}\">\r\n \r\n <div class=\"message-content\">\r\n <div [innerHTML]=\"message.content\"></div>\r\n <div class=\"message-timestamp\">{{ getFormattedTime(message.timestamp) }}</div>\r\n </div>\r\n \r\n <div class=\"message-avatar\">\r\n <span *ngIf=\"message.isUser\" class=\"avatar-icon\" [innerHTML]=\"sanitizedIcons.userAvatar\"></span>\r\n <span *ngIf=\"!message.isUser && !message.isLoading\" class=\"avatar-icon\" [innerHTML]=\"sanitizedIcons.aiAvatar\"></span>\r\n <span *ngIf=\"message.isLoading\" class=\"avatar-icon loading-spinner\" [innerHTML]=\"sanitizedIcons.loadingAvatar\"></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"chat-input\">\r\n <!-- Attachments Preview -->\r\n <div class=\"attachments-preview\" *ngIf=\"selectedFiles.length > 0\">\r\n <div class=\"attachment-item\" *ngFor=\"let file of selectedFiles\">\r\n <svg class=\"attachment-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\"></path>\r\n </svg>\r\n <span class=\"attachment-name\">{{ file.name }}</span>\r\n <span class=\"attachment-size\">{{ getFileSizeFormatted(file.size) }}</span>\r\n <button class=\"remove-attachment\" (click)=\"removeAttachment(file.id)\" title=\"Remove\" attr.aria-label=\"Remove attachment\">\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n \r\n <div class=\"input-container\">\r\n <!-- File Upload Inputs (Hidden) -->\r\n <input type=\"file\" \r\n id=\"image-upload-input\" \r\n accept=\"image/*\" \r\n multiple \r\n (change)=\"onImageUpload($event)\"\r\n style=\"display: none;\"\r\n [disabled]=\"!enableImageUpload\">\r\n \r\n <input type=\"file\" \r\n id=\"file-upload-input\" \r\n accept=\".pdf,.doc,.docx,.txt,.csv\" \r\n multiple \r\n (change)=\"onFileUpload($event)\"\r\n style=\"display: none;\"\r\n [disabled]=\"!enableFileUpload\">\r\n \r\n <!-- Upload Buttons (Conditionally rendered) -->\r\n <div class=\"upload-buttons\" *ngIf=\"enableImageUpload || enableFileUpload\">\r\n <button *ngIf=\"enableImageUpload\" \r\n class=\"upload-button image-upload\" \r\n (click)=\"triggerImageUpload()\"\r\n [disabled]=\"isLoading\"\r\n title=\"Upload Image\"\r\n attr.aria-label=\"Upload Image\">\r\n <span class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.imageUpload\"></span>\r\n </button>\r\n \r\n <button *ngIf=\"enableFileUpload\" \r\n class=\"upload-button file-upload\" \r\n (click)=\"triggerFileUpload()\"\r\n [disabled]=\"isLoading\"\r\n title=\"Attach File\"\r\n attr.aria-label=\"Attach File\">\r\n <span class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.fileUpload\"></span>\r\n </button>\r\n </div>\r\n \r\n <textarea \r\n [(ngModel)]=\"currentInquiry\" \r\n (keypress)=\"onKeyPress($event)\"\r\n [placeholder]=\"placeholder\"\r\n rows=\"2\"\r\n [disabled]=\"isLoading\"\r\n class=\"inquiry-textarea\"\r\n attr.aria-label=\"Type your message\"></textarea>\r\n \r\n <button \r\n (click)=\"sendInquiry()\" \r\n [disabled]=\"!currentInquiry.trim() || isLoading\"\r\n class=\"send-button\"\r\n title=\"Send Message\"\r\n attr.aria-label=\"Send Message\">\r\n <span *ngIf=\"!isLoading\" class=\"icon-svg\" [innerHTML]=\"sanitizedIcons.send\"></span>\r\n <span *ngIf=\"isLoading\" class=\"icon-svg loading-icon\" [innerHTML]=\"sanitizedIcons.sendLoading\"></span>\r\n <span class=\"text\">{{ isLoading ? 'Sending...' : 'Send' }}</span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Confirmation Modal -->\r\n <div class=\"confirmation-modal-backdrop\" \r\n *ngIf=\"showConfirmationModal\"\r\n (click)=\"cancelConfirmation()\">\r\n <div class=\"confirmation-modal\" \r\n [ngClass]=\"getThemeClass()\"\r\n (click)=\"$event.stopPropagation()\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n [attr.aria-labelledby]=\"'modal-title-' + conversationId\"\r\n [attr.aria-describedby]=\"'modal-description-' + conversationId\">\r\n \r\n <div class=\"modal-icon warning-icon\">\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"></path>\r\n <line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"></line>\r\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"></line>\r\n </svg>\r\n </div>\r\n \r\n <h3 class=\"modal-title\" [id]=\"'modal-title-' + conversationId\">Start New Conversation?</h3>\r\n \r\n <p class=\"modal-message\" [id]=\"'modal-description-' + conversationId\">\r\n {{ newConversationConfirmMessage }}\r\n </p>\r\n \r\n <div class=\"modal-actions\">\r\n <button class=\"modal-button secondary-button\" \r\n (click)=\"cancelConfirmation()\"\r\n type=\"button\"\r\n attr.aria-label=\"Cancel\">\r\n Cancel\r\n </button>\r\n <button class=\"modal-button primary-button\" \r\n (click)=\"onConfirmNewConversation()\"\r\n type=\"button\"\r\n attr.aria-label=\"Start New Conversation\">\r\n Start New Chat\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>", styles: ["@charset \"UTF-8\";.ai-chat-container{display:flex;flex-direction:column;height:100vh;max-width:800px;margin:0 auto;background:#ffffff;border-radius:12px;overflow:hidden;position:relative;transition:all .25s cubic-bezier(.4,0,.2,1);box-shadow:0 2px 8px #00000014,0 4px 16px #0000000a;--icon-primary: #667eea;--icon-secondary: #6b7280;--icon-avatar-user: #667eea;--icon-avatar-ai: #764ba2;--icon-header: #ffffff;--icon-upload: #667eea;--icon-send: #ffffff;--icon-active: #667eea;--icon-disabled: #d1d5db;--icon-hover: #5568d3}.ai-chat-container.fullscreen{position:fixed!important;top:0!important;left:0!important;right:0!important;bottom:0!important;width:100vw!important;height:100vh!important;max-width:100vw!important;max-height:100vh!important;border-radius:0;z-index:99999;margin:0}.ai-chat-container.resizing{-webkit-user-select:none;user-select:none;cursor:nwse-resize;transition:none}.resize-handle{position:absolute;background:rgba(102,126,234,.3);transition:background .2s ease;z-index:10}.resize-handle:hover{background:rgba(102,126,234,.6)}.resize-handle.resize-handle-right{top:0;right:-4px;width:8px;height:100%;cursor:ew-resize}.resize-handle.resize-handle-corner{bottom:-4px;right:-4px;width:20px;height:20px;cursor:nwse-resize;border-radius:0 0 8px}.resize-handle.resize-handle-corner:after{content:\"\\22f0\";position:absolute;bottom:2px;right:2px;font-size:12px;color:#fffc}.debug-info{background:yellow;padding:10px;border:1px solid orange}.debug-info h3{margin:0;font-size:1.2rem}.debug-info p{margin:5px 0}.icon-svg{display:inline-block;width:20px;height:20px;color:var(--icon-secondary)}.icon-svg svg{width:100%;height:100%;stroke-width:2;transition:all .2s ease}.avatar-icon{display:inline-block;width:28px;height:28px}.avatar-icon svg{width:100%;height:100%}.chat-header{background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:#fff;padding:1rem 1.25rem;display:flex;justify-content:space-between;align-items:center;grid-gap:1rem;gap:1rem;flex-wrap:wrap;box-shadow:0 2px 4px #0000001a}.chat-header .header-left{display:flex;align-items:center;grid-gap:.75rem;gap:.75rem;flex:1;min-width:200px}.chat-header .header-left h2{margin:0;font-size:1.25rem;font-weight:600;letter-spacing:-.01em}.chat-header .header-controls{display:flex;align-items:center;grid-gap:.5rem;gap:.5rem}.chat-header .header-controls .icon-svg{color:var(--icon-header)}.chat-header .status-indicator-icon{width:10px;height:10px;border-radius:50%;background:rgba(255,255,255,.4);transition:all .3s ease;box-shadow:0 0 0 2px #fff3;cursor:help}.chat-header .status-indicator-icon.transition-enabled{transition:background-color .4s cubic-bezier(.4,0,.2,1),box-shadow .4s cubic-bezier(.4,0,.2,1),transform .3s cubic-bezier(.4,0,.2,1)}.chat-header .status-indicator-icon.status-ready{background:#2ecc71;box-shadow:0 0 0 2px #2ecc714d,0 0 8px #2ecc7166}.chat-header .status-indicator-icon.status-ready.transition-enabled{animation:statusReady .5s cubic-bezier(.4,0,.2,1)}.chat-header .status-indicator-icon.status-error{background:#e74c3c;box-shadow:0 0 0 2px #e74c3c4d,0 0 8px #e74c3c66}.chat-header .status-indicator-icon.status-error.transition-enabled{animation:statusError .5s cubic-bezier(.4,0,.2,1)}.chat-header .status-indicator-icon.status-loading{background:#f39c12;box-shadow:0 0 0 2px #f39c124d;animation:pulse 1.5s ease-in-out infinite}.chat-header .status-indicator-icon.status-loading.transition-enabled{animation:pulse 1.5s ease-in-out infinite,statusLoading .5s cubic-bezier(.4,0,.2,1)}@keyframes pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.6;transform:scale(1.1)}}@keyframes statusReady{0%{transform:scale(.8);opacity:0}50%{transform:scale(1.2)}to{transform:scale(1);opacity:1}}@keyframes statusError{0%{transform:scale(.8);opacity:0}50%{transform:scale(1.15)}to{transform:scale(1);opacity:1}}@keyframes statusLoading{0%{transform:scale(.8) rotate(0);opacity:0}to{transform:scale(1) rotate(360deg);opacity:1}}.ai-chat-container{--animation-duration: .25s;--animation-easing: cubic-bezier(.4, 0, .2, 1)}.ai-chat-container.animation-speed-slow{--animation-duration: .5s}.ai-chat-container.animation-speed-slow *{transition-duration:.5s!important}.ai-chat-container.animation-speed-slow .status-indicator-icon.transition-enabled{transition-duration:.6s!important}.ai-chat-container.animation-speed-slow .message{animation-duration:.6s!important}.ai-chat-container.animation-speed-fast{--animation-duration: .15s}.ai-chat-container.animation-speed-fast *{transition-duration:.15s!important}.ai-chat-container.animation-speed-fast .status-indicator-icon.transition-enabled{transition-duration:.2s!important}.ai-chat-container.animation-speed-fast .message{animation-duration:.2s!important}.ai-chat-container.no-animations{--animation-duration: 0ms}.ai-chat-container.no-animations *,.ai-chat-container.no-animations *:before,.ai-chat-container.no-animations *:after{animation-duration:0ms!important;animation-delay:0ms!important;transition-duration:0ms!important;transition-delay:0ms!important}.ai-chat-container.no-animations .status-indicator-icon{animation:none!important;transition:none!important}.size-controls{display:flex;grid-gap:.5rem;gap:.5rem;background:transparent;padding:0;border-radius:0;backdrop-filter:none}.size-controls .size-button{width:24px!important;height:24px!important;padding:0!important}.size-controls .size-button .icon-svg{width:14px!important;height:14px!important}.control-button{background:transparent;border:none;border-radius:0;width:28px;height:28px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s cubic-bezier(.4,0,.2,1);color:#fff;padding:0}.control-button .icon-svg{width:18px;height:18px;color:var(--icon-header)}.control-button:hover:not(:disabled){opacity:.8;transform:scale(1.1)}.control-button:active:not(:disabled){transform:scale(.95)}.control-button.active{opacity:1;transform:scale(1.15)}.control-button:disabled{opacity:.4;cursor:not-allowed}.prominent-button{background:rgba(255,255,255,.2)!important;border-radius:6px!important;padding:6px 12px!important;width:auto!important;height:auto!important;grid-gap:6px;gap:6px;backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,.3);font-size:13px;font-weight:500}.prominent-button .button-text{color:#fff;white-space:nowrap}.prominent-button svg{width:16px;height:16px}.prominent-button:hover:not(:disabled){background:rgba(255,255,255,.3)!important;transform:translateY(-1px);box-shadow:0 2px 8px #00000026}.prominent-button:active:not(:disabled){transform:translateY(0)}.fullscreen-button{background:transparent}.fullscreen-button:hover:not(:disabled){opacity:.8;transform:scale(1.1)}.clear-button{background:transparent}.clear-button:hover:not(:disabled){opacity:.8;transform:scale(1.1);color:#fcc}.chat-messages{flex:1;overflow-y:auto;overflow-x:hidden;padding:1.25rem;display:flex;flex-direction:column;grid-gap:.875rem;gap:.875rem;scroll-behavior:smooth;background:#f8f9fa}.chat-messages::-webkit-scrollbar{width:6px}.chat-messages::-webkit-scrollbar-track{background:transparent}.chat-messages::-webkit-scrollbar-thumb{background:rgba(0,0,0,.15);border-radius:3px}.chat-messages::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.25)}.message-wrapper{display:flex;flex-direction:column}.message{display:flex;max-width:75%;animation:fadeInSlide .25s cubic-bezier(.4,0,.2,1)}.message.user-message{align-self:flex-end;flex-direction:row-reverse}.message.user-message .message-content{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-radius:18px 18px 4px;box-shadow:0 1px 2px #0000001a}.message.user-message .avatar-icon{color:var(--icon-avatar-user)}.message.ai-message{align-self:flex-start}.message.ai-message .message-content{background:white;color:#1a1a1a;border-radius:18px 18px 18px 4px;border:1px solid #e8e8e8;box-shadow:0 1px 2px #0000000d}.message.ai-message .avatar-icon{color:var(--icon-avatar-ai)}.message.loading-message{opacity:.7}.message.loading-message .message-content{font-style:italic}@keyframes fadeInSlide{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.message-content{padding:.75rem 1rem;margin:0 .5rem;word-wrap:break-word;max-width:100%;line-height:1.5;font-size:.9375rem}.message-timestamp{font-size:.6875rem;opacity:.65;margin-top:.375rem;text-align:right;font-weight:500}.message-avatar{width:36px;height:36px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.message-avatar .avatar-icon{opacity:.9}.loading-spinner{animation:spin 1.2s linear infinite;opacity:.7}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.chat-input{background:white;padding:1rem 1.25rem;border-top:1px solid #e8e8e8;display:flex;flex-direction:column;grid-gap:.75rem;gap:.75rem;box-shadow:0 -2px 8px #0000000a}.input-container{display:flex;grid-gap:.5rem;gap:.5rem;align-items:flex-end}.upload-buttons{display:flex;grid-gap:.5rem;gap:.5rem;flex-direction:column}.upload-button{background:#f5f5f5;border:1px solid #e0e0e0;border-radius:10px;width:42px;height:42px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s cubic-bezier(.4,0,.2,1)}.upload-button .icon-svg{width:20px;height:20px;color:var(--icon-upload)}.upload-button:hover:not(:disabled){background:var(--icon-upload);border-color:var(--icon-upload);transform:translateY(-1px);box-shadow:0 2px 8px #667eea40}.upload-button:hover:not(:disabled) .icon-svg{color:#fff}.upload-button:active:not(:disabled){transform:translateY(0)}.upload-button:disabled{opacity:.4;cursor:not-allowed;background:#fafafa}.attachments-preview{display:flex;flex-wrap:wrap;grid-gap:.5rem;gap:.5rem;padding:.75rem;background:#f8f9fa;border-radius:10px;border:1px solid #e8e8e8}.attachment-item{display:flex;align-items:center;grid-gap:.625rem;gap:.625rem;background:white;border:1px solid #e0e0e0;border-radius:8px;padding:.625rem .875rem;font-size:.875rem;transition:all .2s ease}.attachment-item:hover{border-color:#667eea;box-shadow:0 2px 4px #667eea1a}.attachment-item .attachment-icon{width:20px;height:20px;color:#667eea;flex-shrink:0}.attachment-item .attachment-name{font-weight:500;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#1a1a1a}.attachment-item .attachment-size{color:#666;font-size:.75rem;font-weight:500}.attachment-item .remove-attachment{background:transparent;border:none;color:#999;cursor:pointer;padding:.25rem;margin-left:.25rem;transition:all .2s ease;display:flex;align-items:center;justify-content:center;border-radius:4px}.attachment-item .remove-attachment svg{width:16px;height:16px}.attachment-item .remove-attachment:hover{color:#e74c3c;background:rgba(231,76,60,.1)}.inquiry-textarea{flex:1;border:1.5px solid #e0e0e0;border-radius:22px;padding:.875rem 1.125rem;font-family:inherit;font-size:.9375rem;line-height:1.5;resize:none;outline:none;transition:all .2s cubic-bezier(.4,0,.2,1);background:#fafafa}.inquiry-textarea:focus{border-color:#667eea;background:white;box-shadow:0 0 0 3px #667eea14}.inquiry-textarea:disabled{background:#f5f5f5;cursor:not-allowed;opacity:.6}.inquiry-textarea::placeholder{color:#999}.send-button{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border:none;border-radius:50px;padding:.875rem 1.75rem;font-size:.9375rem;font-weight:600;cursor:pointer;display:flex;align-items:center;grid-gap:.5rem;gap:.5rem;transition:all .2s cubic-bezier(.4,0,.2,1);min-width:110px;justify-content:center;box-shadow:0 2px 8px #667eea40}.send-button .icon-svg{width:18px;height:18px;color:var(--icon-send)}.send-button .loading-icon{animation:spin 1.2s linear infinite}.send-button:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 4px 16px #667eea66}.send-button:active:not(:disabled){transform:translateY(0)}.send-button:disabled{background:#d0d0d0;cursor:not-allowed;box-shadow:none;opacity:.6}@media (max-width: 768px){.ai-chat-container{max-width:100%;border-radius:0;height:100vh}.ai-chat-container:not(.fullscreen){width:100%!important}.resize-handle{display:none}.size-controls{display:none}.chat-header{padding:.875rem 1rem}.chat-header .header-left h2{font-size:1.125rem}.chat-header .status-indicator-icon{width:8px;height:8px}.control-button{width:36px;height:36px}.control-button .icon-svg{width:18px;height:18px}.message{max-width:85%}.message-content{font-size:.875rem}.upload-buttons{flex-direction:row}.input-container{flex-wrap:wrap}.send-button{min-width:90px;padding:.75rem 1.25rem}.send-button .text{display:none}.chat-input{padding:.875rem 1rem}}@media (max-width: 480px){.chat-header{flex-direction:column;align-items:flex-start;grid-gap:.625rem;gap:.625rem}.chat-header .header-controls{width:100%;justify-content:space-between}.control-button{width:34px;height:34px}.control-button .icon-svg{width:16px;height:16px}.message-content{padding:.625rem .875rem;font-size:.875rem}.attachments-preview{flex-direction:column}.attachment-item{width:100%}.attachment-item .attachment-name{max-width:120px}.send-button{padding:.75rem 1rem;min-width:80px}}.control-button:focus,.upload-button:focus,.send-button:focus,.clear-button:focus{outline:3px solid #667eea;outline-offset:2px}@media (prefers-contrast: high){.control-button,.upload-button,.send-button{border:2px solid currentColor}.message-content{border:2px solid currentColor}}@media (prefers-reduced-motion: reduce){*{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}.theme-dark{background:#2c2c2c;--icon-primary: #818cf8;--icon-secondary: #9ca3af;--icon-avatar-user: #818cf8;--icon-avatar-ai: #a78bfa;--icon-header: #e5e7eb;--icon-upload: #818cf8;--icon-send: #ffffff;--icon-active: #818cf8;--icon-disabled: #4b5563;--icon-hover: #6366f1}.theme-dark .chat-header{background:linear-gradient(135deg,#1a1a1a,#333)}.theme-dark .chat-messages{background:#2c2c2c}.theme-dark .ai-message .message-content{background:#3a3a3a;color:#fff;border-color:#555}.theme-dark .chat-input{background:#3a3a3a;border-color:#555}.theme-dark .inquiry-textarea{background:#2c2c2c;color:#fff;border-color:#555}.theme-dark .inquiry-textarea:focus{border-color:#667eea}.theme-dark .attachments-preview{background:#3a3a3a;border-color:#555}.theme-dark .attachment-item{background:#2c2c2c;border-color:#555;color:#fff}.theme-dark .upload-button{background:rgba(255,255,255,.1);border-color:#fff3}.theme-dark .upload-button:hover:not(:disabled){background:rgba(255,255,255,.2)}.theme-blue{background:#e3f2fd;--icon-primary: #1976d2;--icon-secondary: #546e7a;--icon-avatar-user: #1976d2;--icon-avatar-ai: #0288d1;--icon-header: #ffffff;--icon-upload: #1976d2;--icon-send: #ffffff;--icon-active: #1976d2;--icon-disabled: #b0bec5;--icon-hover: #1565c0}.theme-blue .chat-header{background:linear-gradient(135deg,#1976d2,#42a5f5)}.theme-blue .user-message .message-content{background:linear-gradient(135deg,#1976d2,#42a5f5)}.theme-blue .ai-message .message-content{border-color:#bbdefb}.theme-blue .inquiry-textarea:focus{border-color:#1976d2}.theme-blue .send-button{background:linear-gradient(135deg,#1976d2,#42a5f5)}.theme-blue .upload-button{background:rgba(25,118,210,.1);border-color:#1976d24d}.theme-blue .upload-button:hover:not(:disabled){background:rgba(25,118,210,.2)}.theme-green{background:#e8f5e8;--icon-primary: #388e3c;--icon-secondary: #546e7a;--icon-avatar-user: #388e3c;--icon-avatar-ai: #2e7d32;--icon-header: #ffffff;--icon-upload: #388e3c;--icon-send: #ffffff;--icon-active: #388e3c;--icon-disabled: #b0bec5;--icon-hover: #2e7d32}.theme-green .chat-header{background:linear-gradient(135deg,#388e3c,#66bb6a)}.theme-green .user-message .message-content{background:linear-gradient(135deg,#388e3c,#66bb6a)}.theme-green .ai-message .message-content{border-color:#c8e6c9}.theme-green .inquiry-textarea:focus{border-color:#388e3c}.theme-green .send-button{background:linear-gradient(135deg,#388e3c,#66bb6a)}.theme-green .upload-button{background:rgba(56,142,60,.1);border-color:#388e3c4d}.theme-green .upload-button:hover:not(:disabled){background:rgba(56,142,60,.2)}.theme-purple{background:#f3e5f5;--icon-primary: #7b1fa2;--icon-secondary: #6a1b9a;--icon-avatar-user: #7b1fa2;--icon-avatar-ai: #8e24aa;--icon-header: #ffffff;--icon-upload: #7b1fa2;--icon-send: #ffffff;--icon-active: #7b1fa2;--icon-disabled: #ce93d8;--icon-hover: #6a1b9a}.theme-purple .chat-header{background:linear-gradient(135deg,#7b1fa2,#ab47bc)}.theme-purple .user-message .message-content{background:linear-gradient(135deg,#7b1fa2,#ab47bc)}.theme-purple .ai-message .message-content{border-color:#e1bee7}.theme-purple .inquiry-textarea:focus{border-color:#7b1fa2}.theme-purple .send-button{background:linear-gradient(135deg,#7b1fa2,#ab47bc)}.theme-purple .upload-button{background:rgba(123,31,162,.1);border-color:#7b1fa24d}.theme-purple .upload-button:hover:not(:disabled){background:rgba(123,31,162,.2)}.theme-minimal{background:#fafafa;--icon-primary: #333333;--icon-secondary: #757575;--icon-avatar-user: #424242;--icon-avatar-ai: #616161;--icon-header: #333333;--icon-upload: #424242;--icon-send: #ffffff;--icon-active: #333333;--icon-disabled: #bdbdbd;--icon-hover: #212121}.theme-minimal .chat-header{background:#fff;color:#333;border-bottom:1px solid #e0e0e0}.theme-minimal .user-message .message-content{background:#007bff;color:#fff;border-radius:18px}.theme-minimal .ai-message .message-content{background:#f8f9fa;color:#333;border:1px solid #dee2e6}.theme-minimal .send-button{background:#007bff;border-radius:4px}.theme-minimal .upload-button{background:rgba(0,123,255,.1);border-color:#007bff4d}.theme-minimal .upload-button:hover:not(:disabled){background:rgba(0,123,255,.2)}.theme-corporate{background:#f8f9fa}.theme-corporate .chat-header{background:linear-gradient(135deg,#495057,#6c757d)}.theme-corporate .user-message .message-content{background:#495057}.theme-corporate .ai-message .message-content{background:#fff;border-color:#dee2e6}.theme-corporate .inquiry-textarea:focus{border-color:#495057}.theme-corporate .send-button{background:#495057}.theme-corporate .upload-button{background:rgba(73,80,87,.1);border-color:#4950574d}.theme-corporate .upload-button:hover:not(:disabled){background:rgba(73,80,87,.2)}.theme-red{background:#ffebee}.theme-red .chat-header{background:linear-gradient(135deg,#d32f2f,#f44336)}.theme-red .user-message .message-content{background:linear-gradient(135deg,#d32f2f,#f44336)}.theme-red .ai-message .message-content{border-color:#ffcdd2}.theme-red .inquiry-textarea:focus{border-color:#d32f2f}.theme-red .send-button{background:linear-gradient(135deg,#d32f2f,#f44336)}.theme-red .upload-button{background:rgba(211,47,47,.1);border-color:#d32f2f4d}.theme-red .upload-button:hover:not(:disabled){background:rgba(211,47,47,.2)}.theme-yellow{background:#fffde7}.theme-yellow .chat-header{background:linear-gradient(135deg,#f57f17,#ffb300)}.theme-yellow .user-message .message-content{background:linear-gradient(135deg,#f57f17,#ffb300)}.theme-yellow .ai-message .message-content{border-color:#fff9c4}.theme-yellow .inquiry-textarea:focus{border-color:#f57f17}.theme-yellow .send-button{background:linear-gradient(135deg,#f57f17,#ffb300)}.theme-yellow .upload-button{background:rgba(245,127,23,.1);border-color:#f57f174d}.theme-yellow .upload-button:hover:not(:disabled){background:rgba(245,127,23,.2)}.theme-orange{background:#fff3e0}.theme-orange .chat-header{background:linear-gradient(135deg,#e65100,#ff9800)}.theme-orange .user-message .message-content{background:linear-gradient(135deg,#e65100,#ff9800)}.theme-orange .ai-message .message-content{border-color:#ffe0b2}.theme-orange .inquiry-textarea:focus{border-color:#e65100}.theme-orange .send-button{background:linear-gradient(135deg,#e65100,#ff9800)}.theme-orange .upload-button{background:rgba(230,81,0,.1);border-color:#e651004d}.theme-orange .upload-button:hover:not(:disabled){background:rgba(230,81,0,.2)}.confirmation-modal-backdrop{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.5);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:10000;animation:fadeIn .2s ease-out}.confirmation-modal{background:white;border-radius:12px;padding:24px;max-width:400px;width:90%;box-shadow:0 8px 32px #0003;animation:slideUp .3s ease-out;position:relative}.confirmation-modal .modal-icon{display:flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:50%;margin:0 auto 16px}.confirmation-modal .modal-icon svg{width:28px;height:28px}.confirmation-modal .modal-icon.warning-icon{background:rgba(255,152,0,.1);color:#ff9800}.confirmation-modal .modal-title{margin:0 0 12px;font-size:20px;font-weight:600;text-align:center;color:#1a1a1a}.confirmation-modal .modal-message{margin:0 0 24px;font-size:14px;line-height:1.5;text-align:center;color:#666}.confirmation-modal .modal-actions{display:flex;grid-gap:12px;gap:12px;justify-content:center}.confirmation-modal .modal-button{padding:10px 24px;border-radius:8px;border:none;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;min-width:100px}.confirmation-modal .modal-button:focus{outline:2px solid #4a90e2;outline-offset:2px}.confirmation-modal .modal-button.primary-button{background:linear-gradient(135deg,#4a90e2,#357abd);color:#fff}.confirmation-modal .modal-button.primary-button:hover{background:linear-gradient(135deg,#357abd,#2868a8);transform:translateY(-1px);box-shadow:0 4px 12px #4a90e24d}.confirmation-modal .modal-button.primary-button:active{transform:translateY(0)}.confirmation-modal .modal-button.secondary-button{background:#f5f5f5;color:#666}.confirmation-modal .modal-button.secondary-button:hover{background:#e0e0e0}.confirmation-modal .modal-button.secondary-button:active{background:#d5d5d5}.theme-modern .confirmation-modal{background:linear-gradient(135deg,#ffffff,#f8f9fa)}.theme-modern .confirmation-modal .modal-title{color:#2c3e50}.theme-modern .confirmation-modal .modal-button.primary-button{background:linear-gradient(135deg,#4a90e2,#357abd)}.theme-dark .confirmation-modal{background:#2c2c2c}.theme-dark .confirmation-modal .modal-title{color:#fff}.theme-dark .confirmation-modal .modal-message{color:#b0b0b0}.theme-dark .confirmation-modal .modal-button.secondary-button{background:#3a3a3a;color:#b0b0b0}.theme-dark .confirmation-modal .modal-button.secondary-button:hover{background:#454545}.theme-light .confirmation-modal{background:white;box-shadow:0 8px 32px #0000001a}.theme-nature .confirmation-modal{background:linear-gradient(135deg,#f1f8e9,#ffffff)}.theme-nature .confirmation-modal .modal-icon.warning-icon{background:rgba(139,195,74,.1);color:#689f38}.theme-nature .confirmation-modal .modal-button.primary-button{background:linear-gradient(135deg,#8bc34a,#689f38)}.theme-ocean .confirmation-modal{background:linear-gradient(135deg,#e0f7fa,#ffffff)}.theme-ocean .confirmation-modal .modal-icon.warning-icon{background:rgba(0,188,212,.1);color:#00acc1}.theme-ocean .confirmation-modal .modal-button.primary-button{background:linear-gradient(135deg,#00bcd4,#00acc1)}.theme-sunset .confirmation-modal{background:linear-gradient(135deg,#fff3e0,#ffffff)}.theme-sunset .confirmation-modal .modal-icon.warning-icon{background:rgba(255,152,0,.1);color:#f57c00}.theme-sunset .confirmation-modal .modal-button.primary-button{background:linear-gradient(135deg,#ff9800,#f57c00)}.theme-purple .confirmation-modal{background:linear-gradient(135deg,#f3e5f5,#ffffff)}.theme-purple .confirmation-modal .modal-icon.warning-icon{background:rgba(156,39,176,.1);color:#8e24aa}.theme-purple .confirmation-modal .modal-button.primary-button{background:linear-gradient(135deg,#9c27b0,#7b1fa2)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media (max-width: 480px){.confirmation-modal{max-width:340px;padding:20px}.confirmation-modal .modal-actions{flex-direction:column}.confirmation-modal .modal-actions .modal-button{width:100%}}\n"], directives: [{ type: i4__namespace.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i4__namespace.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i4__namespace.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i4__namespace.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i5__namespace.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i5__namespace.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i5__namespace.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
1464
2395
|
i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: AiChatComponent, decorators: [{
|
|
1465
2396
|
type: i0.Component,
|
|
1466
2397
|
args: [{
|
|
@@ -1468,7 +2399,7 @@
|
|
|
1468
2399
|
templateUrl: './ai-chat.component.html',
|
|
1469
2400
|
styleUrls: ['./ai-chat.component.scss']
|
|
1470
2401
|
}]
|
|
1471
|
-
}], ctorParameters: function () { return [{ type: AiAgentService }, { type:
|
|
2402
|
+
}], ctorParameters: function () { return [{ type: AiAgentService }, { type: ConversationStateService }, { type: i3__namespace.DomSanitizer }]; }, propDecorators: { title: [{
|
|
1472
2403
|
type: i0.Input
|
|
1473
2404
|
}], placeholder: [{
|
|
1474
2405
|
type: i0.Input
|
|
@@ -1508,14 +2439,31 @@
|
|
|
1508
2439
|
type: i0.Input
|
|
1509
2440
|
}], enableStatusTransitions: [{
|
|
1510
2441
|
type: i0.Input
|
|
2442
|
+
}], enableConversationLifecycle: [{
|
|
2443
|
+
type: i0.Input
|
|
2444
|
+
}], enableAutoConversationRecovery: [{
|
|
2445
|
+
type: i0.Input
|
|
2446
|
+
}], showCloseButton: [{
|
|
2447
|
+
type: i0.Input
|
|
2448
|
+
}], showNewConversationButton: [{
|
|
2449
|
+
type: i0.Input
|
|
2450
|
+
}], confirmNewConversation: [{
|
|
2451
|
+
type: i0.Input
|
|
2452
|
+
}], newConversationConfirmMessage: [{
|
|
2453
|
+
type: i0.Input
|
|
1511
2454
|
}], messageReceived: [{
|
|
1512
2455
|
type: i0.Output
|
|
2456
|
+
}], conversationStarted: [{
|
|
2457
|
+
type: i0.Output
|
|
1513
2458
|
}], messageSent: [{
|
|
1514
2459
|
type: i0.Output
|
|
1515
2460
|
}], fileUploaded: [{
|
|
1516
2461
|
type: i0.Output
|
|
1517
2462
|
}], sizeChanged: [{
|
|
1518
2463
|
type: i0.Output
|
|
2464
|
+
}], handleEscapeKey: [{
|
|
2465
|
+
type: i0.HostListener,
|
|
2466
|
+
args: ['document:keydown.escape', ['$event']]
|
|
1519
2467
|
}] } });
|
|
1520
2468
|
|
|
1521
2469
|
var EmanateAiChatLibModule = /** @class */ (function () {
|
|
@@ -1525,15 +2473,15 @@
|
|
|
1525
2473
|
}());
|
|
1526
2474
|
EmanateAiChatLibModule.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: EmanateAiChatLibModule, deps: [], target: i0__namespace.ɵɵFactoryTarget.NgModule });
|
|
1527
2475
|
EmanateAiChatLibModule.ɵmod = i0__namespace.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: EmanateAiChatLibModule, declarations: [EmanateAiChatLibComponent,
|
|
1528
|
-
AiChatComponent], imports: [
|
|
1529
|
-
|
|
2476
|
+
AiChatComponent], imports: [i4.CommonModule,
|
|
2477
|
+
i5.FormsModule,
|
|
1530
2478
|
i1.HttpClientModule], exports: [EmanateAiChatLibComponent,
|
|
1531
2479
|
AiChatComponent] });
|
|
1532
2480
|
EmanateAiChatLibModule.ɵinj = i0__namespace.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: EmanateAiChatLibModule, providers: [
|
|
1533
2481
|
AiAgentService
|
|
1534
2482
|
], imports: [[
|
|
1535
|
-
|
|
1536
|
-
|
|
2483
|
+
i4.CommonModule,
|
|
2484
|
+
i5.FormsModule,
|
|
1537
2485
|
i1.HttpClientModule
|
|
1538
2486
|
]] });
|
|
1539
2487
|
i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: EmanateAiChatLibModule, decorators: [{
|
|
@@ -1544,8 +2492,8 @@
|
|
|
1544
2492
|
AiChatComponent
|
|
1545
2493
|
],
|
|
1546
2494
|
imports: [
|
|
1547
|
-
|
|
1548
|
-
|
|
2495
|
+
i4.CommonModule,
|
|
2496
|
+
i5.FormsModule,
|
|
1549
2497
|
i1.HttpClientModule
|
|
1550
2498
|
],
|
|
1551
2499
|
providers: [
|
|
@@ -1569,6 +2517,7 @@
|
|
|
1569
2517
|
exports.AiAgentService = AiAgentService;
|
|
1570
2518
|
exports.AiChatComponent = AiChatComponent;
|
|
1571
2519
|
exports.BOOTSTRAP_ICONS = BOOTSTRAP_ICONS;
|
|
2520
|
+
exports.ConversationStateService = ConversationStateService;
|
|
1572
2521
|
exports.EmanateAiChatLibComponent = EmanateAiChatLibComponent;
|
|
1573
2522
|
exports.EmanateAiChatLibModule = EmanateAiChatLibModule;
|
|
1574
2523
|
exports.EmanateAiChatLibService = EmanateAiChatLibService;
|