@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.
- package/dist/__tests__/mention-parser.test.js +2 -2
- package/dist/__tests__/mention-parser.test.js.map +1 -1
- package/dist/lib/components/artifact/artifact-share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/artifact/artifact-share-modal.component.js +1 -0
- package/dist/lib/components/artifact/artifact-share-modal.component.js.map +1 -1
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts +3 -2
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.js +16 -7
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.js.map +1 -1
- package/dist/lib/components/collection/artifact-create-modal.component.d.ts +3 -2
- package/dist/lib/components/collection/artifact-create-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/artifact-create-modal.component.js +6 -3
- package/dist/lib/components/collection/artifact-create-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-artifact-card.component.d.ts +3 -2
- package/dist/lib/components/collection/collection-artifact-card.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-artifact-card.component.js +8 -3
- package/dist/lib/components/collection/collection-artifact-card.component.js.map +1 -1
- package/dist/lib/components/collection/collection-share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-share-modal.component.js +2 -0
- package/dist/lib/components/collection/collection-share-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.d.ts +19 -3
- package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.js +580 -233
- package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +52 -14
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +243 -108
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-list.component.d.ts +3 -1
- package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-list.component.js +14 -2
- package/dist/lib/components/conversation/conversation-list.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +3 -3
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +22 -26
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +2 -2
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +2 -2
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +6 -2
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/share/share-modal.component.d.ts +3 -2
- package/dist/lib/components/share/share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/share/share-modal.component.js +9 -3
- package/dist/lib/components/share/share-modal.component.js.map +1 -1
- package/dist/lib/components/shared/user-picker.component.d.ts +3 -1
- package/dist/lib/components/shared/user-picker.component.d.ts.map +1 -1
- package/dist/lib/components/shared/user-picker.component.js +7 -2
- package/dist/lib/components/shared/user-picker.component.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +4 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +10 -4
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/models/conversation-state.model.d.ts +3 -3
- package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
- package/dist/lib/models/lazy-artifact-info.d.ts +32 -31
- package/dist/lib/models/lazy-artifact-info.d.ts.map +1 -1
- package/dist/lib/models/lazy-artifact-info.js +99 -75
- package/dist/lib/models/lazy-artifact-info.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +3 -3
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-streaming.service.d.ts +6 -1
- package/dist/lib/services/conversation-streaming.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-streaming.service.js +14 -9
- package/dist/lib/services/conversation-streaming.service.js.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.d.ts +2 -2
- package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
- package/dist/lib/services/mention-parser.service.d.ts +4 -4
- package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
- package/dist/lib/services/mention-parser.service.js.map +1 -1
- 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 ->
|
|
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.
|
|
758
|
-
|
|
759
|
-
|
|
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
|
|
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
|
-
//
|
|
1189
|
-
|
|
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
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
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
|
|
1283
|
-
if (
|
|
1284
|
-
|
|
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
|
-
*
|
|
1336
|
-
*
|
|
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: '
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
*
|
|
1417
|
-
*
|
|
1418
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
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
|
|
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
|
|
1726
|
-
if (
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
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
|
-
|
|
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,
|
|
2098
|
-
|
|
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
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
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: [{
|