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.
@@ -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, i3, i4, i1, rxjs, operators, i2) { 'use strict';
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 i2__namespace = /*#__PURE__*/_interopNamespace(i2);
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/AIAgent',
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
- console.log('Testing configuration at:', this.config.apiUrl + "/TestConfiguration");
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(this.config.apiUrl + "/TestConfiguration", { headers: headers })
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
- // Clean the request object to remove undefined/null values and ensure conversationId is a string
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
- try {
831
- // Load historical messages if provided
832
- if (this.historicalMessages && this.historicalMessages.length > 0) {
833
- this.loadHistoricalMessages(this.historicalMessages);
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
- else {
836
- this.addWelcomeMessage();
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: this.currentInquiry,
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
- var inquiry = this.currentInquiry.trim();
1051
- this.currentInquiry = '';
1052
- this.authorName = '';
1053
- this.intent = '';
1054
- this.isLoading = true;
1055
- var request = Object.assign(Object.assign(Object.assign(Object.assign({ query: inquiry }, (this.conversationId && { conversationId: String(this.conversationId) })), (this.userId && { userId: this.userId })), (this.userName && { userName: this.userName })), (this.appSource && { appSource: this.appSource }));
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
- // Remove loading message
1066
- _this.messages = _this.messages.filter(function (msg) { return !msg.isLoading; });
1067
- if (response.answer || (response.contents && response.contents.length > 0)) {
1068
- if (response.answer) {
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(response.answer),
1076
- authorName: response.contents && response.contents.length > 0 ? response.contents[0].authorName : undefined,
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
- else if (response.contents && response.contents.length > 0) {
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: i2__namespace.DomSanitizer }], target: i0__namespace.ɵɵFactoryTarget.Component });
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: i2__namespace.DomSanitizer }]; }, propDecorators: { title: [{
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: [i3.CommonModule,
1529
- i4.FormsModule,
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
- i3.CommonModule,
1536
- i4.FormsModule,
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
- i3.CommonModule,
1548
- i4.FormsModule,
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;