@memberjunction/ng-conversations 5.1.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/__tests__/mention-parser.test.js +2 -2
  2. package/dist/__tests__/mention-parser.test.js.map +1 -1
  3. package/dist/lib/components/artifact/artifact-share-modal.component.d.ts.map +1 -1
  4. package/dist/lib/components/artifact/artifact-share-modal.component.js +1 -0
  5. package/dist/lib/components/artifact/artifact-share-modal.component.js.map +1 -1
  6. package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts +3 -2
  7. package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts.map +1 -1
  8. package/dist/lib/components/collection/artifact-collection-picker-modal.component.js +16 -7
  9. package/dist/lib/components/collection/artifact-collection-picker-modal.component.js.map +1 -1
  10. package/dist/lib/components/collection/artifact-create-modal.component.d.ts +3 -2
  11. package/dist/lib/components/collection/artifact-create-modal.component.d.ts.map +1 -1
  12. package/dist/lib/components/collection/artifact-create-modal.component.js +6 -3
  13. package/dist/lib/components/collection/artifact-create-modal.component.js.map +1 -1
  14. package/dist/lib/components/collection/collection-artifact-card.component.d.ts +3 -2
  15. package/dist/lib/components/collection/collection-artifact-card.component.d.ts.map +1 -1
  16. package/dist/lib/components/collection/collection-artifact-card.component.js +8 -3
  17. package/dist/lib/components/collection/collection-artifact-card.component.js.map +1 -1
  18. package/dist/lib/components/collection/collection-share-modal.component.d.ts.map +1 -1
  19. package/dist/lib/components/collection/collection-share-modal.component.js +2 -0
  20. package/dist/lib/components/collection/collection-share-modal.component.js.map +1 -1
  21. package/dist/lib/components/collection/collections-full-view.component.d.ts +19 -3
  22. package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
  23. package/dist/lib/components/collection/collections-full-view.component.js +580 -233
  24. package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
  25. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +52 -14
  26. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  27. package/dist/lib/components/conversation/conversation-chat-area.component.js +243 -108
  28. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  29. package/dist/lib/components/conversation/conversation-list.component.d.ts +3 -1
  30. package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -1
  31. package/dist/lib/components/conversation/conversation-list.component.js +14 -2
  32. package/dist/lib/components/conversation/conversation-list.component.js.map +1 -1
  33. package/dist/lib/components/message/message-input.component.d.ts +3 -3
  34. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  35. package/dist/lib/components/message/message-input.component.js +22 -26
  36. package/dist/lib/components/message/message-input.component.js.map +1 -1
  37. package/dist/lib/components/message/message-item.component.d.ts +2 -2
  38. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  39. package/dist/lib/components/message/message-item.component.js.map +1 -1
  40. package/dist/lib/components/message/message-list.component.d.ts +2 -2
  41. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  42. package/dist/lib/components/message/message-list.component.js +6 -2
  43. package/dist/lib/components/message/message-list.component.js.map +1 -1
  44. package/dist/lib/components/share/share-modal.component.d.ts +3 -2
  45. package/dist/lib/components/share/share-modal.component.d.ts.map +1 -1
  46. package/dist/lib/components/share/share-modal.component.js +9 -3
  47. package/dist/lib/components/share/share-modal.component.js.map +1 -1
  48. package/dist/lib/components/shared/user-picker.component.d.ts +3 -1
  49. package/dist/lib/components/shared/user-picker.component.d.ts.map +1 -1
  50. package/dist/lib/components/shared/user-picker.component.js +7 -2
  51. package/dist/lib/components/shared/user-picker.component.js.map +1 -1
  52. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +4 -1
  53. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
  54. package/dist/lib/components/workspace/conversation-workspace.component.js +10 -4
  55. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
  56. package/dist/lib/models/conversation-state.model.d.ts +3 -3
  57. package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
  58. package/dist/lib/models/lazy-artifact-info.d.ts +32 -31
  59. package/dist/lib/models/lazy-artifact-info.d.ts.map +1 -1
  60. package/dist/lib/models/lazy-artifact-info.js +99 -75
  61. package/dist/lib/models/lazy-artifact-info.js.map +1 -1
  62. package/dist/lib/services/conversation-agent.service.d.ts +3 -3
  63. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  64. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  65. package/dist/lib/services/conversation-streaming.service.d.ts +6 -1
  66. package/dist/lib/services/conversation-streaming.service.d.ts.map +1 -1
  67. package/dist/lib/services/conversation-streaming.service.js +14 -9
  68. package/dist/lib/services/conversation-streaming.service.js.map +1 -1
  69. package/dist/lib/services/mention-autocomplete.service.d.ts +2 -2
  70. package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
  71. package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
  72. package/dist/lib/services/mention-parser.service.d.ts +4 -4
  73. package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
  74. package/dist/lib/services/mention-parser.service.js.map +1 -1
  75. package/package.json +17 -17
@@ -1,5 +1,6 @@
1
1
  import { Component, Input, Output, EventEmitter, ViewChild, ViewChildren } from '@angular/core';
2
2
  import { RunView, RunQuery, Metadata, CompositeKey, LogStatusEx } from '@memberjunction/core';
3
+ import { ArtifactMetadataEngine } from '@memberjunction/core-entities';
3
4
  import { AIEngineBase } from '@memberjunction/ai-engine-base';
4
5
  import { LazyArtifactInfo } from '../../models/lazy-artifact-info';
5
6
  import { parseConversationDetailComplete } from '../../models/conversation-complete-query.model';
@@ -234,7 +235,7 @@ function ConversationChatAreaComponent_Conditional_7_Template(rf, ctx) { if (rf
234
235
  const _r13 = i0.ɵɵgetCurrentView();
235
236
  i0.ɵɵconditionalCreate(0, ConversationChatAreaComponent_Conditional_7_Conditional_0_Template, 1, 0, "div", 57);
236
237
  i0.ɵɵelementStart(1, "div", 58)(2, "mj-artifact-viewer-panel", 59);
237
- i0.ɵɵlistener("closed", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_closed_2_listener() { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onCloseArtifactPanel()); })("saveToCollectionRequested", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_saveToCollectionRequested_2_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onSaveToCollectionRequested($event)); })("navigateToLink", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_navigateToLink_2_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onArtifactLinkNavigation($event)); })("shareRequested", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_shareRequested_2_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onArtifactShareRequested($event)); })("maximizeToggled", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_maximizeToggled_2_listener() { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.toggleMaximizeArtifactPane()); })("openEntityRecord", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_openEntityRecord_2_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onOpenEntityRecord($event)); });
238
+ i0.ɵɵlistener("closed", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_closed_2_listener() { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onCloseArtifactPanel()); })("saveToCollectionRequested", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_saveToCollectionRequested_2_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onSaveToCollectionRequested($event)); })("navigateToLink", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_navigateToLink_2_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onArtifactLinkNavigation($event)); })("shareRequested", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_shareRequested_2_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onArtifactShareRequested($event)); })("maximizeToggled", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_maximizeToggled_2_listener() { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.toggleMaximizeArtifactPane()); })("openEntityRecord", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_openEntityRecord_2_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onOpenEntityRecord($event)); })("navigationRequest", function ConversationChatAreaComponent_Conditional_7_Template_mj_artifact_viewer_panel_navigationRequest_2_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onNavigationRequest($event)); });
238
239
  i0.ɵɵelementEnd()();
239
240
  } if (rf & 2) {
240
241
  const ctx_r2 = i0.ɵɵnextContext();
@@ -480,6 +481,7 @@ export class ConversationChatAreaComponent {
480
481
  showSidebarToggle = false;
481
482
  conversationRenamed = new EventEmitter();
482
483
  openEntityRecord = new EventEmitter();
484
+ navigationRequest = new EventEmitter();
483
485
  taskClicked = new EventEmitter();
484
486
  artifactLinkClicked = new EventEmitter();
485
487
  sidebarToggleClicked = new EventEmitter();
@@ -508,6 +510,7 @@ export class ConversationChatAreaComponent {
508
510
  userAvatarMap = new Map();
509
511
  memberCount = 1;
510
512
  artifactCount = 0;
513
+ artifactCountDisplay = 0;
511
514
  isShared = false;
512
515
  showExportModal = false;
513
516
  showAgentPanel = false;
@@ -544,7 +547,7 @@ export class ConversationChatAreaComponent {
544
547
  systemArtifactsByDetailId = new Map();
545
548
  // Cached combined artifacts map - updated when toggle changes
546
549
  _combinedArtifactsMap = null;
547
- // Agent run mapping: ConversationDetailID -> AIAgentRunEntityExtended
550
+ // Agent run mapping: ConversationDetailID -> MJAIAgentRunEntityExtended
548
551
  // Loaded once per conversation and kept in sync as new runs are created
549
552
  agentRunsByDetailId = new Map();
550
553
  /**
@@ -575,6 +578,9 @@ export class ConversationChatAreaComponent {
575
578
  artifactViewerRefresh$ = new Subject();
576
579
  // Track initialization state to prevent loading messages before agents are ready
577
580
  isInitialized = false;
581
+ // Track whether we had active agents on the current conversation's last poll cycle.
582
+ // Used to detect when polling transitions from active → no active agents (completion via poll).
583
+ hadActiveAgents = false;
578
584
  // Resize state
579
585
  isResizing = false;
580
586
  startX = 0;
@@ -617,6 +623,10 @@ export class ConversationChatAreaComponent {
617
623
  console.warn('⚠️ Mention autocomplete not initialized by workspace, initializing now...');
618
624
  await this.mentionAutocompleteService.initialize(this.currentUser);
619
625
  }
626
+ // Ensure ArtifactMetadataEngine is loaded so LazyArtifactInfo can
627
+ // resolve versions from its always-fresh in-memory cache.
628
+ // Config(false) is a no-op if already loaded by another component.
629
+ await ArtifactMetadataEngine.Instance.Config(false, this.currentUser);
620
630
  // Initialize attachment support based on agent modalities
621
631
  await this.initializeAttachmentSupport();
622
632
  // Load saved artifact pane width
@@ -642,6 +652,27 @@ export class ConversationChatAreaComponent {
642
652
  await this.handleMessageCompletion(message, event.agentRunId);
643
653
  }
644
654
  });
655
+ // Subscribe to polling-based agent state as a secondary fallback for completion detection.
656
+ // The sessionId is persisted in localStorage and reused on refresh, so WebSocket events
657
+ // normally arrive correctly. However, there's a brief timing gap between page load and
658
+ // WebSocket reconnection where events can be lost. The catch-up check in
659
+ // detectAndReconnectToInProgressRuns() is the primary fallback; polling is the last resort.
660
+ this.agentStateService.activeAgents$
661
+ .pipe(takeUntil(this.destroy$))
662
+ .subscribe(async (agents) => {
663
+ if (!this.conversationId)
664
+ return;
665
+ const conversationAgents = agents.filter(a => a.run.ConversationID === this.conversationId);
666
+ const hasActiveAgents = conversationAgents.length > 0;
667
+ if (this.hadActiveAgents && !hasActiveAgents) {
668
+ // Agents just completed — reload messages to pick up final response and any
669
+ // delegated-agent messages that were created during the run.
670
+ this.invalidateConversationCache(this.conversationId);
671
+ await this.reloadMessagesForActiveConversation();
672
+ this.cdr.detectChanges();
673
+ }
674
+ this.hadActiveAgents = hasActiveAgents;
675
+ });
645
676
  }
646
677
  /**
647
678
  * Initializes attachment support by checking if the conversation manager agent (Sage)
@@ -746,6 +777,9 @@ export class ConversationChatAreaComponent {
746
777
  // Clearing causes bugs: global tasks panel blanks out, no notifications when switching
747
778
  this.showArtifactPanel = false;
748
779
  this.selectedArtifactId = null;
780
+ // Reset poll-based completion tracking whenever we switch conversations,
781
+ // so the first empty poll on the new conversation doesn't trigger a spurious reload.
782
+ this.hadActiveAgents = false;
749
783
  if (conversationId) {
750
784
  this.currentlyLoadingConversationId = conversationId;
751
785
  if (!this.messageInputMetadataCache.has(conversationId)) {
@@ -754,9 +788,15 @@ export class ConversationChatAreaComponent {
754
788
  conversationName: this.conversation?.Name || null
755
789
  });
756
790
  }
757
- this.isLoadingConversation = true;
758
- this.messages = [];
759
- this.cdr.detectChanges();
791
+ // Only show loading spinner if we don't have cached data for this conversation.
792
+ // When switching between previously visited conversations, the cache provides
793
+ // instant display without the loading flash.
794
+ const hasCachedData = this.conversationDataCache.has(conversationId);
795
+ if (!hasCachedData) {
796
+ this.isLoadingConversation = true;
797
+ this.messages = [];
798
+ this.cdr.detectChanges();
799
+ }
760
800
  try {
761
801
  await this.loadMessages(conversationId);
762
802
  await this.restoreActiveTasks(conversationId);
@@ -949,7 +989,7 @@ export class ConversationChatAreaComponent {
949
989
  const parsed = parseConversationDetailComplete(row);
950
990
  // Build agent runs map
951
991
  if (parsed.agentRuns.length > 0) {
952
- // Convert AgentRunJSON to AIAgentRunEntityExtended
992
+ // Convert AgentRunJSON to MJAIAgentRunEntityExtended
953
993
  const agentRunData = parsed.agentRuns[0]; // Should only be one per detail
954
994
  const agentRun = await md.GetEntityObject('MJ: AI Agent Runs', this.currentUser);
955
995
  // Convert ISO date strings to Date objects
@@ -1016,6 +1056,7 @@ export class ConversationChatAreaComponent {
1016
1056
  this._combinedArtifactsMap = null;
1017
1057
  // Update artifact count for header display (unique artifacts, not versions)
1018
1058
  this.artifactCount = this.calculateUniqueArtifactCount();
1059
+ this.updateArtifactCountDisplay();
1019
1060
  // Debug: Log summary
1020
1061
  const systemArtifactCount = this.systemArtifactsByDetailId.size;
1021
1062
  const attachmentCount = this.attachmentsByDetailId.size;
@@ -1074,6 +1115,10 @@ export class ConversationChatAreaComponent {
1074
1115
  }
1075
1116
  // Scroll to bottom when new message is sent
1076
1117
  this.scrollToBottom = true;
1118
+ // Force change detection — zone.js 0.15 no longer patches graphql-ws WebSocket callbacks,
1119
+ // so progress updates that arrive via PubSub run outside Angular's zone. Without this,
1120
+ // the UI does not update when the messages array is modified from a streaming callback.
1121
+ this.cdr.detectChanges();
1077
1122
  }
1078
1123
  /**
1079
1124
  * Loads attachments for a single message and adds them to the attachmentsByDetailId map.
@@ -1163,6 +1208,14 @@ export class ConversationChatAreaComponent {
1163
1208
  this.messages = [...this.messages];
1164
1209
  this.cdr.detectChanges();
1165
1210
  }
1211
+ /**
1212
+ * Public entry point to reload messages in the active conversation.
1213
+ * Called by the parent resource wrapper when the user clicks the Refresh button,
1214
+ * so that new agent responses are visible without a full page reload.
1215
+ */
1216
+ async reloadMessages() {
1217
+ await this.reloadMessagesForActiveConversation();
1218
+ }
1166
1219
  /**
1167
1220
  * Reload messages for the active conversation from the database
1168
1221
  * Called when completion is detected to discover newly delegated agent messages
@@ -1185,8 +1238,22 @@ export class ConversationChatAreaComponent {
1185
1238
  ResultType: 'entity_object'
1186
1239
  }, this.currentUser);
1187
1240
  if (result.Success && result.Results && result.Results.length > 0) {
1188
- // Update messages with reloaded data
1189
- this.messages = result.Results;
1241
+ // Merge DB results with existing client-side messages rather than replacing entirely.
1242
+ // This prevents dropping messages added client-side (e.g., by handleSubAgentInvocation)
1243
+ // that haven't appeared in the DB query results yet due to timing.
1244
+ const merged = new Map();
1245
+ // Add all DB messages (fresh data)
1246
+ for (const msg of result.Results) {
1247
+ merged.set(msg.ID, msg);
1248
+ }
1249
+ // Preserve client-side messages not yet in DB
1250
+ for (const msg of this.messages) {
1251
+ if (!merged.has(msg.ID)) {
1252
+ merged.set(msg.ID, msg);
1253
+ }
1254
+ }
1255
+ this.messages = Array.from(merged.values())
1256
+ .sort((a, b) => (a.__mj_CreatedAt?.getTime() || 0) - (b.__mj_CreatedAt?.getTime() || 0));
1190
1257
  // Find newly discovered messages (delegated agents)
1191
1258
  const newMessages = result.Results.filter(m => !existingMessageIds.has(m.ID));
1192
1259
  // Load agent runs for new messages so completion detector can reload artifacts
@@ -1221,6 +1288,8 @@ export class ConversationChatAreaComponent {
1221
1288
  async handleMessageCompletion(message, _agentRunId) {
1222
1289
  try {
1223
1290
  LogStatusEx({ message: `🎉 Handling completion for message ${message.ID}`, verboseOnly: true });
1291
+ // Snapshot artifact IDs before reload to detect newly created artifacts
1292
+ const artifactIdsBefore = this.collectAllArtifactIds();
1224
1293
  // Reload message from database to get final content and status
1225
1294
  await message.Load(message.ID);
1226
1295
  // Reload agent run to get final status, timestamps, and cost
@@ -1233,20 +1302,22 @@ export class ConversationChatAreaComponent {
1233
1302
  // Reload messages to pick up newly delegated agent messages
1234
1303
  // When Sage delegates to Marketing Agent, a new message is created
1235
1304
  await this.reloadMessagesForActiveConversation();
1305
+ // Invalidate cache since reloadMessages may have loaded new delegated-agent messages
1306
+ // that are not in the cache set by reloadArtifactsForMessage().
1307
+ // Without this, navigating away and back would show stale data.
1308
+ if (message.ConversationID) {
1309
+ this.invalidateConversationCache(message.ConversationID);
1310
+ }
1236
1311
  // Update inProgressMessageIds to include new delegated agents
1237
1312
  // This triggers callback registration via the setter in message-input
1238
1313
  this.inProgressMessageIds = [...this.messages
1239
1314
  .filter(m => m.Status === 'In-Progress')
1240
1315
  .map(m => m.ID)];
1241
- // Auto-open artifact panel if this message has artifacts and no artifact is currently shown
1242
- if (this.artifactsByDetailId.has(message.ID) && !this.showArtifactPanel) {
1243
- const artifactList = this.artifactsByDetailId.get(message.ID);
1244
- if (artifactList && artifactList.length > 0) {
1245
- // Show the LAST (most recent) artifact
1246
- this.selectedArtifactId = artifactList[artifactList.length - 1].artifactId;
1247
- this.showArtifactPanel = true;
1248
- await this.loadArtifactPermissions(this.selectedArtifactId);
1249
- }
1316
+ // Auto-open artifact panel if NEW artifacts were discovered (not just the triggering message).
1317
+ // When Sage delegates to a sub-agent (e.g., Skip), the artifact is on the sub-agent's
1318
+ // message, not Sage's. Checking only the triggering message would miss delegated artifacts.
1319
+ if (!this.showArtifactPanel) {
1320
+ await this.autoOpenNewArtifact(artifactIdsBefore);
1250
1321
  }
1251
1322
  // Remove task from ActiveTasksService (clears spinner in conversation list)
1252
1323
  const task = this.activeTasks.getByConversationDetailId(message.ID);
@@ -1260,6 +1331,7 @@ export class ConversationChatAreaComponent {
1260
1331
  }
1261
1332
  catch (error) {
1262
1333
  console.error(`Error handling message completion for ${message.ID}:`, error);
1334
+ this.cdr.detectChanges();
1263
1335
  }
1264
1336
  }
1265
1337
  async onAgentResponse(event) {
@@ -1277,18 +1349,13 @@ export class ConversationChatAreaComponent {
1277
1349
  if (event.agentResult?.agentRun?.ID) {
1278
1350
  await this.addAgentRunToMap(event.message.ID, event.agentResult.agentRun.ID, true); // forceRefresh = true
1279
1351
  }
1352
+ // Snapshot artifact IDs before reload to detect newly created artifacts
1353
+ const artifactIdsBefore = this.collectAllArtifactIds();
1280
1354
  // Reload artifact mapping for this message to pick up newly created artifacts
1281
1355
  await this.reloadArtifactsForMessage(event.message.ID);
1282
- // Auto-open artifact panel if this message has artifacts and no artifact is currently shown
1283
- if (this.artifactsByDetailId.has(event.message.ID) && !this.showArtifactPanel) {
1284
- const artifactList = this.artifactsByDetailId.get(event.message.ID);
1285
- if (artifactList && artifactList.length > 0) {
1286
- // Show the LAST (most recent) artifact - uses display data, no lazy load needed
1287
- this.selectedArtifactId = artifactList[artifactList.length - 1].artifactId;
1288
- this.showArtifactPanel = true;
1289
- // Load permissions for the new artifact
1290
- await this.loadArtifactPermissions(this.selectedArtifactId);
1291
- }
1356
+ // Auto-open artifact panel if NEW artifacts were discovered
1357
+ if (!this.showArtifactPanel) {
1358
+ await this.autoOpenNewArtifact(artifactIdsBefore);
1292
1359
  }
1293
1360
  // Force change detection to update the UI
1294
1361
  this.cdr.detectChanges();
@@ -1331,9 +1398,11 @@ export class ConversationChatAreaComponent {
1331
1398
  }
1332
1399
  }
1333
1400
  /**
1334
- * Reload artifacts for a specific message ID
1335
- * Called after an artifact is created to update the UI immediately
1336
- * Invalidates and refreshes the conversation cache
1401
+ * Reload artifacts for a conversation, triggered by a specific message ID.
1402
+ * Processes ALL messages in the conversation (not just the trigger message)
1403
+ * so that artifacts from delegated sub-agent messages are also picked up.
1404
+ * Called after an artifact is created to update the UI immediately.
1405
+ * Invalidates and refreshes the conversation cache.
1337
1406
  */
1338
1407
  async reloadArtifactsForMessage(conversationDetailId) {
1339
1408
  LogStatusEx({ message: `🔄 Reloading artifacts for message ${conversationDetailId}`, verboseOnly: true });
@@ -1351,7 +1420,7 @@ export class ConversationChatAreaComponent {
1351
1420
  // Use optimized single query to reload all conversation data
1352
1421
  const result = await rq.RunQuery({
1353
1422
  QueryName: 'GetConversationComplete',
1354
- CategoryPath: '/MJ/Conversations',
1423
+ CategoryPath: 'MJ/Conversations',
1355
1424
  Parameters: { ConversationID: detail.ConversationID }
1356
1425
  }, this.currentUser);
1357
1426
  if (!result.Success || !result.Results) {
@@ -1362,47 +1431,60 @@ export class ConversationChatAreaComponent {
1362
1431
  // Update cache with fresh data
1363
1432
  const conversationData = result.Results;
1364
1433
  this.conversationDataCache.set(detail.ConversationID, conversationData);
1365
- // Find the specific conversation detail we're reloading and update its artifacts
1434
+ // Process ALL messages in the conversation to pick up artifacts from
1435
+ // delegated sub-agent messages (e.g., when Sage delegates to Skip,
1436
+ // the artifact is on Skip's message, not Sage's)
1366
1437
  for (const row of conversationData) {
1367
- if (row.ID === conversationDetailId) {
1368
- const parsed = parseConversationDetailComplete(row);
1369
- // Clear existing artifacts for this detail and rebuild
1370
- this.artifactsByDetailId.delete(conversationDetailId);
1371
- this.systemArtifactsByDetailId.delete(conversationDetailId);
1372
- if (parsed.artifacts.length > 0) {
1373
- const artifactList = [];
1374
- const systemArtifactList = [];
1375
- for (const artifactData of parsed.artifacts) {
1376
- const lazyInfo = new LazyArtifactInfo(artifactData, this.currentUser);
1377
- // Separate system-only artifacts from user-visible artifacts
1378
- if (artifactData.Visibility === 'System Only') {
1379
- systemArtifactList.push(lazyInfo);
1380
- }
1381
- else {
1382
- artifactList.push(lazyInfo);
1383
- }
1384
- LogStatusEx({ message: `✅ Loaded artifact ${artifactData.ArtifactID} v${artifactData.VersionNumber} for message ${conversationDetailId}`, verboseOnly: true });
1385
- }
1386
- // Add to appropriate maps
1387
- if (artifactList.length > 0) {
1388
- this.artifactsByDetailId.set(conversationDetailId, artifactList);
1389
- }
1390
- if (systemArtifactList.length > 0) {
1391
- this.systemArtifactsByDetailId.set(conversationDetailId, systemArtifactList);
1392
- }
1393
- }
1394
- // Create new Map reference to trigger Angular change detection
1395
- this.artifactsByDetailId = new Map(this.artifactsByDetailId);
1396
- // Update artifact count
1397
- this.artifactCount = this.calculateUniqueArtifactCount();
1398
- break; // Found and updated the target message
1399
- }
1438
+ this.updateArtifactsForRow(row);
1400
1439
  }
1440
+ // Create new Map reference to trigger Angular change detection
1441
+ this.artifactsByDetailId = new Map(this.artifactsByDetailId);
1442
+ this._combinedArtifactsMap = null; // Clear cache so effectiveArtifactsMap rebuilds
1443
+ // Update artifact count
1444
+ this.artifactCount = this.calculateUniqueArtifactCount();
1445
+ this.updateArtifactCountDisplay();
1401
1446
  }
1402
1447
  catch (error) {
1403
1448
  console.error('Failed to reload artifacts for message:', error);
1404
1449
  }
1405
1450
  }
1451
+ /**
1452
+ * Update artifact maps for a single conversation detail row.
1453
+ * Clears existing entries for the row and rebuilds from parsed data.
1454
+ */
1455
+ updateArtifactsForRow(row) {
1456
+ const rowId = row.ID;
1457
+ if (!rowId) {
1458
+ return;
1459
+ }
1460
+ const parsed = parseConversationDetailComplete(row);
1461
+ // Clear existing artifacts for this detail and rebuild
1462
+ this.artifactsByDetailId.delete(rowId);
1463
+ this.systemArtifactsByDetailId.delete(rowId);
1464
+ if (parsed.artifacts.length === 0) {
1465
+ return;
1466
+ }
1467
+ const artifactList = [];
1468
+ const systemArtifactList = [];
1469
+ for (const artifactData of parsed.artifacts) {
1470
+ const lazyInfo = new LazyArtifactInfo(artifactData, this.currentUser);
1471
+ // Separate system-only artifacts from user-visible artifacts
1472
+ if (artifactData.Visibility === 'System Only') {
1473
+ systemArtifactList.push(lazyInfo);
1474
+ }
1475
+ else {
1476
+ artifactList.push(lazyInfo);
1477
+ }
1478
+ LogStatusEx({ message: `✅ Loaded artifact ${artifactData.ArtifactID} v${artifactData.VersionNumber} for message ${rowId}`, verboseOnly: true });
1479
+ }
1480
+ // Add to appropriate maps
1481
+ if (artifactList.length > 0) {
1482
+ this.artifactsByDetailId.set(rowId, artifactList);
1483
+ }
1484
+ if (systemArtifactList.length > 0) {
1485
+ this.systemArtifactsByDetailId.set(rowId, systemArtifactList);
1486
+ }
1487
+ }
1406
1488
  openProjectSelector() {
1407
1489
  this.showProjectSelector = true;
1408
1490
  }
@@ -1413,18 +1495,19 @@ export class ConversationChatAreaComponent {
1413
1495
  this.showArtifactsModal = true;
1414
1496
  }
1415
1497
  /**
1416
- * Calculate count of unique artifacts (not versions)
1417
- * Works with LazyArtifactInfo - uses artifactId from display data
1418
- * Respects showSystemArtifacts toggle to update count dynamically
1498
+ * Recompute the cached artifactCountDisplay from the effective artifacts map.
1499
+ * Must be called whenever artifactsByDetailId, systemArtifactsByDetailId,
1500
+ * or showSystemArtifacts changes, instead of using a getter that can produce
1501
+ * different values between Angular change-detection passes (NG0100).
1419
1502
  */
1420
- get artifactCountDisplay() {
1503
+ updateArtifactCountDisplay() {
1421
1504
  const uniqueArtifactIds = new Set();
1422
1505
  for (const artifactList of this.effectiveArtifactsMap.values()) {
1423
1506
  for (const info of artifactList) {
1424
1507
  uniqueArtifactIds.add(info.artifactId);
1425
1508
  }
1426
1509
  }
1427
- return uniqueArtifactIds.size;
1510
+ this.artifactCountDisplay = uniqueArtifactIds.size;
1428
1511
  }
1429
1512
  /**
1430
1513
  * Calculate count of unique artifacts (not versions) - user-visible only
@@ -1439,6 +1522,39 @@ export class ConversationChatAreaComponent {
1439
1522
  }
1440
1523
  return uniqueArtifactIds.size;
1441
1524
  }
1525
+ /**
1526
+ * Collect all currently known artifact IDs across all messages.
1527
+ * Used as a "before" snapshot to detect newly created artifacts after a reload.
1528
+ */
1529
+ collectAllArtifactIds() {
1530
+ const ids = new Set();
1531
+ for (const artifactList of this.artifactsByDetailId.values()) {
1532
+ for (const info of artifactList) {
1533
+ ids.add(info.artifactId);
1534
+ }
1535
+ }
1536
+ return ids;
1537
+ }
1538
+ /**
1539
+ * Auto-open the artifact panel for the most recent NEW artifact.
1540
+ * Compares current artifacts against a pre-reload snapshot to find
1541
+ * only artifacts that were just discovered (avoiding re-opening for old artifacts).
1542
+ * Searches artifactsByDetailId directly rather than iterating this.messages,
1543
+ * because reloadMessagesForActiveConversation can temporarily remove messages
1544
+ * from this.messages during concurrent operations.
1545
+ */
1546
+ async autoOpenNewArtifact(artifactIdsBefore) {
1547
+ for (const [detailId, artifactList] of this.artifactsByDetailId) {
1548
+ const newArtifact = artifactList.find(a => !artifactIdsBefore.has(a.artifactId));
1549
+ if (newArtifact) {
1550
+ this.selectedArtifactId = newArtifact.artifactId;
1551
+ this.showArtifactPanel = true;
1552
+ await this.loadArtifactPermissions(newArtifact.artifactId);
1553
+ LogStatusEx({ message: `🎨 Auto-opening new artifact ${newArtifact.artifactId} from detail ${detailId}`, verboseOnly: true });
1554
+ return;
1555
+ }
1556
+ }
1557
+ }
1442
1558
  /**
1443
1559
  * Get the effective artifacts map based on showSystemArtifacts toggle
1444
1560
  * Combines user-visible and system artifacts when toggle is on
@@ -1480,6 +1596,7 @@ export class ConversationChatAreaComponent {
1480
1596
  toggleSystemArtifacts() {
1481
1597
  this.showSystemArtifacts = !this.showSystemArtifacts;
1482
1598
  this._combinedArtifactsMap = null; // Clear cache
1599
+ this.updateArtifactCountDisplay();
1483
1600
  this.cdr.detectChanges(); // Force update
1484
1601
  }
1485
1602
  /**
@@ -1707,29 +1824,30 @@ export class ConversationChatAreaComponent {
1707
1824
  this.cdr.detectChanges();
1708
1825
  }
1709
1826
  async onArtifactCreated(data) {
1710
- // Reload artifacts to get full entities
1827
+ // Snapshot artifact IDs before reload to detect newly created artifacts
1828
+ const artifactIdsBefore = this.collectAllArtifactIds();
1829
+ // Reload artifacts to get full entities (processes ALL messages in the conversation)
1711
1830
  await this.reloadArtifactsForMessage(data.conversationDetailId);
1712
- const artifactList = this.artifactsByDetailId.get(data.conversationDetailId);
1713
1831
  // Auto-open artifact panel if no artifact currently shown
1714
1832
  if (!this.showArtifactPanel) {
1715
- if (artifactList && artifactList.length > 0) {
1716
- // Show the LAST (most recent) artifact - use actual ID from map, not empty event data
1717
- this.selectedArtifactId = artifactList[artifactList.length - 1].artifactId;
1718
- this.showArtifactPanel = true;
1719
- // Load permissions for the new artifact
1720
- await this.loadArtifactPermissions(this.selectedArtifactId);
1721
- }
1833
+ // Use robust auto-open that checks ALL messages for new artifacts.
1834
+ // When a sub-agent (e.g., Skip) creates an artifact on a different ConversationDetail
1835
+ // than the one specified in the event, checking only data.conversationDetailId would miss it.
1836
+ await this.autoOpenNewArtifact(artifactIdsBefore);
1722
1837
  }
1723
- else if (this.selectedArtifactId && artifactList && artifactList.length > 0) {
1838
+ else if (this.selectedArtifactId) {
1724
1839
  // Panel is already open - check if new artifact is a new version of currently displayed artifact
1725
- const currentArtifact = artifactList.find(a => a.artifactId === this.selectedArtifactId);
1726
- if (currentArtifact) {
1727
- // New version of the same artifact - refresh to show latest version
1728
- const latestVersion = artifactList[artifactList.length - 1];
1729
- this.artifactViewerRefresh$.next({
1730
- artifactId: latestVersion.artifactId,
1731
- versionNumber: latestVersion.versionNumber
1732
- });
1840
+ const artifactList = this.artifactsByDetailId.get(data.conversationDetailId);
1841
+ if (artifactList && artifactList.length > 0) {
1842
+ const currentArtifact = artifactList.find(a => a.artifactId === this.selectedArtifactId);
1843
+ if (currentArtifact) {
1844
+ // New version of the same artifact - refresh to show latest version
1845
+ const latestVersion = artifactList[artifactList.length - 1];
1846
+ this.artifactViewerRefresh$.next({
1847
+ artifactId: latestVersion.artifactId,
1848
+ versionNumber: latestVersion.versionNumber
1849
+ });
1850
+ }
1733
1851
  }
1734
1852
  }
1735
1853
  // Force change detection to update the UI immediately
@@ -1772,6 +1890,7 @@ export class ConversationChatAreaComponent {
1772
1890
  this.showCollectionPicker = false;
1773
1891
  this.collectionPickerArtifactId = null;
1774
1892
  this.collectionPickerExcludedIds = [];
1893
+ this.cdr.detectChanges();
1775
1894
  }
1776
1895
  }
1777
1896
  onCollectionPickerCancelled() {
@@ -1936,6 +2055,10 @@ export class ConversationChatAreaComponent {
1936
2055
  // Pass the event up to the parent component (workspace or explorer wrapper)
1937
2056
  this.openEntityRecord.emit(event);
1938
2057
  }
2058
+ onNavigationRequest(event) {
2059
+ // Pass the event up to the parent component for app-level navigation
2060
+ this.navigationRequest.emit(event);
2061
+ }
1939
2062
  viewTestRun(testRunId) {
1940
2063
  // Open the test run record in the entity viewer
1941
2064
  const compositeKey = new CompositeKey();
@@ -1957,8 +2080,9 @@ export class ConversationChatAreaComponent {
1957
2080
  };
1958
2081
  const dialogRef = this.dialogService.open({
1959
2082
  content: TestFeedbackDialogComponent,
2083
+ title: 'Provide Test Feedback',
1960
2084
  width: 600,
1961
- height: 680
2085
+ minHeight: 500
1962
2086
  });
1963
2087
  const dialogInstance = dialogRef.content.instance;
1964
2088
  dialogInstance.data = dialogData;
@@ -2022,6 +2146,7 @@ export class ConversationChatAreaComponent {
2022
2146
  if (artifact) {
2023
2147
  this.artifactToShare = artifact;
2024
2148
  this.isArtifactShareModalOpen = true;
2149
+ this.cdr.detectChanges();
2025
2150
  }
2026
2151
  }
2027
2152
  /**
@@ -2030,6 +2155,7 @@ export class ConversationChatAreaComponent {
2030
2155
  onArtifactShareModalClose() {
2031
2156
  this.isArtifactShareModalOpen = false;
2032
2157
  this.artifactToShare = null;
2158
+ this.cdr.detectChanges();
2033
2159
  }
2034
2160
  /**
2035
2161
  * Handle successful share - refresh permissions
@@ -2041,6 +2167,7 @@ export class ConversationChatAreaComponent {
2041
2167
  if (this.selectedArtifactId) {
2042
2168
  await this.loadArtifactPermissions(this.selectedArtifactId);
2043
2169
  }
2170
+ this.cdr.detectChanges();
2044
2171
  }
2045
2172
  // Scroll functionality (pattern from skip-chat)
2046
2173
  checkScroll() {
@@ -2085,32 +2212,38 @@ export class ConversationChatAreaComponent {
2085
2212
  }
2086
2213
  }
2087
2214
  /**
2088
- * Detect in-progress agent runs/tasks and reconnect to their streaming updates
2089
- * Called after loading a conversation to resume progress tracking
2215
+ * Detect in-progress agent runs/tasks and reconnect to their streaming updates.
2216
+ * Called after loading a conversation to resume progress tracking.
2217
+ *
2218
+ * Also handles the "timing gap" scenario: if an agent completed between the page
2219
+ * refresh and the WebSocket reconnection, the completion PubSub event is lost.
2220
+ * We catch this by comparing message status against the agent run status from
2221
+ * the database — if the run is already complete, we handle the completion immediately.
2090
2222
  */
2091
2223
  async detectAndReconnectToInProgressRuns(conversationId) {
2092
- // Check for in-progress messages
2093
2224
  const inProgressMessages = this.messages.filter(m => m.Status === 'In-Progress' && m.Role === 'AI');
2094
2225
  if (inProgressMessages.length === 0) {
2095
2226
  return;
2096
2227
  }
2097
- LogStatusEx({ message: `🔄 Found ${inProgressMessages.length} in-progress messages, reconnecting...`, verboseOnly: true });
2098
- // For each in-progress message, check if there's an active agent run
2228
+ LogStatusEx({ message: `🔄 Found ${inProgressMessages.length} in-progress messages, checking status...`, verboseOnly: true });
2229
+ const completedStatuses = ['Completed', 'Failed', 'Error', 'Cancelled'];
2099
2230
  for (const message of inProgressMessages) {
2100
- if (message.AgentID) {
2101
- // Check agent state service for this run
2102
- const agentRun = this.agentRunsByDetailId.get(message.ID);
2103
- if (agentRun && agentRun.Status === 'Running') {
2104
- LogStatusEx({ message: `🔌 Reconnecting to agent run ${agentRun.ID} for message ${message.ID}`, verboseOnly: true });
2105
- // Agent state service polling will automatically pick this up
2106
- // The WebSocket subscription is already active via PushStatusUpdates()
2107
- // No additional action needed - just log for visibility
2108
- }
2231
+ const agentRun = this.agentRunsByDetailId.get(message.ID);
2232
+ if (!agentRun) {
2233
+ // No agent run yet — fire-and-forget may not have created it.
2234
+ // Polling will pick this up once the server creates the run.
2235
+ LogStatusEx({ message: `⏳ No agent run found for in-progress message ${message.ID}, waiting for server...`, verboseOnly: true });
2236
+ continue;
2237
+ }
2238
+ if (completedStatuses.includes(agentRun.Status)) {
2239
+ // Agent completed during the WebSocket reconnection gap — handle now
2240
+ LogStatusEx({ message: `🔄 Agent run ${agentRun.ID} already completed (${agentRun.Status}) for message ${message.ID}, handling catch-up...`, verboseOnly: true });
2241
+ await this.handleMessageCompletion(message, agentRun.ID);
2242
+ }
2243
+ else {
2244
+ LogStatusEx({ message: `🔌 Agent run ${agentRun.ID} still ${agentRun.Status} for message ${message.ID}, WebSocket will receive updates`, verboseOnly: true });
2109
2245
  }
2110
2246
  }
2111
- // Agent state service is already polling via startPolling() in onConversationChanged()
2112
- // WebSocket subscription is already active via message-input component's subscribeToPushStatus()
2113
- // Both will automatically receive updates for these in-progress runs
2114
2247
  }
2115
2248
  /**
2116
2249
  * Handle pending artifact navigation from collection
@@ -2216,7 +2349,7 @@ export class ConversationChatAreaComponent {
2216
2349
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.scrollContainer = _t.first);
2217
2350
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.artifactViewerComponent = _t.first);
2218
2351
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.messageInputComponents = _t);
2219
- } }, inputs: { environmentId: "environmentId", currentUser: "currentUser", conversationId: "conversationId", conversation: "conversation", threadId: "threadId", isNewConversation: "isNewConversation", pendingMessage: "pendingMessage", pendingAttachments: "pendingAttachments", pendingArtifactId: "pendingArtifactId", pendingArtifactVersionNumber: "pendingArtifactVersionNumber", showSidebarToggle: "showSidebarToggle" }, outputs: { conversationRenamed: "conversationRenamed", openEntityRecord: "openEntityRecord", taskClicked: "taskClicked", artifactLinkClicked: "artifactLinkClicked", sidebarToggleClicked: "sidebarToggleClicked", conversationCreated: "conversationCreated", threadOpened: "threadOpened", threadClosed: "threadClosed", pendingArtifactConsumed: "pendingArtifactConsumed", pendingMessageConsumed: "pendingMessageConsumed", pendingMessageRequested: "pendingMessageRequested" }, standalone: false, decls: 16, vars: 21, consts: [["scrollContainer", ""], ["messageInput", ""], [1, "chat-area"], [1, "chat-header"], [1, "chat-content-area"], [1, "chat-messages-pane"], [3, "currentUser", "disabled", "showSidebarToggle"], [1, "conversation-loading-state"], [1, "chat-messages-wrapper"], [3, "saved", "cancelled", "isOpen", "artifact", "currentUser"], [3, "parentMessageId", "conversationId", "currentUser"], [3, "cancelled", "exported", "isVisible", "conversation", "currentUser"], [3, "cancelled", "membersChanged", "isVisible", "conversation", "currentUser"], [1, "modal-overlay"], [3, "isOpen", "environmentId", "currentUser", "excludeCollectionIds"], [3, "imageUrl", "alt", "fileName", "visible"], [1, "chat-info"], ["title", "Show conversations", 1, "sidebar-toggle-btn"], [1, "chat-title"], ["title", "Assign to project", 1, "project-tag"], ["title", "View Test Run", 1, "test-indicator"], [3, "togglePanel", "agentSelected", "conversationId", "currentUser"], [1, "chat-actions", "chat-actions-buttons"], ["title", "View artifacts", 1, "artifact-indicator"], ["title", "View members", 1, "chat-members"], ["title", "Export conversation", 1, "action-btn", 3, "click"], [1, "fas", "fa-download"], [1, "btn-label"], [1, "action-btn", "share-btn", 3, "click", "title"], [1, "fas", "fa-share-nodes"], ["title", "Show conversations", 1, "sidebar-toggle-btn", 3, "click"], [1, "fas", "fa-table-columns"], ["title", "Assign to project", 1, "project-tag", 3, "click"], [1, "fas", "fa-folder"], ["title", "View Test Run", 1, "test-indicator", 3, "click"], [1, "fas", "fa-flask"], ["title", "View artifacts", 1, "artifact-indicator", 3, "click"], [1, "fas", "fa-cube"], [1, "artifact-badge"], ["title", "View members", 1, "chat-members", 3, "click"], [1, "fas", "fa-users"], [1, "members-badge"], [3, "sidebarToggleClicked", "messageSent", "currentUser", "disabled", "showSidebarToggle"], ["text", "Loading conversation...", "size", "large"], [1, "upload-indicator-overlay"], [1, "chat-messages-container", 3, "scroll"], [3, "replyInThread", "viewThread", "retryMessage", "testFeedbackMessage", "artifactClicked", "messageEdited", "openEntityRecord", "suggestedResponseSelected", "attachmentClicked", "messages", "conversation", "currentUser", "isProcessing", "artifactMap", "agentRunMap", "ratingsMap", "userAvatarMap", "attachmentsMap"], [1, "scroll-to-bottom-icon", 2, "left", "50%"], [1, "chat-input-container"], [1, "loading-peripheral-placeholder"], [1, "message-input-container-wrapper"], ["size", "medium", 3, "text"], [1, "scroll-to-bottom-icon", 2, "left", "50%", 3, "click"], [1, "fas", "fa-arrow-down"], ["text", "Loading conversation data...", "size", "medium"], [3, "hidden", "conversationId", "conversationName", "currentUser", "conversationHistory", "artifactsByDetailId", "systemArtifactsByDetailId", "agentRunsByDetailId", "inProgressMessageIds", "disabled", "enableAttachments", "maxAttachments", "maxAttachmentSizeBytes", "acceptedFileTypes", "initialMessage", "initialAttachments"], [3, "messageSent", "agentResponse", "agentRunDetected", "agentRunUpdate", "messageComplete", "artifactCreated", "conversationRenamed", "intentCheckStarted", "intentCheckCompleted", "uploadStateChanged", "hidden", "conversationId", "conversationName", "currentUser", "conversationHistory", "artifactsByDetailId", "systemArtifactsByDetailId", "agentRunsByDetailId", "inProgressMessageIds", "disabled", "enableAttachments", "maxAttachments", "maxAttachmentSizeBytes", "acceptedFileTypes", "initialMessage", "initialAttachments"], [1, "resize-handle"], [1, "chat-artifact-pane"], [3, "closed", "saveToCollectionRequested", "navigateToLink", "shareRequested", "maximizeToggled", "openEntityRecord", "artifactId", "currentUser", "environmentId", "versionNumber", "viewContext", "canShare", "canEdit", "isMaximized", "refreshTrigger"], [1, "resize-handle", 3, "mousedown", "touchstart"], [3, "closed", "replyAdded", "parentMessageId", "conversationId", "currentUser"], [1, "modal-overlay", 3, "click"], [1, "modal-content", "project-selector-modal", 3, "click"], [1, "modal-header"], [1, "modal-close-btn", 3, "click"], [1, "fas", "fa-times"], [1, "modal-body"], [3, "projectSelected", "environmentId", "currentUser", "selectedProjectId"], [1, "modal-content", "artifacts-modal", 3, "click"], [1, "modal-header-actions"], ["title", "Toggle system artifacts visibility", 1, "toggle-system-btn", 3, "active"], [1, "modal-body", "artifacts-grid"], [1, "empty-state"], [1, "artifact-modal-card", 3, "expanded", "system-artifact"], ["title", "Toggle system artifacts visibility", 1, "toggle-system-btn", 3, "click"], [1, "fas", "fa-cog"], [1, "fas", "fa-cube", 2, "font-size", "48px", "color", "#D1D5DB", "margin-bottom", "16px"], [2, "color", "#6B7280", "font-size", "14px"], [1, "artifact-modal-card"], [1, "artifact-card-header", 3, "click"], [1, "artifact-modal-icon"], [1, "fas", "fa-file-code"], [1, "artifact-modal-info"], [1, "artifact-modal-title"], [1, "artifact-modal-meta"], [1, "expand-btn"], [1, "artifact-modal-action"], [1, "fas", "fa-external-link-alt"], [1, "artifact-versions-list"], [1, "expand-btn", 3, "click"], [1, "fas"], [1, "artifact-version-item"], [1, "artifact-version-item", 3, "click"], [1, "version-badge"], [1, "version-open-text"], [1, "fas", "fa-arrow-right"], [3, "saved", "cancelled", "isOpen", "environmentId", "currentUser", "excludeCollectionIds"], [3, "closed", "imageUrl", "alt", "fileName", "visible"]], template: function ConversationChatAreaComponent_Template(rf, ctx) { if (rf & 1) {
2352
+ } }, inputs: { environmentId: "environmentId", currentUser: "currentUser", conversationId: "conversationId", conversation: "conversation", threadId: "threadId", isNewConversation: "isNewConversation", pendingMessage: "pendingMessage", pendingAttachments: "pendingAttachments", pendingArtifactId: "pendingArtifactId", pendingArtifactVersionNumber: "pendingArtifactVersionNumber", showSidebarToggle: "showSidebarToggle" }, outputs: { conversationRenamed: "conversationRenamed", openEntityRecord: "openEntityRecord", navigationRequest: "navigationRequest", taskClicked: "taskClicked", artifactLinkClicked: "artifactLinkClicked", sidebarToggleClicked: "sidebarToggleClicked", conversationCreated: "conversationCreated", threadOpened: "threadOpened", threadClosed: "threadClosed", pendingArtifactConsumed: "pendingArtifactConsumed", pendingMessageConsumed: "pendingMessageConsumed", pendingMessageRequested: "pendingMessageRequested" }, standalone: false, decls: 16, vars: 21, consts: [["scrollContainer", ""], ["messageInput", ""], [1, "chat-area"], [1, "chat-header"], [1, "chat-content-area"], [1, "chat-messages-pane"], [3, "currentUser", "disabled", "showSidebarToggle"], [1, "conversation-loading-state"], [1, "chat-messages-wrapper"], [3, "saved", "cancelled", "isOpen", "artifact", "currentUser"], [3, "parentMessageId", "conversationId", "currentUser"], [3, "cancelled", "exported", "isVisible", "conversation", "currentUser"], [3, "cancelled", "membersChanged", "isVisible", "conversation", "currentUser"], [1, "modal-overlay"], [3, "isOpen", "environmentId", "currentUser", "excludeCollectionIds"], [3, "imageUrl", "alt", "fileName", "visible"], [1, "chat-info"], ["title", "Show conversations", 1, "sidebar-toggle-btn"], [1, "chat-title"], ["title", "Assign to project", 1, "project-tag"], ["title", "View Test Run", 1, "test-indicator"], [3, "togglePanel", "agentSelected", "conversationId", "currentUser"], [1, "chat-actions", "chat-actions-buttons"], ["title", "View artifacts", 1, "artifact-indicator"], ["title", "View members", 1, "chat-members"], ["title", "Export conversation", 1, "action-btn", 3, "click"], [1, "fas", "fa-download"], [1, "btn-label"], [1, "action-btn", "share-btn", 3, "click", "title"], [1, "fas", "fa-share-nodes"], ["title", "Show conversations", 1, "sidebar-toggle-btn", 3, "click"], [1, "fas", "fa-table-columns"], ["title", "Assign to project", 1, "project-tag", 3, "click"], [1, "fas", "fa-folder"], ["title", "View Test Run", 1, "test-indicator", 3, "click"], [1, "fas", "fa-flask"], ["title", "View artifacts", 1, "artifact-indicator", 3, "click"], [1, "fas", "fa-cube"], [1, "artifact-badge"], ["title", "View members", 1, "chat-members", 3, "click"], [1, "fas", "fa-users"], [1, "members-badge"], [3, "sidebarToggleClicked", "messageSent", "currentUser", "disabled", "showSidebarToggle"], ["text", "Loading conversation...", "size", "large"], [1, "upload-indicator-overlay"], [1, "chat-messages-container", 3, "scroll"], [3, "replyInThread", "viewThread", "retryMessage", "testFeedbackMessage", "artifactClicked", "messageEdited", "openEntityRecord", "suggestedResponseSelected", "attachmentClicked", "messages", "conversation", "currentUser", "isProcessing", "artifactMap", "agentRunMap", "ratingsMap", "userAvatarMap", "attachmentsMap"], [1, "scroll-to-bottom-icon", 2, "left", "50%"], [1, "chat-input-container"], [1, "loading-peripheral-placeholder"], [1, "message-input-container-wrapper"], ["size", "medium", 3, "text"], [1, "scroll-to-bottom-icon", 2, "left", "50%", 3, "click"], [1, "fas", "fa-arrow-down"], ["text", "Loading conversation data...", "size", "medium"], [3, "hidden", "conversationId", "conversationName", "currentUser", "conversationHistory", "artifactsByDetailId", "systemArtifactsByDetailId", "agentRunsByDetailId", "inProgressMessageIds", "disabled", "enableAttachments", "maxAttachments", "maxAttachmentSizeBytes", "acceptedFileTypes", "initialMessage", "initialAttachments"], [3, "messageSent", "agentResponse", "agentRunDetected", "agentRunUpdate", "messageComplete", "artifactCreated", "conversationRenamed", "intentCheckStarted", "intentCheckCompleted", "uploadStateChanged", "hidden", "conversationId", "conversationName", "currentUser", "conversationHistory", "artifactsByDetailId", "systemArtifactsByDetailId", "agentRunsByDetailId", "inProgressMessageIds", "disabled", "enableAttachments", "maxAttachments", "maxAttachmentSizeBytes", "acceptedFileTypes", "initialMessage", "initialAttachments"], [1, "resize-handle"], [1, "chat-artifact-pane"], [3, "closed", "saveToCollectionRequested", "navigateToLink", "shareRequested", "maximizeToggled", "openEntityRecord", "navigationRequest", "artifactId", "currentUser", "environmentId", "versionNumber", "viewContext", "canShare", "canEdit", "isMaximized", "refreshTrigger"], [1, "resize-handle", 3, "mousedown", "touchstart"], [3, "closed", "replyAdded", "parentMessageId", "conversationId", "currentUser"], [1, "modal-overlay", 3, "click"], [1, "modal-content", "project-selector-modal", 3, "click"], [1, "modal-header"], [1, "modal-close-btn", 3, "click"], [1, "fas", "fa-times"], [1, "modal-body"], [3, "projectSelected", "environmentId", "currentUser", "selectedProjectId"], [1, "modal-content", "artifacts-modal", 3, "click"], [1, "modal-header-actions"], ["title", "Toggle system artifacts visibility", 1, "toggle-system-btn", 3, "active"], [1, "modal-body", "artifacts-grid"], [1, "empty-state"], [1, "artifact-modal-card", 3, "expanded", "system-artifact"], ["title", "Toggle system artifacts visibility", 1, "toggle-system-btn", 3, "click"], [1, "fas", "fa-cog"], [1, "fas", "fa-cube", 2, "font-size", "48px", "color", "#D1D5DB", "margin-bottom", "16px"], [2, "color", "#6B7280", "font-size", "14px"], [1, "artifact-modal-card"], [1, "artifact-card-header", 3, "click"], [1, "artifact-modal-icon"], [1, "fas", "fa-file-code"], [1, "artifact-modal-info"], [1, "artifact-modal-title"], [1, "artifact-modal-meta"], [1, "expand-btn"], [1, "artifact-modal-action"], [1, "fas", "fa-external-link-alt"], [1, "artifact-versions-list"], [1, "expand-btn", 3, "click"], [1, "fas"], [1, "artifact-version-item"], [1, "artifact-version-item", 3, "click"], [1, "version-badge"], [1, "version-open-text"], [1, "fas", "fa-arrow-right"], [3, "saved", "cancelled", "isOpen", "environmentId", "currentUser", "excludeCollectionIds"], [3, "closed", "imageUrl", "alt", "fileName", "visible"]], template: function ConversationChatAreaComponent_Template(rf, ctx) { if (rf & 1) {
2220
2353
  i0.ɵɵelementStart(0, "div", 2);
2221
2354
  i0.ɵɵconditionalCreate(1, ConversationChatAreaComponent_Conditional_1_Template, 20, 15, "div", 3);
2222
2355
  i0.ɵɵelementStart(2, "div", 4)(3, "div", 5);
@@ -2266,7 +2399,7 @@ export class ConversationChatAreaComponent {
2266
2399
  }
2267
2400
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ConversationChatAreaComponent, [{
2268
2401
  type: Component,
2269
- args: [{ standalone: false, selector: 'mj-conversation-chat-area', template: "<div class=\"chat-area\">\n <!-- Fixed Header -->\n @if (conversation) {\n <div class=\"chat-header\">\n <div class=\"chat-info\" [class.with-sidebar-toggle]=\"showSidebarToggle\">\n @if (showSidebarToggle) {\n <button class=\"sidebar-toggle-btn\"\n (click)=\"sidebarToggleClicked.emit()\"\n title=\"Show conversations\">\n <i class=\"fas fa-table-columns\"></i>\n </button>\n }\n <div class=\"chat-title\">{{ conversation.Name }}</div>\n @if (conversation.ProjectID) {\n <button class=\"project-tag\" (click)=\"openProjectSelector()\" title=\"Assign to project\">\n <i class=\"fas fa-folder\"></i>\n <span>{{ conversation.Project || 'Project' }}</span>\n </button>\n }\n @if (conversation.TestRunID) {\n <button class=\"test-indicator\" (click)=\"viewTestRun(conversation.TestRunID)\" title=\"View Test Run\">\n <i class=\"fas fa-flask\"></i>\n <span>Test</span>\n </button>\n }\n <mj-active-agent-indicator\n [conversationId]=\"conversation.ID\"\n [currentUser]=\"currentUser\"\n (togglePanel)=\"onToggleAgentPanel()\"\n (agentSelected)=\"onAgentSelected($event)\">\n </mj-active-agent-indicator>\n </div>\n <div class=\"chat-actions chat-actions-buttons\">\n @if (artifactCountDisplay > 0) {\n <button class=\"artifact-indicator\" (click)=\"viewArtifacts()\" title=\"View artifacts\">\n <i class=\"fas fa-cube\"></i>\n <span class=\"artifact-badge\">{{ artifactCountDisplay }}</span>\n </button>\n }\n @if (memberCount > 1) {\n <button class=\"chat-members\" (click)=\"toggleMembersModal()\" title=\"View members\">\n <i class=\"fas fa-users\"></i>\n <span class=\"members-badge\">{{ memberCount }}</span>\n </button>\n }\n <button class=\"action-btn\" (click)=\"exportConversation()\" title=\"Export conversation\">\n <i class=\"fas fa-download\"></i>\n <span class=\"btn-label\">Export</span>\n </button>\n <button class=\"action-btn share-btn\"\n [class.shared]=\"isShared\"\n (click)=\"shareConversation()\"\n [title]=\"isShared ? 'Manage sharing' : 'Share conversation'\">\n <i class=\"fas fa-share-nodes\"></i>\n <span class=\"btn-label\">Share</span>\n </button>\n <mj-active-agent-indicator\n [conversationId]=\"conversation.ID\"\n [currentUser]=\"currentUser\"\n (togglePanel)=\"onToggleAgentPanel()\"\n (agentSelected)=\"onAgentSelected($event)\">\n </mj-active-agent-indicator>\n </div>\n </div>\n }\n\n <!-- Messages and Artifact Split Layout -->\n <div class=\"chat-content-area\">\n <!-- Messages Pane -->\n <div class=\"chat-messages-pane\"\n [class.full-width]=\"!showArtifactPanel\"\n [class.hidden]=\"isArtifactPaneMaximized\">\n @if (isNewConversation || !conversationId) {\n <!-- Empty State - No conversation selected OR new unsaved conversation -->\n <mj-conversation-empty-state\n [currentUser]=\"currentUser\"\n [disabled]=\"isProcessing\"\n [showSidebarToggle]=\"showSidebarToggle\"\n (sidebarToggleClicked)=\"sidebarToggleClicked.emit()\"\n (messageSent)=\"onEmptyStateMessageSent($event)\">\n </mj-conversation-empty-state>\n } @else if (isLoadingConversation) {\n <!-- Loading State - Show centered spinner while conversation loads -->\n <div class=\"conversation-loading-state\">\n <mj-loading text=\"Loading conversation...\" size=\"large\"></mj-loading>\n </div>\n } @else {\n <!-- Normal Message View -->\n <div class=\"chat-messages-wrapper\">\n <!-- Upload Indicator Overlay (centered in conversation area) -->\n @if (isUploadingAttachments) {\n <div class=\"upload-indicator-overlay\">\n <mj-loading [text]=\"uploadingMessage\" size=\"medium\"></mj-loading>\n </div>\n }\n <div class=\"chat-messages-container\" #scrollContainer (scroll)=\"checkScroll()\">\n <mj-conversation-message-list\n [messages]=\"messages\"\n [conversation]=\"conversation\"\n [currentUser]=\"currentUser\"\n [isProcessing]=\"isProcessing\"\n [artifactMap]=\"effectiveArtifactsMap\"\n [agentRunMap]=\"agentRunsByDetailId\"\n [ratingsMap]=\"ratingsByDetailId\"\n [userAvatarMap]=\"userAvatarMap\"\n [attachmentsMap]=\"attachmentsByDetailId\"\n (replyInThread)=\"onReplyInThread($event)\"\n (viewThread)=\"onViewThread($event)\"\n (retryMessage)=\"onRetryMessage($event)\"\n (testFeedbackMessage)=\"onTestFeedbackMessage($event)\"\n (artifactClicked)=\"onArtifactClicked($event)\"\n (messageEdited)=\"onMessageEdited($event)\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\"\n (suggestedResponseSelected)=\"onSuggestedResponseSelected($event)\"\n (attachmentClicked)=\"onAttachmentClicked($event)\">\n </mj-conversation-message-list>\n\n <!-- Scroll to Bottom Icon (positioned within scroll container for proper centering) -->\n @if (showScrollToBottomIcon && messages && messages.length > 0) {\n <span class=\"scroll-to-bottom-icon\" style=\"left: 50%;\"\n (click)=\"scrollToBottomAnimate()\">\n <i class=\"fas fa-arrow-down\"></i>\n </span>\n }\n </div>\n\n <!-- Fixed Input Area -->\n <div class=\"chat-input-container\">\n @if (isLoadingPeripheralData) {\n <!-- Loading State -->\n <div class=\"loading-peripheral-placeholder\">\n <mj-loading text=\"Loading conversation data...\" size=\"medium\"></mj-loading>\n </div>\n } @else {\n <!-- Input Component - Multiple instances cached, only one visible -->\n <div class=\"message-input-container-wrapper\">\n @for (inputRef of getCachedInputs(); track inputRef.conversationId) {\n <mj-message-input\n #messageInput\n [hidden]=\"inputRef.conversationId !== conversationId\"\n [conversationId]=\"inputRef.conversationId\"\n [conversationName]=\"inputRef.conversationName\"\n [currentUser]=\"currentUser\"\n [conversationHistory]=\"inputRef.conversationId === conversationId ? messages : []\"\n [artifactsByDetailId]=\"inputRef.conversationId === conversationId ? artifactsByDetailId : emptyArtifactsMap\"\n [systemArtifactsByDetailId]=\"inputRef.conversationId === conversationId ? systemArtifactsByDetailId : emptyArtifactsMap\"\n [agentRunsByDetailId]=\"inputRef.conversationId === conversationId ? agentRunsByDetailId : emptyAgentRunsMap\"\n [inProgressMessageIds]=\"inputRef.conversationId === conversationId ? inProgressMessageIds : emptyInProgressIds\"\n [disabled]=\"isProcessing\"\n [enableAttachments]=\"enableAttachments\"\n [maxAttachments]=\"maxAttachments\"\n [maxAttachmentSizeBytes]=\"maxAttachmentSizeBytes\"\n [acceptedFileTypes]=\"acceptedFileTypes\"\n [initialMessage]=\"inputRef.conversationId === conversationId ? pendingMessage : null\"\n [initialAttachments]=\"inputRef.conversationId === conversationId ? pendingAttachments : null\"\n (messageSent)=\"onMessageSent($event)\"\n (agentResponse)=\"onAgentResponse($event)\"\n (agentRunDetected)=\"onAgentRunDetected($event)\"\n (agentRunUpdate)=\"onAgentRunUpdate($event)\"\n (messageComplete)=\"onMessageComplete($event)\"\n (artifactCreated)=\"onArtifactCreated($event)\"\n (conversationRenamed)=\"onConversationRenamed($event)\"\n (intentCheckStarted)=\"onIntentCheckStarted()\"\n (intentCheckCompleted)=\"onIntentCheckCompleted()\"\n (uploadStateChanged)=\"onUploadStateChanged($event)\">\n </mj-message-input>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- Artifact Viewer Pane -->\n @if (showArtifactPanel && selectedArtifactId) {\n @if (!isArtifactPaneMaximized) {\n <div class=\"resize-handle\" (mousedown)=\"onResizeStart($event)\" (touchstart)=\"onResizeTouchStart($event)\"></div>\n }\n <div class=\"chat-artifact-pane\"\n [style.width.%]=\"artifactPaneWidth\"\n [class.maximized]=\"isArtifactPaneMaximized\">\n <mj-artifact-viewer-panel\n [artifactId]=\"selectedArtifactId\"\n [currentUser]=\"currentUser\"\n [environmentId]=\"environmentId\"\n [versionNumber]=\"selectedVersionNumber\"\n [viewContext]=\"'conversation'\"\n [canShare]=\"canShareSelectedArtifact\"\n [canEdit]=\"canEditSelectedArtifact\"\n [isMaximized]=\"isArtifactPaneMaximized\"\n [refreshTrigger]=\"artifactViewerRefresh$\"\n (closed)=\"onCloseArtifactPanel()\"\n (saveToCollectionRequested)=\"onSaveToCollectionRequested($event)\"\n (navigateToLink)=\"onArtifactLinkNavigation($event)\"\n (shareRequested)=\"onArtifactShareRequested($event)\"\n (maximizeToggled)=\"toggleMaximizeArtifactPane()\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\">\n </mj-artifact-viewer-panel>\n </div>\n }\n\n <!-- Artifact Share Modal -->\n <mj-artifact-share-modal\n [isOpen]=\"isArtifactShareModalOpen\"\n [artifact]=\"artifactToShare\"\n [currentUser]=\"currentUser\"\n (saved)=\"onArtifactShared()\"\n (cancelled)=\"onArtifactShareModalClose()\">\n </mj-artifact-share-modal>\n </div>\n</div>\n\n<!-- Thread Panel -->\n@if (threadId) {\n <mj-thread-panel\n [parentMessageId]=\"threadId\"\n [conversationId]=\"conversationId || ''\"\n [currentUser]=\"currentUser\"\n (closed)=\"onLocalThreadClosed()\"\n (replyAdded)=\"onThreadReplyAdded($event)\">\n </mj-thread-panel>\n}\n\n<!-- Export Modal -->\n<mj-export-modal\n [isVisible]=\"showExportModal\"\n [conversation]=\"conversation || undefined\"\n [currentUser]=\"currentUser\"\n (cancelled)=\"onExportModalCancelled()\"\n (exported)=\"onExportModalComplete()\">\n</mj-export-modal>\n\n<!-- Members Modal -->\n<mj-members-modal\n [isVisible]=\"showMembersModal\"\n [conversation]=\"conversation || undefined\"\n [currentUser]=\"currentUser\"\n (cancelled)=\"showMembersModal = false\"\n (membersChanged)=\"showMembersModal = false\">\n</mj-members-modal>\n\n<!-- Project Selector Modal -->\n@if (showProjectSelector && conversation) {\n <div class=\"modal-overlay\" (click)=\"showProjectSelector = false\">\n <div class=\"modal-content project-selector-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Assign Project</h3>\n <button class=\"modal-close-btn\" (click)=\"showProjectSelector = false\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n <div class=\"modal-body\">\n <mj-project-selector\n [environmentId]=\"environmentId\"\n [currentUser]=\"currentUser\"\n [selectedProjectId]=\"conversation.ProjectID\"\n (projectSelected)=\"onProjectSelected($event)\">\n </mj-project-selector>\n </div>\n </div>\n </div>\n}\n\n<!-- Artifacts Modal -->\n@if (showArtifactsModal) {\n <div class=\"modal-overlay\" (click)=\"showArtifactsModal = false\">\n <div class=\"modal-content artifacts-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Conversation Artifacts</h3>\n <div class=\"modal-header-actions\">\n @if (hasSystemArtifacts) {\n <button class=\"toggle-system-btn\"\n [class.active]=\"showSystemArtifacts\"\n (click)=\"toggleSystemArtifacts()\"\n title=\"Toggle system artifacts visibility\">\n <i class=\"fas fa-cog\"></i>\n <span>{{ showSystemArtifacts ? 'Hide' : 'Show' }} System</span>\n </button>\n }\n <button class=\"modal-close-btn\" (click)=\"showArtifactsModal = false\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n </div>\n <div class=\"modal-body artifacts-grid\">\n @if (artifactsByDetailId.size === 0) {\n <div class=\"empty-state\">\n <i class=\"fas fa-cube\" style=\"font-size: 48px; color: #D1D5DB; margin-bottom: 16px;\"></i>\n <p style=\"color: #6B7280; font-size: 14px;\">No artifacts in this conversation yet</p>\n </div>\n }\n @for (artifact of getArtifactsArray(); track artifact.artifactId) {\n <div class=\"artifact-modal-card\"\n [class.expanded]=\"expandedArtifactId === artifact.artifactId\"\n [class.system-artifact]=\"artifact.visibility === 'System Only'\">\n <!-- Main card header - click to open latest version -->\n <div class=\"artifact-card-header\" (click)=\"openArtifactFromModal(artifact.artifactId)\">\n <div class=\"artifact-modal-icon\">\n <i class=\"fas fa-file-code\"></i>\n </div>\n <div class=\"artifact-modal-info\">\n <div class=\"artifact-modal-title\">{{artifact.name}}</div>\n <div class=\"artifact-modal-meta\">\n @if (artifact.versionCount > 1) {\n {{artifact.versionCount}} versions\n } @else {\n 1 version\n }\n </div>\n </div>\n @if (artifact.versionCount > 1) {\n <button class=\"expand-btn\" (click)=\"toggleArtifactExpansion(artifact.artifactId, $event)\">\n <i class=\"fas\" [class.fa-chevron-down]=\"expandedArtifactId !== artifact.artifactId\"\n [class.fa-chevron-up]=\"expandedArtifactId === artifact.artifactId\"></i>\n </button>\n }\n <div class=\"artifact-modal-action\">\n <i class=\"fas fa-external-link-alt\"></i>\n </div>\n </div>\n\n <!-- Expanded version list -->\n @if (expandedArtifactId === artifact.artifactId && artifact.versionCount > 1) {\n <div class=\"artifact-versions-list\">\n @for (version of artifact.versions; track version.versionId) {\n <div class=\"artifact-version-item\" (click)=\"openArtifactFromModal(artifact.artifactId, version.versionNumber); $event.stopPropagation()\">\n <span class=\"version-badge\">v{{version.versionNumber}}</span>\n <span class=\"version-open-text\">Open this version</span>\n <i class=\"fas fa-arrow-right\"></i>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n </div>\n}\n\n<!-- Collection Picker Modal -->\n@if (showCollectionPicker) {\n <mj-artifact-collection-picker-modal\n [isOpen]=\"showCollectionPicker\"\n [environmentId]=\"environmentId\"\n [currentUser]=\"currentUser\"\n [excludeCollectionIds]=\"collectionPickerExcludedIds\"\n (saved)=\"onCollectionPickerSaved($event)\"\n (cancelled)=\"onCollectionPickerCancelled()\">\n </mj-artifact-collection-picker-modal>\n}\n\n<!-- Image Viewer Modal -->\n@if (showImageViewer) {\n <mj-image-viewer\n [imageUrl]=\"selectedImageUrl\"\n [alt]=\"selectedImageAlt\"\n [fileName]=\"selectedImageFileName\"\n [visible]=\"showImageViewer\"\n (closed)=\"onImageViewerClosed()\">\n </mj-image-viewer>\n}", styles: [":host {\n display: flex;\n width: 100%;\n height: 100%;\n}\n\n.chat-area {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.chat-header {\n flex-shrink: 0;\n padding: 12px 20px;\n border-bottom: 1px solid #D9D9D9;\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 16px;\n background: #FFF;\n z-index: 10;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n}\n\n.chat-info {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n}\n\n/* Sidebar toggle button in header */\n.sidebar-toggle-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n background: transparent;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\n\n.sidebar-toggle-btn:hover {\n background: rgba(0, 0, 0, 0.08);\n}\n\n.sidebar-toggle-btn:active {\n background: rgba(0, 0, 0, 0.12);\n}\n\n.sidebar-toggle-btn i {\n color: #666;\n font-size: 18px;\n transition: color 0.15s ease;\n}\n\n.sidebar-toggle-btn:hover i {\n color: #333;\n}\n\n.chat-title {\n font-size: 16px;\n font-weight: 600;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.project-tag {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: #F4F4F4;\n border: 1px solid #D9D9D9;\n border-radius: 16px;\n font-size: 11px;\n font-weight: 600;\n color: #AAA;\n cursor: pointer;\n transition: all 0.2s;\n height: 28px;\n margin-left: 12px;\n}\n\n.project-tag:hover {\n background: #D9D9D9;\n border-color: #AAA;\n}\n\n.project-tag i {\n font-size: 10px;\n}\n\n.test-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: #FFF8E1;\n border: 1px solid #FFD54F;\n border-radius: 16px;\n font-size: 11px;\n font-weight: 600;\n color: #F57C00;\n cursor: pointer;\n transition: all 0.2s;\n height: 28px;\n margin-left: 8px;\n}\n\n.test-indicator:hover {\n background: #FFE082;\n border-color: #FFA000;\n}\n\n.test-indicator i {\n font-size: 10px;\n}\n\n.chat-members,\n.artifact-indicator {\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n padding: 6px 8px;\n background: transparent;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n font-size: 14px;\n color: #6B7280;\n cursor: pointer;\n transition: all 150ms ease;\n}\n\n.chat-members:hover,\n.artifact-indicator:hover {\n background: #F9FAFB;\n color: #111827;\n}\n\n/* Badge overlay for artifact and member counts */\n.artifact-badge,\n.members-badge {\n position: absolute;\n top: -6px;\n right: -6px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n background: #3B82F6;\n color: white;\n font-size: 10px;\n font-weight: 600;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n}\n\n.members-badge {\n background: #6366F1;\n}\n\n.ambient-agent-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: #F3F4F6;\n border: 1px solid #D1D5DB;\n border-radius: 6px;\n font-size: 13px;\n color: #6B7280;\n animation: pulse 2s ease-in-out infinite;\n}\n\n.ambient-agent-indicator i {\n color: #0076B6;\n}\n\n@keyframes pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.7;\n }\n}\n.chat-actions {\n display: flex;\n gap: 8px;\n}\n\n.action-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 12px;\n background: transparent;\n border: 1px solid #E5E7EB;\n cursor: pointer;\n border-radius: 6px;\n font-size: 13px;\n color: #6B7280;\n transition: all 150ms ease;\n}\n\n.action-btn:hover {\n background: #F9FAFB;\n color: #111827;\n}\n\n.share-btn.shared {\n background: #EFF6FF;\n border-color: #1e40af;\n color: #1e40af;\n}\n\n.share-btn.shared:hover {\n background: #DBEAFE;\n color: #1e3a8a;\n}\n\n.chat-content-area {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n display: flex;\n flex-direction: row;\n position: relative;\n}\n\n.chat-messages-pane {\n height: 100%;\n display: flex;\n flex-direction: column;\n min-width: min(300px, 100%); /* Respect container bounds while maintaining minimum */\n overflow: hidden;\n transition: width 0.3s ease;\n}\n\n.chat-messages-pane.full-width {\n width: 100%;\n}\n\n.chat-messages-pane:not(.full-width) {\n flex: 1;\n}\n\n.chat-messages-pane.hidden {\n display: none;\n}\n\n.resize-handle {\n width: 4px;\n background: transparent;\n cursor: col-resize;\n flex-shrink: 0;\n position: relative;\n transition: background-color 0.2s;\n}\n\n.resize-handle:hover {\n background: #3B82F6;\n}\n\n.resize-handle::before {\n content: \"\";\n position: absolute;\n left: -4px;\n right: -4px;\n top: 0;\n bottom: 0;\n}\n\n.chat-artifact-pane {\n height: 100%;\n display: flex;\n flex-direction: column;\n background: #FAFAFA;\n overflow: hidden;\n flex-shrink: 0;\n}\n\n.chat-artifact-pane.maximized {\n width: 100% !important;\n}\n\n.chat-artifact-pane > mj-artifact-viewer-panel {\n display: flex;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n.chat-messages-wrapper {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow: hidden;\n position: relative; /* For upload overlay positioning */\n}\n\n/* Upload indicator overlay - centered in conversation area */\n.upload-indicator-overlay {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem 1.5rem;\n background: rgba(255, 255, 255, 0.95);\n border-radius: 12px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);\n z-index: 100;\n pointer-events: none;\n}\n\n.chat-messages-container {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n background: #FFF;\n min-height: 0;\n position: relative;\n}\n\n.scroll-to-bottom-icon {\n position: sticky;\n bottom: 21px;\n left: 50%;\n transform: translateX(-50%);\n width: 40px;\n height: 40px;\n margin-top: -40px;\n margin-left: auto;\n margin-right: auto;\n background: white;\n border: 1px solid #D1D5DB;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n transition: all 0.2s ease;\n z-index: 100;\n pointer-events: auto;\n}\n\n.scroll-to-bottom-icon:hover {\n background: #F3F4F6;\n border-color: #3B82F6;\n transform: translateX(-50%) translateY(-2px);\n box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);\n}\n\n.scroll-to-bottom-icon i {\n color: #6B7280;\n font-size: 16px;\n transition: color 0.2s;\n}\n\n.scroll-to-bottom-icon:hover i {\n color: #3B82F6;\n}\n\n.chat-input-container {\n flex-shrink: 0;\n background: #FFF;\n padding: 0 1.25rem 1.25rem 1.25rem;\n overflow: visible;\n}\n\n.loading-peripheral-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 140px;\n padding: 24px;\n background: rgba(255, 255, 255, 0.5);\n backdrop-filter: blur(2px);\n border-radius: 12px;\n margin: 12px;\n animation: fadeIn 0.2s ease-in-out;\n}\n\n.modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n\n.modal-content {\n background: white;\n border-radius: 8px;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n max-width: 90vw;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n}\n\n.project-selector-modal {\n width: 600px;\n height: 500px;\n}\n\n.modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #E5E7EB;\n}\n\n.modal-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n}\n\n.modal-header-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toggle-system-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #F3F4F6;\n border: 1px solid #E5E7EB;\n cursor: pointer;\n color: #6B7280;\n padding: 6px 12px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n transition: all 0.2s;\n}\n\n.toggle-system-btn:hover {\n background: #E5E7EB;\n border-color: #D1D5DB;\n color: #374151;\n}\n\n.toggle-system-btn.active {\n background: #3B82F6;\n border-color: #3B82F6;\n color: white;\n}\n\n.toggle-system-btn.active:hover {\n background: #2563EB;\n border-color: #2563EB;\n}\n\n.toggle-system-btn i {\n font-size: 12px;\n}\n\n.modal-close-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: #6B7280;\n padding: 4px 8px;\n border-radius: 4px;\n transition: all 0.2s;\n}\n\n.modal-close-btn:hover {\n background: #F3F4F6;\n color: #111827;\n}\n\n.modal-body {\n flex: 1;\n overflow: auto;\n padding: 20px;\n}\n\n.artifacts-modal {\n width: 700px;\n max-height: 600px;\n}\n\n.artifacts-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 16px;\n}\n\n.empty-state {\n grid-column: 1/-1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n}\n\n.artifact-modal-card {\n display: flex;\n flex-direction: column;\n background: white;\n border: 1.5px solid #E5E7EB;\n border-radius: 12px;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n}\n\n.artifact-modal-card.expanded {\n border-color: #3B82F6;\n}\n\n.artifact-modal-card.system-artifact {\n opacity: 0.85;\n border-color: #D1D5DB;\n border-style: dashed;\n position: relative;\n}\n\n.artifact-modal-card.system-artifact::before {\n content: \"SYSTEM\";\n position: absolute;\n top: 8px;\n right: 8px;\n font-size: 9px;\n font-weight: 600;\n color: #9CA3AF;\n background: #F3F4F6;\n padding: 2px 6px;\n border-radius: 3px;\n letter-spacing: 0.5px;\n z-index: 10;\n}\n\n.artifact-modal-card.system-artifact:hover {\n border-color: #9CA3AF;\n box-shadow: 0 4px 12px rgba(156, 163, 175, 0.15);\n}\n\n.artifact-card-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px;\n cursor: pointer;\n}\n\n.artifact-card-header:hover {\n background: #F9FAFB;\n}\n\n.artifact-modal-card:hover {\n border-color: #3B82F6;\n box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);\n transform: translateY(-2px);\n}\n\n.artifact-modal-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: linear-gradient(135deg, #EFF6FF 0%, #DBEAFE 100%);\n border-radius: 10px;\n color: #3B82F6;\n flex-shrink: 0;\n}\n\n.artifact-modal-icon i {\n font-size: 18px;\n}\n\n.artifact-modal-info {\n flex: 1;\n min-width: 0;\n}\n\n.artifact-modal-title {\n font-size: 14px;\n font-weight: 600;\n color: #1F2937;\n margin-bottom: 4px;\n}\n\n.artifact-modal-meta {\n font-size: 12px;\n color: #6B7280;\n}\n\n.artifact-modal-action {\n color: #9CA3AF;\n transition: color 0.2s;\n}\n\n.artifact-modal-card:hover .artifact-modal-action {\n color: #3B82F6;\n}\n\n.expand-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n color: #6B7280;\n cursor: pointer;\n border-radius: 6px;\n transition: all 0.2s;\n}\n.expand-btn:hover {\n background: #F3F4F6;\n color: #3B82F6;\n}\n\n.artifact-versions-list {\n display: flex;\n flex-direction: column;\n padding: 0 1rem 1rem 1rem;\n background: #F9FAFB;\n}\n\n.artifact-version-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px 12px 68px;\n cursor: pointer;\n transition: background 0.15s;\n}\n.artifact-version-item:hover {\n background: #F3F4F6;\n}\n.artifact-version-item .version-badge {\n display: inline-block;\n padding: 4px 8px;\n background: #EEF2FF;\n color: #4F46E5;\n font-size: 12px;\n font-weight: 600;\n font-family: monospace;\n border-radius: 4px;\n}\n.artifact-version-item .version-open-text {\n flex: 1;\n font-size: 13px;\n color: #6B7280;\n}\n.artifact-version-item i {\n color: #9CA3AF;\n font-size: 12px;\n}\n.artifact-version-item:hover .version-badge {\n background: #4F46E5;\n color: white;\n}\n.artifact-version-item:hover .version-open-text {\n color: #3B82F6;\n}\n.artifact-version-item:hover i {\n color: #3B82F6;\n}\n\n.loading-peripheral-content {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 24px;\n background: white;\n border: 2px solid #E5E7EB;\n border-radius: 12px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n color: #4B5563;\n font-size: 14px;\n font-weight: 500;\n}\n.loading-peripheral-content i {\n font-size: 20px;\n color: #9333EA;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n.conversation-loading-state {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n width: 100%;\n}\n\n.loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding-top: 20px;\n gap: 16px;\n color: #6B7280;\n font-size: 15px;\n}\n.loading-content i {\n font-size: 32px;\n color: #3B82F6;\n}\n.loading-content span {\n font-weight: 500;\n}\n\n/* Mobile adjustments: 481px - 768px */\n@media (max-width: 768px) {\n .chat-header {\n padding: 8px 12px;\n gap: 6px;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: center;\n position: relative;\n }\n .chat-info {\n flex-direction: row;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n order: 1;\n }\n .chat-title {\n font-size: 15px;\n font-weight: 700;\n width: auto;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n flex: 1;\n min-width: 0;\n }\n .project-tag {\n margin-left: 0;\n font-size: 10px;\n padding: 3px 8px;\n height: 24px;\n display: none; /* Hide on mobile to save space */\n }\n .test-indicator {\n margin-left: 0;\n font-size: 10px;\n padding: 3px 8px;\n height: 24px;\n }\n /* Action buttons - icon only on mobile */\n .chat-actions-buttons {\n order: 2;\n flex-shrink: 0;\n }\n .chat-actions-buttons .action-btn {\n padding: 6px 8px;\n min-width: auto;\n }\n .chat-actions-buttons .action-btn .btn-label {\n display: none;\n }\n .chat-actions {\n flex-wrap: nowrap;\n }\n .chat-members,\n .artifact-indicator {\n padding: 5px 7px;\n font-size: 13px;\n }\n .action-btn {\n padding: 6px 8px;\n font-size: 12px;\n }\n .ambient-agent-indicator {\n font-size: 12px;\n padding: 4px 8px;\n }\n .project-selector-modal {\n width: min(95vw, 600px);\n height: auto;\n }\n .artifacts-modal {\n width: min(95vw, 700px);\n }\n .artifacts-grid {\n grid-template-columns: 1fr;\n }\n .chat-input-container {\n padding: 0 0.75rem 0.75rem 0.75rem;\n }\n .scroll-to-bottom-icon {\n bottom: 16px;\n width: 36px;\n height: 36px;\n }\n /* Artifact pane - full width overlay on mobile, overlapping header */\n .chat-content-area {\n position: relative;\n }\n .chat-artifact-pane {\n position: fixed;\n left: 0;\n right: 0;\n top: 56px; /* 48px nav + 8px dark strip above blue border */\n bottom: 0;\n width: 100% !important;\n z-index: 100;\n background: #FFF;\n }\n .resize-handle {\n display: none;\n }\n}\n/* Small Phone adjustments: <= 480px */\n@media (max-width: 480px) {\n .chat-header {\n padding: 6px 8px;\n gap: 4px;\n }\n .chat-title {\n font-size: 14px;\n font-weight: 700;\n }\n .project-tag {\n font-size: 9px;\n padding: 2px 6px;\n height: 20px;\n display: none;\n }\n .test-indicator {\n font-size: 9px;\n padding: 2px 6px;\n height: 20px;\n }\n .chat-members,\n .artifact-indicator {\n padding: 4px 8px;\n font-size: 11px;\n }\n .action-btn {\n padding: 5px 7px;\n font-size: 11px;\n }\n .ambient-agent-indicator {\n font-size: 11px;\n padding: 3px 6px;\n }\n .project-selector-modal,\n .artifacts-modal {\n width: 100vw;\n height: 100vh;\n border-radius: 0;\n }\n .chat-input-container {\n padding: 0 0.5rem 0.5rem 0.5rem;\n }\n .scroll-to-bottom-icon {\n bottom: 12px;\n width: 32px;\n height: 32px;\n }\n .scroll-to-bottom-icon i {\n font-size: 14px;\n }\n}\n"] }]
2402
+ args: [{ standalone: false, selector: 'mj-conversation-chat-area', template: "<div class=\"chat-area\">\n <!-- Fixed Header -->\n @if (conversation) {\n <div class=\"chat-header\">\n <div class=\"chat-info\" [class.with-sidebar-toggle]=\"showSidebarToggle\">\n @if (showSidebarToggle) {\n <button class=\"sidebar-toggle-btn\"\n (click)=\"sidebarToggleClicked.emit()\"\n title=\"Show conversations\">\n <i class=\"fas fa-table-columns\"></i>\n </button>\n }\n <div class=\"chat-title\">{{ conversation.Name }}</div>\n @if (conversation.ProjectID) {\n <button class=\"project-tag\" (click)=\"openProjectSelector()\" title=\"Assign to project\">\n <i class=\"fas fa-folder\"></i>\n <span>{{ conversation.Project || 'Project' }}</span>\n </button>\n }\n @if (conversation.TestRunID) {\n <button class=\"test-indicator\" (click)=\"viewTestRun(conversation.TestRunID)\" title=\"View Test Run\">\n <i class=\"fas fa-flask\"></i>\n <span>Test</span>\n </button>\n }\n <mj-active-agent-indicator\n [conversationId]=\"conversation.ID\"\n [currentUser]=\"currentUser\"\n (togglePanel)=\"onToggleAgentPanel()\"\n (agentSelected)=\"onAgentSelected($event)\">\n </mj-active-agent-indicator>\n </div>\n <div class=\"chat-actions chat-actions-buttons\">\n @if (artifactCountDisplay > 0) {\n <button class=\"artifact-indicator\" (click)=\"viewArtifacts()\" title=\"View artifacts\">\n <i class=\"fas fa-cube\"></i>\n <span class=\"artifact-badge\">{{ artifactCountDisplay }}</span>\n </button>\n }\n @if (memberCount > 1) {\n <button class=\"chat-members\" (click)=\"toggleMembersModal()\" title=\"View members\">\n <i class=\"fas fa-users\"></i>\n <span class=\"members-badge\">{{ memberCount }}</span>\n </button>\n }\n <button class=\"action-btn\" (click)=\"exportConversation()\" title=\"Export conversation\">\n <i class=\"fas fa-download\"></i>\n <span class=\"btn-label\">Export</span>\n </button>\n <button class=\"action-btn share-btn\"\n [class.shared]=\"isShared\"\n (click)=\"shareConversation()\"\n [title]=\"isShared ? 'Manage sharing' : 'Share conversation'\">\n <i class=\"fas fa-share-nodes\"></i>\n <span class=\"btn-label\">Share</span>\n </button>\n <mj-active-agent-indicator\n [conversationId]=\"conversation.ID\"\n [currentUser]=\"currentUser\"\n (togglePanel)=\"onToggleAgentPanel()\"\n (agentSelected)=\"onAgentSelected($event)\">\n </mj-active-agent-indicator>\n </div>\n </div>\n }\n\n <!-- Messages and Artifact Split Layout -->\n <div class=\"chat-content-area\">\n <!-- Messages Pane -->\n <div class=\"chat-messages-pane\"\n [class.full-width]=\"!showArtifactPanel\"\n [class.hidden]=\"isArtifactPaneMaximized\">\n @if (isNewConversation || !conversationId) {\n <!-- Empty State - No conversation selected OR new unsaved conversation -->\n <mj-conversation-empty-state\n [currentUser]=\"currentUser\"\n [disabled]=\"isProcessing\"\n [showSidebarToggle]=\"showSidebarToggle\"\n (sidebarToggleClicked)=\"sidebarToggleClicked.emit()\"\n (messageSent)=\"onEmptyStateMessageSent($event)\">\n </mj-conversation-empty-state>\n } @else if (isLoadingConversation) {\n <!-- Loading State - Show centered spinner while conversation loads -->\n <div class=\"conversation-loading-state\">\n <mj-loading text=\"Loading conversation...\" size=\"large\"></mj-loading>\n </div>\n } @else {\n <!-- Normal Message View -->\n <div class=\"chat-messages-wrapper\">\n <!-- Upload Indicator Overlay (centered in conversation area) -->\n @if (isUploadingAttachments) {\n <div class=\"upload-indicator-overlay\">\n <mj-loading [text]=\"uploadingMessage\" size=\"medium\"></mj-loading>\n </div>\n }\n <div class=\"chat-messages-container\" #scrollContainer (scroll)=\"checkScroll()\">\n <mj-conversation-message-list\n [messages]=\"messages\"\n [conversation]=\"conversation\"\n [currentUser]=\"currentUser\"\n [isProcessing]=\"isProcessing\"\n [artifactMap]=\"effectiveArtifactsMap\"\n [agentRunMap]=\"agentRunsByDetailId\"\n [ratingsMap]=\"ratingsByDetailId\"\n [userAvatarMap]=\"userAvatarMap\"\n [attachmentsMap]=\"attachmentsByDetailId\"\n (replyInThread)=\"onReplyInThread($event)\"\n (viewThread)=\"onViewThread($event)\"\n (retryMessage)=\"onRetryMessage($event)\"\n (testFeedbackMessage)=\"onTestFeedbackMessage($event)\"\n (artifactClicked)=\"onArtifactClicked($event)\"\n (messageEdited)=\"onMessageEdited($event)\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\"\n (suggestedResponseSelected)=\"onSuggestedResponseSelected($event)\"\n (attachmentClicked)=\"onAttachmentClicked($event)\">\n </mj-conversation-message-list>\n\n <!-- Scroll to Bottom Icon (positioned within scroll container for proper centering) -->\n @if (showScrollToBottomIcon && messages && messages.length > 0) {\n <span class=\"scroll-to-bottom-icon\" style=\"left: 50%;\"\n (click)=\"scrollToBottomAnimate()\">\n <i class=\"fas fa-arrow-down\"></i>\n </span>\n }\n </div>\n\n <!-- Fixed Input Area -->\n <div class=\"chat-input-container\">\n @if (isLoadingPeripheralData) {\n <!-- Loading State -->\n <div class=\"loading-peripheral-placeholder\">\n <mj-loading text=\"Loading conversation data...\" size=\"medium\"></mj-loading>\n </div>\n } @else {\n <!-- Input Component - Multiple instances cached, only one visible -->\n <div class=\"message-input-container-wrapper\">\n @for (inputRef of getCachedInputs(); track inputRef.conversationId) {\n <mj-message-input\n #messageInput\n [hidden]=\"inputRef.conversationId !== conversationId\"\n [conversationId]=\"inputRef.conversationId\"\n [conversationName]=\"inputRef.conversationName\"\n [currentUser]=\"currentUser\"\n [conversationHistory]=\"inputRef.conversationId === conversationId ? messages : []\"\n [artifactsByDetailId]=\"inputRef.conversationId === conversationId ? artifactsByDetailId : emptyArtifactsMap\"\n [systemArtifactsByDetailId]=\"inputRef.conversationId === conversationId ? systemArtifactsByDetailId : emptyArtifactsMap\"\n [agentRunsByDetailId]=\"inputRef.conversationId === conversationId ? agentRunsByDetailId : emptyAgentRunsMap\"\n [inProgressMessageIds]=\"inputRef.conversationId === conversationId ? inProgressMessageIds : emptyInProgressIds\"\n [disabled]=\"isProcessing\"\n [enableAttachments]=\"enableAttachments\"\n [maxAttachments]=\"maxAttachments\"\n [maxAttachmentSizeBytes]=\"maxAttachmentSizeBytes\"\n [acceptedFileTypes]=\"acceptedFileTypes\"\n [initialMessage]=\"inputRef.conversationId === conversationId ? pendingMessage : null\"\n [initialAttachments]=\"inputRef.conversationId === conversationId ? pendingAttachments : null\"\n (messageSent)=\"onMessageSent($event)\"\n (agentResponse)=\"onAgentResponse($event)\"\n (agentRunDetected)=\"onAgentRunDetected($event)\"\n (agentRunUpdate)=\"onAgentRunUpdate($event)\"\n (messageComplete)=\"onMessageComplete($event)\"\n (artifactCreated)=\"onArtifactCreated($event)\"\n (conversationRenamed)=\"onConversationRenamed($event)\"\n (intentCheckStarted)=\"onIntentCheckStarted()\"\n (intentCheckCompleted)=\"onIntentCheckCompleted()\"\n (uploadStateChanged)=\"onUploadStateChanged($event)\">\n </mj-message-input>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- Artifact Viewer Pane -->\n @if (showArtifactPanel && selectedArtifactId) {\n @if (!isArtifactPaneMaximized) {\n <div class=\"resize-handle\" (mousedown)=\"onResizeStart($event)\" (touchstart)=\"onResizeTouchStart($event)\"></div>\n }\n <div class=\"chat-artifact-pane\"\n [style.width.%]=\"artifactPaneWidth\"\n [class.maximized]=\"isArtifactPaneMaximized\">\n <mj-artifact-viewer-panel\n [artifactId]=\"selectedArtifactId\"\n [currentUser]=\"currentUser\"\n [environmentId]=\"environmentId\"\n [versionNumber]=\"selectedVersionNumber\"\n [viewContext]=\"'conversation'\"\n [canShare]=\"canShareSelectedArtifact\"\n [canEdit]=\"canEditSelectedArtifact\"\n [isMaximized]=\"isArtifactPaneMaximized\"\n [refreshTrigger]=\"artifactViewerRefresh$\"\n (closed)=\"onCloseArtifactPanel()\"\n (saveToCollectionRequested)=\"onSaveToCollectionRequested($event)\"\n (navigateToLink)=\"onArtifactLinkNavigation($event)\"\n (shareRequested)=\"onArtifactShareRequested($event)\"\n (maximizeToggled)=\"toggleMaximizeArtifactPane()\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\"\n (navigationRequest)=\"onNavigationRequest($event)\">\n </mj-artifact-viewer-panel>\n </div>\n }\n\n <!-- Artifact Share Modal -->\n <mj-artifact-share-modal\n [isOpen]=\"isArtifactShareModalOpen\"\n [artifact]=\"artifactToShare\"\n [currentUser]=\"currentUser\"\n (saved)=\"onArtifactShared()\"\n (cancelled)=\"onArtifactShareModalClose()\">\n </mj-artifact-share-modal>\n </div>\n</div>\n\n<!-- Thread Panel -->\n@if (threadId) {\n <mj-thread-panel\n [parentMessageId]=\"threadId\"\n [conversationId]=\"conversationId || ''\"\n [currentUser]=\"currentUser\"\n (closed)=\"onLocalThreadClosed()\"\n (replyAdded)=\"onThreadReplyAdded($event)\">\n </mj-thread-panel>\n}\n\n<!-- Export Modal -->\n<mj-export-modal\n [isVisible]=\"showExportModal\"\n [conversation]=\"conversation || undefined\"\n [currentUser]=\"currentUser\"\n (cancelled)=\"onExportModalCancelled()\"\n (exported)=\"onExportModalComplete()\">\n</mj-export-modal>\n\n<!-- Members Modal -->\n<mj-members-modal\n [isVisible]=\"showMembersModal\"\n [conversation]=\"conversation || undefined\"\n [currentUser]=\"currentUser\"\n (cancelled)=\"showMembersModal = false\"\n (membersChanged)=\"showMembersModal = false\">\n</mj-members-modal>\n\n<!-- Project Selector Modal -->\n@if (showProjectSelector && conversation) {\n <div class=\"modal-overlay\" (click)=\"showProjectSelector = false\">\n <div class=\"modal-content project-selector-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Assign Project</h3>\n <button class=\"modal-close-btn\" (click)=\"showProjectSelector = false\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n <div class=\"modal-body\">\n <mj-project-selector\n [environmentId]=\"environmentId\"\n [currentUser]=\"currentUser\"\n [selectedProjectId]=\"conversation.ProjectID\"\n (projectSelected)=\"onProjectSelected($event)\">\n </mj-project-selector>\n </div>\n </div>\n </div>\n}\n\n<!-- Artifacts Modal -->\n@if (showArtifactsModal) {\n <div class=\"modal-overlay\" (click)=\"showArtifactsModal = false\">\n <div class=\"modal-content artifacts-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Conversation Artifacts</h3>\n <div class=\"modal-header-actions\">\n @if (hasSystemArtifacts) {\n <button class=\"toggle-system-btn\"\n [class.active]=\"showSystemArtifacts\"\n (click)=\"toggleSystemArtifacts()\"\n title=\"Toggle system artifacts visibility\">\n <i class=\"fas fa-cog\"></i>\n <span>{{ showSystemArtifacts ? 'Hide' : 'Show' }} System</span>\n </button>\n }\n <button class=\"modal-close-btn\" (click)=\"showArtifactsModal = false\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n </div>\n <div class=\"modal-body artifacts-grid\">\n @if (artifactsByDetailId.size === 0) {\n <div class=\"empty-state\">\n <i class=\"fas fa-cube\" style=\"font-size: 48px; color: #D1D5DB; margin-bottom: 16px;\"></i>\n <p style=\"color: #6B7280; font-size: 14px;\">No artifacts in this conversation yet</p>\n </div>\n }\n @for (artifact of getArtifactsArray(); track artifact.artifactId) {\n <div class=\"artifact-modal-card\"\n [class.expanded]=\"expandedArtifactId === artifact.artifactId\"\n [class.system-artifact]=\"artifact.visibility === 'System Only'\">\n <!-- Main card header - click to open latest version -->\n <div class=\"artifact-card-header\" (click)=\"openArtifactFromModal(artifact.artifactId)\">\n <div class=\"artifact-modal-icon\">\n <i class=\"fas fa-file-code\"></i>\n </div>\n <div class=\"artifact-modal-info\">\n <div class=\"artifact-modal-title\">{{artifact.name}}</div>\n <div class=\"artifact-modal-meta\">\n @if (artifact.versionCount > 1) {\n {{artifact.versionCount}} versions\n } @else {\n 1 version\n }\n </div>\n </div>\n @if (artifact.versionCount > 1) {\n <button class=\"expand-btn\" (click)=\"toggleArtifactExpansion(artifact.artifactId, $event)\">\n <i class=\"fas\" [class.fa-chevron-down]=\"expandedArtifactId !== artifact.artifactId\"\n [class.fa-chevron-up]=\"expandedArtifactId === artifact.artifactId\"></i>\n </button>\n }\n <div class=\"artifact-modal-action\">\n <i class=\"fas fa-external-link-alt\"></i>\n </div>\n </div>\n\n <!-- Expanded version list -->\n @if (expandedArtifactId === artifact.artifactId && artifact.versionCount > 1) {\n <div class=\"artifact-versions-list\">\n @for (version of artifact.versions; track version.versionId) {\n <div class=\"artifact-version-item\" (click)=\"openArtifactFromModal(artifact.artifactId, version.versionNumber); $event.stopPropagation()\">\n <span class=\"version-badge\">v{{version.versionNumber}}</span>\n <span class=\"version-open-text\">Open this version</span>\n <i class=\"fas fa-arrow-right\"></i>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n </div>\n}\n\n<!-- Collection Picker Modal -->\n@if (showCollectionPicker) {\n <mj-artifact-collection-picker-modal\n [isOpen]=\"showCollectionPicker\"\n [environmentId]=\"environmentId\"\n [currentUser]=\"currentUser\"\n [excludeCollectionIds]=\"collectionPickerExcludedIds\"\n (saved)=\"onCollectionPickerSaved($event)\"\n (cancelled)=\"onCollectionPickerCancelled()\">\n </mj-artifact-collection-picker-modal>\n}\n\n<!-- Image Viewer Modal -->\n@if (showImageViewer) {\n <mj-image-viewer\n [imageUrl]=\"selectedImageUrl\"\n [alt]=\"selectedImageAlt\"\n [fileName]=\"selectedImageFileName\"\n [visible]=\"showImageViewer\"\n (closed)=\"onImageViewerClosed()\">\n </mj-image-viewer>\n}", styles: [":host {\n display: flex;\n width: 100%;\n height: 100%;\n}\n\n.chat-area {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.chat-header {\n flex-shrink: 0;\n padding: 12px 20px;\n border-bottom: 1px solid #D9D9D9;\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 16px;\n background: #FFF;\n z-index: 10;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n}\n\n.chat-info {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n}\n\n/* Sidebar toggle button in header */\n.sidebar-toggle-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n background: transparent;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\n\n.sidebar-toggle-btn:hover {\n background: rgba(0, 0, 0, 0.08);\n}\n\n.sidebar-toggle-btn:active {\n background: rgba(0, 0, 0, 0.12);\n}\n\n.sidebar-toggle-btn i {\n color: #666;\n font-size: 18px;\n transition: color 0.15s ease;\n}\n\n.sidebar-toggle-btn:hover i {\n color: #333;\n}\n\n.chat-title {\n font-size: 16px;\n font-weight: 600;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.project-tag {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: #F4F4F4;\n border: 1px solid #D9D9D9;\n border-radius: 16px;\n font-size: 11px;\n font-weight: 600;\n color: #AAA;\n cursor: pointer;\n transition: all 0.2s;\n height: 28px;\n margin-left: 12px;\n}\n\n.project-tag:hover {\n background: #D9D9D9;\n border-color: #AAA;\n}\n\n.project-tag i {\n font-size: 10px;\n}\n\n.test-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: #FFF8E1;\n border: 1px solid #FFD54F;\n border-radius: 16px;\n font-size: 11px;\n font-weight: 600;\n color: #F57C00;\n cursor: pointer;\n transition: all 0.2s;\n height: 28px;\n margin-left: 8px;\n}\n\n.test-indicator:hover {\n background: #FFE082;\n border-color: #FFA000;\n}\n\n.test-indicator i {\n font-size: 10px;\n}\n\n.chat-members,\n.artifact-indicator {\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n padding: 6px 8px;\n background: transparent;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n font-size: 14px;\n color: #6B7280;\n cursor: pointer;\n transition: all 150ms ease;\n}\n\n.chat-members:hover,\n.artifact-indicator:hover {\n background: #F9FAFB;\n color: #111827;\n}\n\n/* Badge overlay for artifact and member counts */\n.artifact-badge,\n.members-badge {\n position: absolute;\n top: -6px;\n right: -6px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n background: #3B82F6;\n color: white;\n font-size: 10px;\n font-weight: 600;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n}\n\n.members-badge {\n background: #6366F1;\n}\n\n.ambient-agent-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: #F3F4F6;\n border: 1px solid #D1D5DB;\n border-radius: 6px;\n font-size: 13px;\n color: #6B7280;\n animation: pulse 2s ease-in-out infinite;\n}\n\n.ambient-agent-indicator i {\n color: #0076B6;\n}\n\n@keyframes pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.7;\n }\n}\n.chat-actions {\n display: flex;\n gap: 8px;\n}\n\n.action-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 12px;\n background: transparent;\n border: 1px solid #E5E7EB;\n cursor: pointer;\n border-radius: 6px;\n font-size: 13px;\n color: #6B7280;\n transition: all 150ms ease;\n}\n\n.action-btn:hover {\n background: #F9FAFB;\n color: #111827;\n}\n\n.share-btn.shared {\n background: #EFF6FF;\n border-color: #1e40af;\n color: #1e40af;\n}\n\n.share-btn.shared:hover {\n background: #DBEAFE;\n color: #1e3a8a;\n}\n\n.chat-content-area {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n display: flex;\n flex-direction: row;\n position: relative;\n}\n\n.chat-messages-pane {\n height: 100%;\n display: flex;\n flex-direction: column;\n min-width: min(300px, 100%); /* Respect container bounds while maintaining minimum */\n overflow: hidden;\n transition: width 0.3s ease;\n}\n\n.chat-messages-pane.full-width {\n width: 100%;\n}\n\n.chat-messages-pane:not(.full-width) {\n flex: 1;\n}\n\n.chat-messages-pane.hidden {\n display: none;\n}\n\n.resize-handle {\n width: 4px;\n background: transparent;\n cursor: col-resize;\n flex-shrink: 0;\n position: relative;\n transition: background-color 0.2s;\n}\n\n.resize-handle:hover {\n background: #3B82F6;\n}\n\n.resize-handle::before {\n content: \"\";\n position: absolute;\n left: -4px;\n right: -4px;\n top: 0;\n bottom: 0;\n}\n\n.chat-artifact-pane {\n height: 100%;\n display: flex;\n flex-direction: column;\n background: #FAFAFA;\n overflow: hidden;\n flex-shrink: 0;\n}\n\n.chat-artifact-pane.maximized {\n width: 100% !important;\n}\n\n.chat-artifact-pane > mj-artifact-viewer-panel {\n display: flex;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n.chat-messages-wrapper {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow: hidden;\n position: relative; /* For upload overlay positioning */\n}\n\n/* Upload indicator overlay - centered in conversation area */\n.upload-indicator-overlay {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem 1.5rem;\n background: rgba(255, 255, 255, 0.95);\n border-radius: 12px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);\n z-index: 100;\n pointer-events: none;\n}\n\n.chat-messages-container {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n background: #FFF;\n min-height: 0;\n position: relative;\n}\n\n.scroll-to-bottom-icon {\n position: sticky;\n bottom: 21px;\n left: 50%;\n transform: translateX(-50%);\n width: 40px;\n height: 40px;\n margin-top: -40px;\n margin-left: auto;\n margin-right: auto;\n background: white;\n border: 1px solid #D1D5DB;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n transition: all 0.2s ease;\n z-index: 100;\n pointer-events: auto;\n}\n\n.scroll-to-bottom-icon:hover {\n background: #F3F4F6;\n border-color: #3B82F6;\n transform: translateX(-50%) translateY(-2px);\n box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);\n}\n\n.scroll-to-bottom-icon i {\n color: #6B7280;\n font-size: 16px;\n transition: color 0.2s;\n}\n\n.scroll-to-bottom-icon:hover i {\n color: #3B82F6;\n}\n\n.chat-input-container {\n flex-shrink: 0;\n background: #FFF;\n padding: 0 1.25rem 1.25rem 1.25rem;\n overflow: visible;\n}\n\n.loading-peripheral-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 140px;\n padding: 24px;\n background: rgba(255, 255, 255, 0.5);\n backdrop-filter: blur(2px);\n border-radius: 12px;\n margin: 12px;\n animation: fadeIn 0.2s ease-in-out;\n}\n\n.modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n\n.modal-content {\n background: white;\n border-radius: 8px;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n max-width: 90vw;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n}\n\n.project-selector-modal {\n width: 600px;\n height: 500px;\n}\n\n.modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #E5E7EB;\n}\n\n.modal-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n}\n\n.modal-header-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toggle-system-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #F3F4F6;\n border: 1px solid #E5E7EB;\n cursor: pointer;\n color: #6B7280;\n padding: 6px 12px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n transition: all 0.2s;\n}\n\n.toggle-system-btn:hover {\n background: #E5E7EB;\n border-color: #D1D5DB;\n color: #374151;\n}\n\n.toggle-system-btn.active {\n background: #3B82F6;\n border-color: #3B82F6;\n color: white;\n}\n\n.toggle-system-btn.active:hover {\n background: #2563EB;\n border-color: #2563EB;\n}\n\n.toggle-system-btn i {\n font-size: 12px;\n}\n\n.modal-close-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: #6B7280;\n padding: 4px 8px;\n border-radius: 4px;\n transition: all 0.2s;\n}\n\n.modal-close-btn:hover {\n background: #F3F4F6;\n color: #111827;\n}\n\n.modal-body {\n flex: 1;\n overflow: auto;\n padding: 20px;\n}\n\n.artifacts-modal {\n width: 700px;\n max-height: 600px;\n}\n\n.artifacts-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 16px;\n}\n\n.empty-state {\n grid-column: 1/-1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n}\n\n.artifact-modal-card {\n display: flex;\n flex-direction: column;\n background: white;\n border: 1.5px solid #E5E7EB;\n border-radius: 12px;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n}\n\n.artifact-modal-card.expanded {\n border-color: #3B82F6;\n}\n\n.artifact-modal-card.system-artifact {\n opacity: 0.85;\n border-color: #D1D5DB;\n border-style: dashed;\n position: relative;\n}\n\n.artifact-modal-card.system-artifact::before {\n content: \"SYSTEM\";\n position: absolute;\n top: 8px;\n right: 8px;\n font-size: 9px;\n font-weight: 600;\n color: #9CA3AF;\n background: #F3F4F6;\n padding: 2px 6px;\n border-radius: 3px;\n letter-spacing: 0.5px;\n z-index: 10;\n}\n\n.artifact-modal-card.system-artifact:hover {\n border-color: #9CA3AF;\n box-shadow: 0 4px 12px rgba(156, 163, 175, 0.15);\n}\n\n.artifact-card-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px;\n cursor: pointer;\n}\n\n.artifact-card-header:hover {\n background: #F9FAFB;\n}\n\n.artifact-modal-card:hover {\n border-color: #3B82F6;\n box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);\n transform: translateY(-2px);\n}\n\n.artifact-modal-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: linear-gradient(135deg, #EFF6FF 0%, #DBEAFE 100%);\n border-radius: 10px;\n color: #3B82F6;\n flex-shrink: 0;\n}\n\n.artifact-modal-icon i {\n font-size: 18px;\n}\n\n.artifact-modal-info {\n flex: 1;\n min-width: 0;\n}\n\n.artifact-modal-title {\n font-size: 14px;\n font-weight: 600;\n color: #1F2937;\n margin-bottom: 4px;\n}\n\n.artifact-modal-meta {\n font-size: 12px;\n color: #6B7280;\n}\n\n.artifact-modal-action {\n color: #9CA3AF;\n transition: color 0.2s;\n}\n\n.artifact-modal-card:hover .artifact-modal-action {\n color: #3B82F6;\n}\n\n.expand-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n color: #6B7280;\n cursor: pointer;\n border-radius: 6px;\n transition: all 0.2s;\n}\n.expand-btn:hover {\n background: #F3F4F6;\n color: #3B82F6;\n}\n\n.artifact-versions-list {\n display: flex;\n flex-direction: column;\n padding: 0 1rem 1rem 1rem;\n background: #F9FAFB;\n}\n\n.artifact-version-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px 12px 68px;\n cursor: pointer;\n transition: background 0.15s;\n}\n.artifact-version-item:hover {\n background: #F3F4F6;\n}\n.artifact-version-item .version-badge {\n display: inline-block;\n padding: 4px 8px;\n background: #EEF2FF;\n color: #4F46E5;\n font-size: 12px;\n font-weight: 600;\n font-family: monospace;\n border-radius: 4px;\n}\n.artifact-version-item .version-open-text {\n flex: 1;\n font-size: 13px;\n color: #6B7280;\n}\n.artifact-version-item i {\n color: #9CA3AF;\n font-size: 12px;\n}\n.artifact-version-item:hover .version-badge {\n background: #4F46E5;\n color: white;\n}\n.artifact-version-item:hover .version-open-text {\n color: #3B82F6;\n}\n.artifact-version-item:hover i {\n color: #3B82F6;\n}\n\n.loading-peripheral-content {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 24px;\n background: white;\n border: 2px solid #E5E7EB;\n border-radius: 12px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n color: #4B5563;\n font-size: 14px;\n font-weight: 500;\n}\n.loading-peripheral-content i {\n font-size: 20px;\n color: #9333EA;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n.conversation-loading-state {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n width: 100%;\n}\n\n.loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding-top: 20px;\n gap: 16px;\n color: #6B7280;\n font-size: 15px;\n}\n.loading-content i {\n font-size: 32px;\n color: #3B82F6;\n}\n.loading-content span {\n font-weight: 500;\n}\n\n/* Mobile adjustments: 481px - 768px */\n@media (max-width: 768px) {\n .chat-header {\n padding: 8px 12px;\n gap: 6px;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: center;\n position: relative;\n }\n .chat-info {\n flex-direction: row;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n order: 1;\n }\n .chat-title {\n font-size: 15px;\n font-weight: 700;\n width: auto;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n flex: 1;\n min-width: 0;\n }\n .project-tag {\n margin-left: 0;\n font-size: 10px;\n padding: 3px 8px;\n height: 24px;\n display: none; /* Hide on mobile to save space */\n }\n .test-indicator {\n margin-left: 0;\n font-size: 10px;\n padding: 3px 8px;\n height: 24px;\n }\n /* Action buttons - icon only on mobile */\n .chat-actions-buttons {\n order: 2;\n flex-shrink: 0;\n }\n .chat-actions-buttons .action-btn {\n padding: 6px 8px;\n min-width: auto;\n }\n .chat-actions-buttons .action-btn .btn-label {\n display: none;\n }\n .chat-actions {\n flex-wrap: nowrap;\n }\n .chat-members,\n .artifact-indicator {\n padding: 5px 7px;\n font-size: 13px;\n }\n .action-btn {\n padding: 6px 8px;\n font-size: 12px;\n }\n .ambient-agent-indicator {\n font-size: 12px;\n padding: 4px 8px;\n }\n .project-selector-modal {\n width: min(95vw, 600px);\n height: auto;\n }\n .artifacts-modal {\n width: min(95vw, 700px);\n }\n .artifacts-grid {\n grid-template-columns: 1fr;\n }\n .chat-input-container {\n padding: 0 0.75rem 0.75rem 0.75rem;\n }\n .scroll-to-bottom-icon {\n bottom: 16px;\n width: 36px;\n height: 36px;\n }\n /* Artifact pane - full width overlay on mobile, overlapping header */\n .chat-content-area {\n position: relative;\n }\n .chat-artifact-pane {\n position: fixed;\n left: 0;\n right: 0;\n top: 56px; /* 48px nav + 8px dark strip above blue border */\n bottom: 0;\n width: 100% !important;\n z-index: 100;\n background: #FFF;\n }\n .resize-handle {\n display: none;\n }\n}\n/* Small Phone adjustments: <= 480px */\n@media (max-width: 480px) {\n .chat-header {\n padding: 6px 8px;\n gap: 4px;\n }\n .chat-title {\n font-size: 14px;\n font-weight: 700;\n }\n .project-tag {\n font-size: 9px;\n padding: 2px 6px;\n height: 20px;\n display: none;\n }\n .test-indicator {\n font-size: 9px;\n padding: 2px 6px;\n height: 20px;\n }\n .chat-members,\n .artifact-indicator {\n padding: 4px 8px;\n font-size: 11px;\n }\n .action-btn {\n padding: 5px 7px;\n font-size: 11px;\n }\n .ambient-agent-indicator {\n font-size: 11px;\n padding: 3px 6px;\n }\n .project-selector-modal,\n .artifacts-modal {\n width: 100vw;\n height: 100vh;\n border-radius: 0;\n }\n .chat-input-container {\n padding: 0 0.5rem 0.5rem 0.5rem;\n }\n .scroll-to-bottom-icon {\n bottom: 12px;\n width: 32px;\n height: 32px;\n }\n .scroll-to-bottom-icon i {\n font-size: 14px;\n }\n}\n"] }]
2270
2403
  }], () => [{ type: i1.ConversationDataService }, { type: i2.AgentStateService }, { type: i3.ConversationAgentService }, { type: i4.ActiveTasksService }, { type: i0.ChangeDetectorRef }, { type: i5.MentionAutocompleteService }, { type: i6.ArtifactPermissionService }, { type: i7.DialogService }, { type: i8.ConversationAttachmentService }, { type: i9.ConversationStreamingService }], { environmentId: [{
2271
2404
  type: Input
2272
2405
  }], currentUser: [{
@@ -2293,6 +2426,8 @@ export class ConversationChatAreaComponent {
2293
2426
  type: Output
2294
2427
  }], openEntityRecord: [{
2295
2428
  type: Output
2429
+ }], navigationRequest: [{
2430
+ type: Output
2296
2431
  }], taskClicked: [{
2297
2432
  type: Output
2298
2433
  }], artifactLinkClicked: [{