@memberjunction/ng-conversations 5.47.0 → 5.49.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/README.md +37 -0
- package/dist/lib/components/composer/ai-composer.component.d.ts +8 -2
- package/dist/lib/components/composer/ai-composer.component.d.ts.map +1 -1
- package/dist/lib/components/composer/ai-composer.component.js +16 -3
- package/dist/lib/components/composer/ai-composer.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +90 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +322 -99
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts +16 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.js +62 -24
- package/dist/lib/components/conversation/conversation-empty-state.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +48 -1
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +146 -6
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +13 -1
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +307 -235
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +18 -1
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +48 -4
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.js +2 -2
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.js.map +1 -1
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js +2 -2
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js.map +1 -1
- package/dist/lib/services/composer-draft-store.d.ts +52 -0
- package/dist/lib/services/composer-draft-store.d.ts.map +1 -0
- package/dist/lib/services/composer-draft-store.js +123 -0
- package/dist/lib/services/composer-draft-store.js.map +1 -0
- package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js +5 -1
- package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session.service.js +2 -1
- package/dist/lib/services/realtime-session.service.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +29 -29
|
@@ -19,6 +19,16 @@ export declare class MessageListComponent extends BaseAngularComponent implement
|
|
|
19
19
|
conversation: MJConversationEntity | null;
|
|
20
20
|
currentUser: UserInfo;
|
|
21
21
|
isProcessing: boolean;
|
|
22
|
+
/** Whether the built-in "No messages yet" filler renders for empty conversations. Hosts with their own empty-state chrome set false. */
|
|
23
|
+
showEmptyFill: boolean;
|
|
24
|
+
/** Whether the sticky date header + jump-to-date dropdown render. */
|
|
25
|
+
showDateNavigation: boolean;
|
|
26
|
+
showAgentRunDetails: boolean;
|
|
27
|
+
showReactions: boolean;
|
|
28
|
+
showMessageRating: boolean;
|
|
29
|
+
allowPinning: boolean;
|
|
30
|
+
allowMessageEdit: boolean;
|
|
31
|
+
allowMessageDelete: boolean;
|
|
22
32
|
artifactMap: Map<string, LazyArtifactInfo[]>;
|
|
23
33
|
agentRunMap: Map<string, MJAIAgentRunEntityExtended>;
|
|
24
34
|
ratingsMap: Map<string, RatingJSON[]>;
|
|
@@ -146,6 +156,13 @@ export declare class MessageListComponent extends BaseAngularComponent implement
|
|
|
146
156
|
* etc. changes mid-stream.
|
|
147
157
|
*/
|
|
148
158
|
private updateMessageItemInstance;
|
|
159
|
+
/**
|
|
160
|
+
* Forward the host-level per-message feature gates onto a MessageItemComponent
|
|
161
|
+
* instance. These are static host config (not per-message data), so the same
|
|
162
|
+
* values apply to every message; applied on both the create and in-place-update
|
|
163
|
+
* paths so a mid-session rebind stays consistent.
|
|
164
|
+
*/
|
|
165
|
+
private applyMessageItemFeatureFlags;
|
|
149
166
|
/**
|
|
150
167
|
* Creates a new rendered entry — either a `MessageItemComponent` (default path)
|
|
151
168
|
* or an `EmbeddedViewRef` from the consumer's `messageRenderer` slot template.
|
|
@@ -201,6 +218,6 @@ export declare class MessageListComponent extends BaseAngularComponent implement
|
|
|
201
218
|
*/
|
|
202
219
|
removeMessage(message: MJConversationDetailEntity): void;
|
|
203
220
|
static ɵfac: i0.ɵɵFactoryDeclaration<MessageListComponent, never>;
|
|
204
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<MessageListComponent, "mj-conversation-message-list", never, { "messages": { "alias": "messages"; "required": false; }; "conversation": { "alias": "conversation"; "required": false; }; "currentUser": { "alias": "currentUser"; "required": false; }; "isProcessing": { "alias": "isProcessing"; "required": false; }; "artifactMap": { "alias": "artifactMap"; "required": false; }; "agentRunMap": { "alias": "agentRunMap"; "required": false; }; "ratingsMap": { "alias": "ratingsMap"; "required": false; }; "userAvatarMap": { "alias": "userAvatarMap"; "required": false; }; "attachmentsMap": { "alias": "attachmentsMap"; "required": false; }; "sessionMetaMap": { "alias": "sessionMetaMap"; "required": false; }; "messageRendererTemplate": { "alias": "messageRendererTemplate"; "required": false; }; "messageExtraTemplate": { "alias": "messageExtraTemplate"; "required": false; }; "messagesUpdate": { "alias": "messagesUpdate"; "required": false; }; }, { "editMessage": "editMessage"; "deleteMessage": "deleteMessage"; "retryMessage": "retryMessage"; "testFeedbackMessage": "testFeedbackMessage"; "artifactClicked": "artifactClicked"; "replyInThread": "replyInThread"; "viewThread": "viewThread"; "messageEdited": "messageEdited"; "openEntityRecord": "openEntityRecord"; "suggestedResponseSelected": "suggestedResponseSelected"; "attachmentClicked": "attachmentClicked"; "diagnosticRequested": "diagnosticRequested"; "messagePinToggled": "messagePinToggled"; "realtimeSessionOpenRequested": "realtimeSessionOpenRequested"; "beforeResponseFormSubmitted": "beforeResponseFormSubmitted"; "afterResponseFormSubmitted": "afterResponseFormSubmitted"; }, never, never, false, never>;
|
|
221
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<MessageListComponent, "mj-conversation-message-list", never, { "messages": { "alias": "messages"; "required": false; }; "conversation": { "alias": "conversation"; "required": false; }; "currentUser": { "alias": "currentUser"; "required": false; }; "isProcessing": { "alias": "isProcessing"; "required": false; }; "showEmptyFill": { "alias": "showEmptyFill"; "required": false; }; "showDateNavigation": { "alias": "showDateNavigation"; "required": false; }; "showAgentRunDetails": { "alias": "showAgentRunDetails"; "required": false; }; "showReactions": { "alias": "showReactions"; "required": false; }; "showMessageRating": { "alias": "showMessageRating"; "required": false; }; "allowPinning": { "alias": "allowPinning"; "required": false; }; "allowMessageEdit": { "alias": "allowMessageEdit"; "required": false; }; "allowMessageDelete": { "alias": "allowMessageDelete"; "required": false; }; "artifactMap": { "alias": "artifactMap"; "required": false; }; "agentRunMap": { "alias": "agentRunMap"; "required": false; }; "ratingsMap": { "alias": "ratingsMap"; "required": false; }; "userAvatarMap": { "alias": "userAvatarMap"; "required": false; }; "attachmentsMap": { "alias": "attachmentsMap"; "required": false; }; "sessionMetaMap": { "alias": "sessionMetaMap"; "required": false; }; "messageRendererTemplate": { "alias": "messageRendererTemplate"; "required": false; }; "messageExtraTemplate": { "alias": "messageExtraTemplate"; "required": false; }; "messagesUpdate": { "alias": "messagesUpdate"; "required": false; }; }, { "editMessage": "editMessage"; "deleteMessage": "deleteMessage"; "retryMessage": "retryMessage"; "testFeedbackMessage": "testFeedbackMessage"; "artifactClicked": "artifactClicked"; "replyInThread": "replyInThread"; "viewThread": "viewThread"; "messageEdited": "messageEdited"; "openEntityRecord": "openEntityRecord"; "suggestedResponseSelected": "suggestedResponseSelected"; "attachmentClicked": "attachmentClicked"; "diagnosticRequested": "diagnosticRequested"; "messagePinToggled": "messagePinToggled"; "realtimeSessionOpenRequested": "realtimeSessionOpenRequested"; "beforeResponseFormSubmitted": "beforeResponseFormSubmitted"; "afterResponseFormSubmitted": "afterResponseFormSubmitted"; }, never, never, false, never>;
|
|
205
222
|
}
|
|
206
223
|
//# sourceMappingURL=message-list.component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-list.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/message/message-list.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,YAAY,EAEZ,gBAAgB,EAChB,MAAM,EACN,SAAS,EACT,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,gBAAgB,EAGhB,WAAW,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC7G,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAwB,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,EACL,oCAAoC,EACpC,mCAAmC,EACpC,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAIL,2BAA2B,EAC5B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;;AA4B1E;;;;GAIG;AACH,qBAMa,oBAAqB,SAAQ,oBAAqB,YAAW,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB;
|
|
1
|
+
{"version":3,"file":"message-list.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/message/message-list.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -58,6 +58,18 @@ export class MessageListComponent extends BaseAngularComponent {
|
|
|
58
58
|
conversation;
|
|
59
59
|
currentUser;
|
|
60
60
|
isProcessing = false;
|
|
61
|
+
/** Whether the built-in "No messages yet" filler renders for empty conversations. Hosts with their own empty-state chrome set false. */
|
|
62
|
+
showEmptyFill = true;
|
|
63
|
+
/** Whether the sticky date header + jump-to-date dropdown render. */
|
|
64
|
+
showDateNavigation = true;
|
|
65
|
+
// Per-message feature gates — forwarded onto each MessageItemComponent instance
|
|
66
|
+
// (see applyMessageItemFeatureFlags). All default true.
|
|
67
|
+
showAgentRunDetails = true;
|
|
68
|
+
showReactions = true;
|
|
69
|
+
showMessageRating = true;
|
|
70
|
+
allowPinning = true;
|
|
71
|
+
allowMessageEdit = true;
|
|
72
|
+
allowMessageDelete = true;
|
|
61
73
|
artifactMap = new Map();
|
|
62
74
|
agentRunMap = new Map();
|
|
63
75
|
ratingsMap = new Map();
|
|
@@ -367,6 +379,7 @@ export class MessageListComponent extends BaseAngularComponent {
|
|
|
367
379
|
instance.userAvatarMap = this.userAvatarMap;
|
|
368
380
|
instance.isLastMessage = (index === messages.length - 1);
|
|
369
381
|
instance.messageExtraTemplate = this.messageExtraTemplate;
|
|
382
|
+
this.applyMessageItemFeatureFlags(instance);
|
|
370
383
|
this.applyArtifactsToInstance(instance, message.ID, ref.changeDetectorRef);
|
|
371
384
|
instance.agentRun = this.agentRunMap.get(message.ID) || null;
|
|
372
385
|
instance.ratings = this.ratingsMap.get(message.ID);
|
|
@@ -376,6 +389,20 @@ export class MessageListComponent extends BaseAngularComponent {
|
|
|
376
389
|
ref.changeDetectorRef.markForCheck();
|
|
377
390
|
}
|
|
378
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Forward the host-level per-message feature gates onto a MessageItemComponent
|
|
394
|
+
* instance. These are static host config (not per-message data), so the same
|
|
395
|
+
* values apply to every message; applied on both the create and in-place-update
|
|
396
|
+
* paths so a mid-session rebind stays consistent.
|
|
397
|
+
*/
|
|
398
|
+
applyMessageItemFeatureFlags(instance) {
|
|
399
|
+
instance.showAgentRunDetails = this.showAgentRunDetails;
|
|
400
|
+
instance.showReactions = this.showReactions;
|
|
401
|
+
instance.showMessageRating = this.showMessageRating;
|
|
402
|
+
instance.allowPinning = this.allowPinning;
|
|
403
|
+
instance.allowMessageEdit = this.allowMessageEdit;
|
|
404
|
+
instance.allowMessageDelete = this.allowMessageDelete;
|
|
405
|
+
}
|
|
379
406
|
/**
|
|
380
407
|
* Creates a new rendered entry — either a `MessageItemComponent` (default path)
|
|
381
408
|
* or an `EmbeddedViewRef` from the consumer's `messageRenderer` slot template.
|
|
@@ -403,6 +430,7 @@ export class MessageListComponent extends BaseAngularComponent {
|
|
|
403
430
|
instance.userAvatarMap = this.userAvatarMap;
|
|
404
431
|
instance.isLastMessage = (index === messages.length - 1);
|
|
405
432
|
instance.messageExtraTemplate = this.messageExtraTemplate;
|
|
433
|
+
this.applyMessageItemFeatureFlags(instance);
|
|
406
434
|
this.applyArtifactsToInstance(instance, message.ID, componentRef.changeDetectorRef);
|
|
407
435
|
instance.agentRun = this.agentRunMap.get(message.ID) || null;
|
|
408
436
|
instance.ratings = this.ratingsMap.get(message.ID);
|
|
@@ -547,7 +575,7 @@ export class MessageListComponent extends BaseAngularComponent {
|
|
|
547
575
|
let _t;
|
|
548
576
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.messageContainerRef = _t.first);
|
|
549
577
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.scrollContainer = _t.first);
|
|
550
|
-
} }, inputs: { messages: "messages", conversation: "conversation", currentUser: "currentUser", isProcessing: "isProcessing", artifactMap: "artifactMap", agentRunMap: "agentRunMap", ratingsMap: "ratingsMap", userAvatarMap: "userAvatarMap", attachmentsMap: "attachmentsMap", sessionMetaMap: "sessionMetaMap", messageRendererTemplate: "messageRendererTemplate", messageExtraTemplate: "messageExtraTemplate", messagesUpdate: "messagesUpdate" }, outputs: { editMessage: "editMessage", deleteMessage: "deleteMessage", retryMessage: "retryMessage", testFeedbackMessage: "testFeedbackMessage", artifactClicked: "artifactClicked", replyInThread: "replyInThread", viewThread: "viewThread", messageEdited: "messageEdited", openEntityRecord: "openEntityRecord", suggestedResponseSelected: "suggestedResponseSelected", attachmentClicked: "attachmentClicked", diagnosticRequested: "diagnosticRequested", messagePinToggled: "messagePinToggled", realtimeSessionOpenRequested: "realtimeSessionOpenRequested", beforeResponseFormSubmitted: "beforeResponseFormSubmitted", afterResponseFormSubmitted: "afterResponseFormSubmitted" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 6, vars: 2, consts: [["scrollContainer", ""], ["messageContainer", ""], [1, "message-list-container"], [1, "sticky-date-header"], ["Icon", "fa-solid fa-comments", "Title", "No messages yet. Start a conversation!", 1, "messages-empty-fill"], [1, "sticky-date-wrapper"], [1, "sticky-date-button", 3, "click"], [1, "fas", "fa-chevron-down"], [1, "date-nav-dropdown"], [1, "date-nav-option", "jump-to"], [1, "date-nav-option", 3, "click"]], template: function MessageListComponent_Template(rf, ctx) { if (rf & 1) {
|
|
578
|
+
} }, inputs: { messages: "messages", conversation: "conversation", currentUser: "currentUser", isProcessing: "isProcessing", showEmptyFill: "showEmptyFill", showDateNavigation: "showDateNavigation", showAgentRunDetails: "showAgentRunDetails", showReactions: "showReactions", showMessageRating: "showMessageRating", allowPinning: "allowPinning", allowMessageEdit: "allowMessageEdit", allowMessageDelete: "allowMessageDelete", artifactMap: "artifactMap", agentRunMap: "agentRunMap", ratingsMap: "ratingsMap", userAvatarMap: "userAvatarMap", attachmentsMap: "attachmentsMap", sessionMetaMap: "sessionMetaMap", messageRendererTemplate: "messageRendererTemplate", messageExtraTemplate: "messageExtraTemplate", messagesUpdate: "messagesUpdate" }, outputs: { editMessage: "editMessage", deleteMessage: "deleteMessage", retryMessage: "retryMessage", testFeedbackMessage: "testFeedbackMessage", artifactClicked: "artifactClicked", replyInThread: "replyInThread", viewThread: "viewThread", messageEdited: "messageEdited", openEntityRecord: "openEntityRecord", suggestedResponseSelected: "suggestedResponseSelected", attachmentClicked: "attachmentClicked", diagnosticRequested: "diagnosticRequested", messagePinToggled: "messagePinToggled", realtimeSessionOpenRequested: "realtimeSessionOpenRequested", beforeResponseFormSubmitted: "beforeResponseFormSubmitted", afterResponseFormSubmitted: "afterResponseFormSubmitted" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 6, vars: 2, consts: [["scrollContainer", ""], ["messageContainer", ""], [1, "message-list-container"], [1, "sticky-date-header"], ["Icon", "fa-solid fa-comments", "Title", "No messages yet. Start a conversation!", 1, "messages-empty-fill"], [1, "sticky-date-wrapper"], [1, "sticky-date-button", 3, "click"], [1, "fas", "fa-chevron-down"], [1, "date-nav-dropdown"], [1, "date-nav-option", "jump-to"], [1, "date-nav-option", 3, "click"]], template: function MessageListComponent_Template(rf, ctx) { if (rf & 1) {
|
|
551
579
|
i0.ɵɵelementStart(0, "div", 2, 0);
|
|
552
580
|
i0.ɵɵconditionalCreate(2, MessageListComponent_Conditional_2_Template, 17, 3, "div", 3);
|
|
553
581
|
i0.ɵɵelementContainer(3, null, 1);
|
|
@@ -555,14 +583,14 @@ export class MessageListComponent extends BaseAngularComponent {
|
|
|
555
583
|
i0.ɵɵelementEnd();
|
|
556
584
|
} if (rf & 2) {
|
|
557
585
|
i0.ɵɵadvance(2);
|
|
558
|
-
i0.ɵɵconditional(ctx.shouldShowDateFilter && ctx.messages.length > 0 ? 2 : -1);
|
|
586
|
+
i0.ɵɵconditional(ctx.showDateNavigation && ctx.shouldShowDateFilter && ctx.messages.length > 0 ? 2 : -1);
|
|
559
587
|
i0.ɵɵadvance(3);
|
|
560
|
-
i0.ɵɵconditional(ctx.messages.length === 0 ? 5 : -1);
|
|
588
|
+
i0.ɵɵconditional(ctx.messages.length === 0 && ctx.showEmptyFill ? 5 : -1);
|
|
561
589
|
} }, dependencies: [i1.MJEmptyStateComponent], styles: [".message-list-container[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n background-color: var(--mj-bg-surface-card);\n position: relative;\n}\n\n\n\n.messages-empty-fill[_ngcontent-%COMP%] {\n height: 100%;\n}\n\n\n\n.sticky-date-header[_ngcontent-%COMP%] {\n position: sticky;\n top: 12px;\n z-index: 100;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0;\n margin: 0;\n pointer-events: none;\n}\n\n.sticky-date-wrapper[_ngcontent-%COMP%] {\n pointer-events: auto;\n position: relative;\n}\n\n.sticky-date-button[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 16px;\n background: var(--mj-bg-surface-card);\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n cursor: pointer;\n transition: all 0.2s;\n box-shadow: var(--mj-shadow-sm);\n}\n\n.sticky-date-button[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n box-shadow: var(--mj-shadow-md);\n}\n\n.sticky-date-button[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--mj-text-secondary);\n}\n\n\n\n.date-nav-dropdown[_ngcontent-%COMP%] {\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n margin-top: 8px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-lg);\n min-width: 200px;\n display: none;\n z-index: 101;\n}\n\n.date-nav-dropdown.show[_ngcontent-%COMP%] {\n display: block;\n}\n\n.date-nav-option[_ngcontent-%COMP%] {\n padding: 10px 16px;\n cursor: pointer;\n transition: background 0.2s;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.date-nav-option[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.date-nav-option.jump-to[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-secondary);\n font-size: 12px;\n cursor: default;\n padding: 8px 16px 4px 16px;\n}\n\n.date-nav-option.jump-to[_ngcontent-%COMP%]:hover {\n background: none;\n}"] });
|
|
562
590
|
}
|
|
563
591
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MessageListComponent, [{
|
|
564
592
|
type: Component,
|
|
565
|
-
args: [{ standalone: false, selector: 'mj-conversation-message-list', template: "<div class=\"message-list-container\" #scrollContainer>\n <!-- Sticky Date Header (only show for long conversations spanning multiple days) -->\n @if (shouldShowDateFilter && messages.length > 0) {\n <div class=\"sticky-date-header\">\n <div class=\"sticky-date-wrapper\">\n <button class=\"sticky-date-button\" (click)=\"toggleDateNav()\">\n <span>{{ currentDateDisplay }}</span>\n <i class=\"fas fa-chevron-down\"></i>\n </button>\n <div class=\"date-nav-dropdown\" [class.show]=\"showDateNav\">\n <div class=\"date-nav-option jump-to\">Jump to...</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('today')\">Today</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('yesterday')\">Yesterday</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('last-week')\">Last week</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('last-month')\">Last month</div>\n </div>\n </div>\n </div>\n }\n\n <ng-container #messageContainer></ng-container>\n\n @if (messages.length === 0) {\n <mj-empty-state\n class=\"messages-empty-fill\"\n Icon=\"fa-solid fa-comments\"\n Title=\"No messages yet. Start a conversation!\" />\n }\n</div>", styles: [".message-list-container {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n background-color: var(--mj-bg-surface-card);\n position: relative;\n}\n\n/* Empty-state fills the message pane vertically */\n.messages-empty-fill {\n height: 100%;\n}\n\n/* Sticky Date Header */\n.sticky-date-header {\n position: sticky;\n top: 12px;\n z-index: 100;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0;\n margin: 0;\n pointer-events: none;\n}\n\n.sticky-date-wrapper {\n pointer-events: auto;\n position: relative;\n}\n\n.sticky-date-button {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 16px;\n background: var(--mj-bg-surface-card);\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n cursor: pointer;\n transition: all 0.2s;\n box-shadow: var(--mj-shadow-sm);\n}\n\n.sticky-date-button:hover {\n background: var(--mj-bg-surface-sunken);\n box-shadow: var(--mj-shadow-md);\n}\n\n.sticky-date-button i {\n font-size: 10px;\n color: var(--mj-text-secondary);\n}\n\n/* Date Navigation Dropdown */\n.date-nav-dropdown {\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n margin-top: 8px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-lg);\n min-width: 200px;\n display: none;\n z-index: 101;\n}\n\n.date-nav-dropdown.show {\n display: block;\n}\n\n.date-nav-option {\n padding: 10px 16px;\n cursor: pointer;\n transition: background 0.2s;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.date-nav-option:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.date-nav-option.jump-to {\n font-weight: 600;\n color: var(--mj-text-secondary);\n font-size: 12px;\n cursor: default;\n padding: 8px 16px 4px 16px;\n}\n\n.date-nav-option.jump-to:hover {\n background: none;\n}\n"] }]
|
|
593
|
+
args: [{ standalone: false, selector: 'mj-conversation-message-list', template: "<div class=\"message-list-container\" #scrollContainer>\n <!-- Sticky Date Header (only show for long conversations spanning multiple days) -->\n @if (showDateNavigation && shouldShowDateFilter && messages.length > 0) {\n <div class=\"sticky-date-header\">\n <div class=\"sticky-date-wrapper\">\n <button class=\"sticky-date-button\" (click)=\"toggleDateNav()\">\n <span>{{ currentDateDisplay }}</span>\n <i class=\"fas fa-chevron-down\"></i>\n </button>\n <div class=\"date-nav-dropdown\" [class.show]=\"showDateNav\">\n <div class=\"date-nav-option jump-to\">Jump to...</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('today')\">Today</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('yesterday')\">Yesterday</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('last-week')\">Last week</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('last-month')\">Last month</div>\n </div>\n </div>\n </div>\n }\n\n <ng-container #messageContainer></ng-container>\n\n @if (messages.length === 0 && showEmptyFill) {\n <mj-empty-state\n class=\"messages-empty-fill\"\n Icon=\"fa-solid fa-comments\"\n Title=\"No messages yet. Start a conversation!\" />\n }\n</div>", styles: [".message-list-container {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n background-color: var(--mj-bg-surface-card);\n position: relative;\n}\n\n/* Empty-state fills the message pane vertically */\n.messages-empty-fill {\n height: 100%;\n}\n\n/* Sticky Date Header */\n.sticky-date-header {\n position: sticky;\n top: 12px;\n z-index: 100;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0;\n margin: 0;\n pointer-events: none;\n}\n\n.sticky-date-wrapper {\n pointer-events: auto;\n position: relative;\n}\n\n.sticky-date-button {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 16px;\n background: var(--mj-bg-surface-card);\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n cursor: pointer;\n transition: all 0.2s;\n box-shadow: var(--mj-shadow-sm);\n}\n\n.sticky-date-button:hover {\n background: var(--mj-bg-surface-sunken);\n box-shadow: var(--mj-shadow-md);\n}\n\n.sticky-date-button i {\n font-size: 10px;\n color: var(--mj-text-secondary);\n}\n\n/* Date Navigation Dropdown */\n.date-nav-dropdown {\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n margin-top: 8px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-lg);\n min-width: 200px;\n display: none;\n z-index: 101;\n}\n\n.date-nav-dropdown.show {\n display: block;\n}\n\n.date-nav-option {\n padding: 10px 16px;\n cursor: pointer;\n transition: background 0.2s;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.date-nav-option:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.date-nav-option.jump-to {\n font-weight: 600;\n color: var(--mj-text-secondary);\n font-size: 12px;\n cursor: default;\n padding: 8px 16px 4px 16px;\n}\n\n.date-nav-option.jump-to:hover {\n background: none;\n}\n"] }]
|
|
566
594
|
}], () => [{ type: i0.ChangeDetectorRef }], { messages: [{
|
|
567
595
|
type: Input
|
|
568
596
|
}], conversation: [{
|
|
@@ -571,6 +599,22 @@ export class MessageListComponent extends BaseAngularComponent {
|
|
|
571
599
|
type: Input
|
|
572
600
|
}], isProcessing: [{
|
|
573
601
|
type: Input
|
|
602
|
+
}], showEmptyFill: [{
|
|
603
|
+
type: Input
|
|
604
|
+
}], showDateNavigation: [{
|
|
605
|
+
type: Input
|
|
606
|
+
}], showAgentRunDetails: [{
|
|
607
|
+
type: Input
|
|
608
|
+
}], showReactions: [{
|
|
609
|
+
type: Input
|
|
610
|
+
}], showMessageRating: [{
|
|
611
|
+
type: Input
|
|
612
|
+
}], allowPinning: [{
|
|
613
|
+
type: Input
|
|
614
|
+
}], allowMessageEdit: [{
|
|
615
|
+
type: Input
|
|
616
|
+
}], allowMessageDelete: [{
|
|
617
|
+
type: Input
|
|
574
618
|
}], artifactMap: [{
|
|
575
619
|
type: Input
|
|
576
620
|
}], agentRunMap: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-list.component.js","sourceRoot":"","sources":["../../../../src/lib/components/message/message-list.component.ts","../../../../src/lib/components/message/message-list.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n ViewChild,\n ViewContainerRef,\n OnInit,\n OnDestroy,\n OnChanges,\n SimpleChanges,\n ChangeDetectorRef,\n ElementRef,\n AfterViewInit,\n AfterViewChecked,\n ComponentRef,\n EmbeddedViewRef,\n TemplateRef\n} from '@angular/core';\nimport { MJConversationDetailEntity, MJConversationEntity, RatingJSON } from '@memberjunction/core-entities';\nimport { UserInfo, CompositeKey } from '@memberjunction/core';\nimport { NormalizeUUID } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { MessageItemComponent, MessageAttachment } from './message-item.component';\nimport {\n BeforeResponseFormSubmittedEventArgs,\n AfterResponseFormSubmittedEventArgs,\n} from '../../events/chat-events';\nimport { RealtimeSessionTimelineCardComponent } from '../realtime/realtime-session-timeline-card.component';\nimport { LazyArtifactInfo } from '../../models/lazy-artifact-info';\nimport { selectDistinctLatestArtifacts } from '../../utils/distinct-artifacts';\nimport {\n BuildConversationTimeline,\n ConversationTimelineItem,\n RealtimeSessionTimelineGroup,\n RealtimeSessionTimelineMeta\n} from '../../utils/realtime-session-timeline';\nimport { MJAIAgentRunEntityExtended } from '@memberjunction/ai-core-plus';\n\n/** Context handed to the `messageRenderer` slot template per message. */\ninterface MessageRendererContext {\n $implicit: MJConversationDetailEntity;\n message: MJConversationDetailEntity;\n}\n\n/**\n * One per rendered message in the list. Three paths can produce an entry:\n * - `component` — the default `MessageItemComponent` (single message).\n * - `embedded` — an `EmbeddedViewRef` from the consumer's\n * `messageRenderer` slot template (full per-message\n * replacement, PR 2c slot system).\n * - `realtime-session` — a `RealtimeSessionTimelineCardComponent` collapsing\n * an entire past realtime session into one timeline\n * card (PR #2787 co-agent timeline). The grouping pass\n * (`BuildConversationTimeline`) emits these alongside\n * normal messages — see the timeline utility for the\n * rules.\n *\n * Discriminated by the `kind` tag for ergonomic narrowing.\n */\ntype RenderedMessageEntry =\n | { kind: 'component'; ref: ComponentRef<MessageItemComponent> }\n | { kind: 'embedded'; ref: EmbeddedViewRef<MessageRendererContext> }\n | { kind: 'realtime-session'; ref: ComponentRef<RealtimeSessionTimelineCardComponent> };\n\n/**\n * Container component for displaying a list of messages\n * Uses dynamic component creation (like skip-chat) to avoid Angular binding overhead\n * This dramatically improves performance when messages are added/removed\n */\n@Component({\n standalone: false,\n selector: 'mj-conversation-message-list',\n templateUrl: './message-list.component.html',\n styleUrls: ['./message-list.component.css']\n})\nexport class MessageListComponent extends BaseAngularComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit, AfterViewChecked {\n @Input() public messages: MJConversationDetailEntity[] = [];\n @Input() public conversation!: MJConversationEntity | null;\n @Input() public currentUser!: UserInfo;\n @Input() public isProcessing: boolean = false;\n @Input() public artifactMap: Map<string, LazyArtifactInfo[]> = new Map();\n @Input() public agentRunMap: Map<string, MJAIAgentRunEntityExtended> = new Map();\n @Input() public ratingsMap: Map<string, RatingJSON[]> = new Map();\n @Input() public userAvatarMap: Map<string, {imageUrl: string | null; iconClass: string | null}> = new Map();\n @Input() public attachmentsMap: Map<string, MessageAttachment[]> = new Map();\n /**\n * Optional session-row enrichment for realtime SESSION BLOCKS, keyed by\n * `NormalizeUUID(sessionId)` (agent name / status / close reason). Details stamped\n * with an `AgentSessionID` collapse into one timeline card per session — see\n * `BuildConversationTimeline` — and this map dresses those cards up when present.\n */\n @Input() public sessionMetaMap: Map<string, RealtimeSessionTimelineMeta> = new Map();\n\n /**\n * Optional per-iteration custom message renderer. When set, the list renders each\n * message via this template (full replacement) instead of the default\n * `MessageItemComponent`. Forwarded by chat-area when a consumer projects\n * `mjChatSlot=\"messageRenderer\"`. The template receives the message as\n * `$implicit` and as a named `message` context binding.\n *\n * Consumers opting into a custom renderer take full responsibility for displaying\n * the message — edit/delete/retry affordances, artifacts, ratings, attachments,\n * etc. The minimal `MJChatMessageBubbleDefaultComponent` ships as a ready-to-use\n * bubble renderer.\n */\n @Input() public messageRendererTemplate: TemplateRef<unknown> | null = null;\n\n /**\n * Optional per-message additive decoration template, projected INSIDE the default\n * `MessageItemComponent` (after the message text). Forwarded by chat-area when a\n * consumer projects `mjChatSlot=\"messageExtra\"`. Ignored when\n * `messageRendererTemplate` is set (custom renderers own all per-message content).\n */\n @Input() public messageExtraTemplate: TemplateRef<unknown> | null = null;\n\n @Output() public editMessage = new EventEmitter<MJConversationDetailEntity>();\n @Output() public deleteMessage = new EventEmitter<MJConversationDetailEntity>();\n @Output() public retryMessage = new EventEmitter<MJConversationDetailEntity>();\n @Output() public testFeedbackMessage = new EventEmitter<MJConversationDetailEntity>();\n @Output() public artifactClicked = new EventEmitter<{artifactId: string; versionId?: string}>();\n @Output() public replyInThread = new EventEmitter<MJConversationDetailEntity>();\n @Output() public viewThread = new EventEmitter<MJConversationDetailEntity>();\n @Output() public messageEdited = new EventEmitter<MJConversationDetailEntity>();\n @Output() public openEntityRecord = new EventEmitter<{entityName: string; compositeKey: CompositeKey}>();\n @Output() public suggestedResponseSelected = new EventEmitter<{text: string; customInput?: string}>();\n @Output() public attachmentClicked = new EventEmitter<MessageAttachment>();\n @Output() public diagnosticRequested = new EventEmitter<string>(); // emits messageId\n @Output() public messagePinToggled = new EventEmitter<MJConversationDetailEntity>();\n /** Emitted with the `MJ: AI Agent Sessions.ID` when a realtime session block's Open affordance is clicked. */\n @Output() public realtimeSessionOpenRequested = new EventEmitter<string>();\n\n /** Forwarded from MessageItemComponent — see its docs. */\n @Output() public beforeResponseFormSubmitted = new EventEmitter<BeforeResponseFormSubmittedEventArgs>();\n /** Forwarded from MessageItemComponent — see its docs. */\n @Output() public afterResponseFormSubmitted = new EventEmitter<AfterResponseFormSubmittedEventArgs>();\n\n @ViewChild('messageContainer', { read: ViewContainerRef }) messageContainerRef!: ViewContainerRef;\n @ViewChild('scrollContainer') scrollContainer!: ElementRef;\n\n /**\n * Per-message rendered entries — see `RenderedMessageEntry` for the 3-way\n * union (default MessageItemComponent / consumer messageRenderer slot\n * embedded view / collapsed realtime session timeline card).\n */\n private _renderedMessages = new Map<string, RenderedMessageEntry>();\n private _shouldScrollToBottom = false;\n private _previousMessageCount = 0; // Track previous count to detect new messages\n\n public currentDateDisplay: string = 'Today';\n public showDateNav: boolean = false;\n public shouldShowDateFilter: boolean = false;\n\n constructor(private cdRef: ChangeDetectorRef) {\n super();\n }\n\n public toggleDateNav(): void {\n this.showDateNav = !this.showDateNav;\n }\n\n public jumpToDate(period: string): void {\n // TODO: Implement date jumping logic\n console.log('Jump to date:', period);\n this.showDateNav = false;\n\n // Update display based on period\n switch(period) {\n case 'today':\n this.currentDateDisplay = 'Today';\n break;\n case 'yesterday':\n this.currentDateDisplay = 'Yesterday';\n break;\n case 'last-week':\n this.currentDateDisplay = 'Last week';\n break;\n case 'last-month':\n this.currentDateDisplay = 'Last month';\n break;\n }\n }\n\n // Track whether initial render has happened\n private _initialRenderComplete = false;\n\n ngOnInit() {\n // Initial render will happen in ngAfterViewInit when ViewContainerRef is available\n }\n\n ngAfterViewInit() {\n // ViewContainerRef is now available - perform initial render if we have messages\n if (this.messages && this.messages.length > 0 && this.messageContainerRef && !this._initialRenderComplete) {\n this._initialRenderComplete = true;\n this.updateMessages(this.messages);\n this.updateDateFilterVisibility();\n }\n }\n\n ngOnChanges(changes: SimpleChanges) {\n // React to messages array changes\n // Note: On initial load, messageContainerRef may not be available yet (ngOnChanges runs before ngAfterViewInit)\n // In that case, ngAfterViewInit will handle the initial render\n if (changes['messages'] && this.messages && this.messageContainerRef) {\n this._initialRenderComplete = true;\n this.updateMessages(this.messages);\n this.updateDateFilterVisibility();\n }\n\n // Watch for artifactMap changes to handle newly created artifacts\n // While artifacts are pre-loaded during initial peripheral data load,\n // new artifacts can be created mid-conversation (e.g., by agent runs)\n // This ensures artifact cards appear in messages immediately without requiring a refresh\n if (changes['artifactMap'] && this.messages && this.messageContainerRef) {\n this.updateMessages(this.messages);\n }\n\n // Watch for attachmentsMap changes to handle newly created attachments\n // This ensures media attachments (e.g., images generated by agents) appear\n // immediately without requiring a page refresh\n if (changes['attachmentsMap'] && this.messages && this.messageContainerRef) {\n this.updateMessages(this.messages);\n }\n\n // Watch for session-meta changes so realtime session blocks pick up their\n // agent name / status chip once the (async, batched) session lookup lands\n if (changes['sessionMetaMap'] && this.messages && this.messageContainerRef) {\n this.updateMessages(this.messages);\n }\n }\n\n ngAfterViewChecked() {\n if (this._shouldScrollToBottom) {\n this.scrollToBottom();\n this._shouldScrollToBottom = false;\n }\n }\n\n ngOnDestroy() {\n // Clean up all dynamically created components AND embedded views (both have destroy()).\n this._renderedMessages.forEach((entry) => {\n if (entry) {\n entry.ref.destroy();\n }\n });\n this._renderedMessages.clear();\n\n if (this.messageContainerRef) {\n this.messageContainerRef.clear();\n }\n }\n\n /**\n * Called when messages array changes\n * Efficiently updates the DOM without re-rendering everything\n */\n @Input()\n set messagesUpdate(messages: MJConversationDetailEntity[]) {\n if (messages && this.messageContainerRef) {\n this.updateMessages(messages);\n }\n }\n\n /**\n * Updates the message list using dynamic component creation\n * Only adds/removes changed messages for optimal performance\n *\n * REALTIME SESSIONS: details stamped with an `AgentSessionID` (turns persisted during\n * a live realtime session) do NOT render as normal chat bubbles. The timeline pass\n * (`BuildConversationTimeline`) collapses each session's stamped rows into ONE session\n * block, rendered as a `RealtimeSessionTimelineCardComponent` at the session's\n * chronological position. Everything else renders exactly as before.\n */\n private updateMessages(messages: MJConversationDetailEntity[]): void {\n // Temporarily detach change detection for performance\n this.cdRef.detach();\n\n try {\n // Build the timeline first — collapses contiguous realtime-session rows\n // into ONE session block per session, leaving normal messages in place.\n // See `BuildConversationTimeline` in `utils/realtime-session-timeline`.\n const timeline = BuildConversationTimeline(messages);\n\n // Remove rendered items (messages AND session blocks) that no longer exist\n const currentKeys = new Set(timeline.map(item => this.getTimelineKey(item)));\n this._renderedMessages.forEach((entry, key) => {\n if (!currentKeys.has(key)) {\n entry.ref.destroy();\n this._renderedMessages.delete(key);\n }\n });\n\n // Add or update timeline items in chronological order. `renderMessageItem`\n // internally branches on the slot template (messageRenderer) vs the\n // default MessageItemComponent; `renderSessionBlock` always creates a\n // RealtimeSessionTimelineCardComponent.\n const lastMessageKey = this.findLastMessageKey(timeline);\n for (const item of timeline) {\n if (item.Kind === 'session') {\n this.renderSessionBlock(item.Group);\n } else {\n this.renderMessageItem(item.Detail, messages, this.getMessageKey(item.Detail) === lastMessageKey);\n }\n }\n\n // Only scroll to bottom if new messages were added (not just updates)\n // This prevents scrolling when the message list is merely refreshed (e.g., during agent run timer)\n const previousCount = this._previousMessageCount;\n this._previousMessageCount = messages.length;\n\n if (messages.length > previousCount) {\n this._shouldScrollToBottom = true;\n }\n } finally {\n // Re-attach change detection\n this.cdRef.reattach();\n this.cdRef.detectChanges();\n }\n }\n\n /** Stable render key for a timeline item — message ID, or a prefixed session key for session blocks. */\n private getTimelineKey(item: ConversationTimelineItem<MJConversationDetailEntity>): string {\n return item.Kind === 'session' ? this.getSessionKey(item.Group.SessionID) : this.getMessageKey(item.Detail);\n }\n\n /** Render key for a session block (case-insensitive on the session id; prefixed so it can't collide with message IDs). */\n private getSessionKey(sessionId: string): string {\n return `session:${NormalizeUUID(sessionId)}`;\n }\n\n /** The render key of the LAST normal message item in the timeline (drives `isLastMessage`), or null when none. */\n private findLastMessageKey(timeline: ConversationTimelineItem<MJConversationDetailEntity>[]): string | null {\n for (let i = timeline.length - 1; i >= 0; i--) {\n const item = timeline[i];\n if (item.Kind === 'message') {\n return this.getMessageKey(item.Detail);\n }\n }\n return null;\n }\n\n /**\n * Creates or updates the ONE timeline card a realtime session collapses to.\n * Click/Open on the card bubbles up via {@link realtimeSessionOpenRequested} so the\n * chat area can host the SESSION REVIEW overlay for it.\n */\n private renderSessionBlock(group: RealtimeSessionTimelineGroup): void {\n const key = this.getSessionKey(group.SessionID);\n const meta = this.sessionMetaMap.get(NormalizeUUID(group.SessionID)) ?? null;\n const existing = this._renderedMessages.get(key);\n\n if (existing && existing.kind === 'realtime-session') {\n // Update existing card in place.\n const instance = existing.ref.instance;\n instance.Group = group;\n instance.Meta = meta;\n existing.ref.changeDetectorRef.markForCheck();\n return;\n }\n\n // Different kind at this key (e.g. mode changed) — drop the old entry, fall through to create.\n if (existing) {\n existing.ref.destroy();\n this._renderedMessages.delete(key);\n }\n\n const componentRef = this.messageContainerRef.createComponent(RealtimeSessionTimelineCardComponent);\n componentRef.instance.Group = group;\n componentRef.instance.Meta = meta;\n componentRef.instance.UserName = this.currentUser?.Name || 'You';\n componentRef.instance.OpenRequested.subscribe((sessionId: string) => this.realtimeSessionOpenRequested.emit(sessionId));\n this._renderedMessages.set(key, { kind: 'realtime-session', ref: componentRef });\n }\n\n /**\n * Creates or updates one normal chat message item. Routes to the\n * consumer-projected `messageRenderer` slot template (EmbeddedViewRef path)\n * when present, otherwise falls back to the default `MessageItemComponent`\n * dynamic-component path. Both paths flow through the shared\n * `createRenderedEntry` / `updateMessageItemInstance` helpers.\n */\n private renderMessageItem(message: MJConversationDetailEntity, messages: MJConversationDetailEntity[], isLastMessage: boolean): void {\n const key = this.getMessageKey(message);\n // `index` is only used for the `isLastMessage` heuristic inside\n // updateMessageItemInstance — synthesize a value that produces the right\n // boolean (last index when `isLastMessage` is true, else 0 — any non-last\n // index works since it just affects that one comparison).\n const index = isLastMessage ? messages.length - 1 : 0;\n const useCustomRenderer = this.messageRendererTemplate !== null;\n const existing = this._renderedMessages.get(key);\n\n if (existing && existing.kind === 'embedded' && useCustomRenderer) {\n // Update existing embedded view from messageRenderer slot\n existing.ref.context.$implicit = message;\n existing.ref.context.message = message;\n existing.ref.markForCheck();\n return;\n }\n if (existing && existing.kind === 'component' && !useCustomRenderer) {\n // Update existing MessageItemComponent in place\n this.updateMessageItemInstance(existing.ref, message, messages, index);\n return;\n }\n if (existing) {\n // Rendering kind changed (e.g. component↔embedded, or a session block was\n // overwritten with a message). Destroy + recreate.\n existing.ref.destroy();\n this._renderedMessages.delete(key);\n }\n\n this.createRenderedEntry(message, messages, index, key, useCustomRenderer);\n }\n\n /**\n * Updates an existing `MessageItemComponent` in place — used on the default\n * (non-custom-renderer) path when a message's status / artifacts / agent-run /\n * etc. changes mid-stream.\n */\n private updateMessageItemInstance(\n ref: ComponentRef<MessageItemComponent>,\n message: MJConversationDetailEntity,\n messages: MJConversationDetailEntity[],\n index: number\n ): void {\n const instance = ref.instance;\n const previousMessage = instance.message;\n\n instance.message = message;\n instance.allMessages = messages;\n instance.isProcessing = this.isProcessing;\n instance.userAvatarMap = this.userAvatarMap;\n instance.isLastMessage = (index === messages.length - 1);\n instance.messageExtraTemplate = this.messageExtraTemplate;\n\n this.applyArtifactsToInstance(instance, message.ID, ref.changeDetectorRef);\n\n instance.agentRun = this.agentRunMap.get(message.ID) || null;\n instance.ratings = this.ratingsMap.get(message.ID);\n instance.attachments = this.attachmentsMap.get(message.ID) || [];\n\n // Status change requires explicit markForCheck on the OnPush dynamic child.\n if (previousMessage && previousMessage.Status !== message.Status) {\n ref.changeDetectorRef.markForCheck();\n }\n }\n\n /**\n * Creates a new rendered entry — either a `MessageItemComponent` (default path)\n * or an `EmbeddedViewRef` from the consumer's `messageRenderer` slot template.\n * Stores the entry in `_renderedMessages` and stamps a back-reference on the\n * message entity.\n */\n private createRenderedEntry(\n message: MJConversationDetailEntity,\n messages: MJConversationDetailEntity[],\n index: number,\n key: string,\n useCustomRenderer: boolean\n ): void {\n if (useCustomRenderer && this.messageRendererTemplate) {\n // The slot directive carries TemplateRef<unknown>; assert the contract here\n // (consumers' `let-message` bindings consume the message context shape below).\n const template = this.messageRendererTemplate as TemplateRef<MessageRendererContext>;\n const viewRef = this.messageContainerRef.createEmbeddedView<MessageRendererContext>(\n template,\n { $implicit: message, message }\n );\n this._renderedMessages.set(key, { kind: 'embedded', ref: viewRef });\n // Stamp back-ref for parity with the component path.\n (message as unknown as { _viewRef?: EmbeddedViewRef<MessageRendererContext> })._viewRef = viewRef;\n return;\n }\n\n const componentRef = this.messageContainerRef.createComponent(MessageItemComponent);\n const instance = componentRef.instance;\n\n instance.message = message;\n instance.conversation = this.conversation;\n instance.currentUser = this.currentUser;\n instance.allMessages = messages;\n instance.isProcessing = this.isProcessing;\n instance.userAvatarMap = this.userAvatarMap;\n instance.isLastMessage = (index === messages.length - 1);\n instance.messageExtraTemplate = this.messageExtraTemplate;\n\n this.applyArtifactsToInstance(instance, message.ID, componentRef.changeDetectorRef);\n\n instance.agentRun = this.agentRunMap.get(message.ID) || null;\n instance.ratings = this.ratingsMap.get(message.ID);\n instance.attachments = this.attachmentsMap.get(message.ID) || [];\n\n instance.editClicked.subscribe((msg: MJConversationDetailEntity) => this.editMessage.emit(msg));\n instance.deleteClicked.subscribe((msg: MJConversationDetailEntity) => this.deleteMessage.emit(msg));\n instance.retryClicked.subscribe((msg: MJConversationDetailEntity) => this.retryMessage.emit(msg));\n instance.testFeedbackClicked.subscribe((msg: MJConversationDetailEntity) => this.testFeedbackMessage.emit(msg));\n instance.artifactClicked.subscribe((data: {artifactId: string; versionId?: string}) => this.artifactClicked.emit(data));\n instance.messageEdited.subscribe((msg: MJConversationDetailEntity) => this.messageEdited.emit(msg));\n instance.openEntityRecord.subscribe((data: {entityName: string; compositeKey: CompositeKey}) => this.openEntityRecord.emit(data));\n instance.suggestedResponseSelected.subscribe((data: {text: string; customInput?: string}) => this.suggestedResponseSelected.emit(data));\n instance.attachmentClicked.subscribe((attachment: MessageAttachment) => this.attachmentClicked.emit(attachment));\n instance.diagnosticRequested.subscribe((messageId: string) => this.diagnosticRequested.emit(messageId));\n instance.messagePinToggled.subscribe((msg: MJConversationDetailEntity) => this.messagePinToggled.emit(msg));\n instance.beforeResponseFormSubmitted.subscribe((e: BeforeResponseFormSubmittedEventArgs) => this.beforeResponseFormSubmitted.emit(e));\n instance.afterResponseFormSubmitted.subscribe((e: AfterResponseFormSubmittedEventArgs) => this.afterResponseFormSubmitted.emit(e));\n\n if (instance.artifactActionPerformed) {\n instance.artifactActionPerformed.subscribe((data: {action: string; artifactId: string}) => {\n // Parent can handle artifact actions (save, fork, history, share)\n console.log('Artifact action:', data);\n });\n }\n\n this._renderedMessages.set(key, { kind: 'component', ref: componentRef });\n // Preserve the existing back-ref pattern from the skip-chat performance design.\n (message as unknown as { _componentRef?: ComponentRef<MessageItemComponent> })._componentRef = componentRef;\n }\n\n /**\n * Resolves the DISTINCT artifacts for a message (one entry per artifactId at its\n * latest version), lazy-loads them all, and applies them to the rendered\n * message-item. Loads in the background so the UI never blocks.\n *\n * WHY WE SURFACE THEM ALL (design rationale — see PR discussion w/ Pranav & Ethan):\n * A single message can legitimately carry more than one DISTINCT artifact — e.g. a\n * research report PLUS a *standalone* generated infographic. This is deliberately\n * NOT in conflict with the server-side consolidation in AgentRunner\n * (Pranav, 5664b86: \"keep the report's embedded image in the report, not as a\n * duplicate artifact\"): that logic only suppresses media that is *embedded inline*\n * (base64) in another artifact's payload — a true duplicate. Genuinely standalone\n * sibling artifacts (the report uses SVG charts; the infographic is a separate JPEG)\n * are correctly kept as separate artifacts, and the UI must show every one of them.\n *\n * The earlier `artifactList[length - 1]` (\"show only the most recent\") approach\n * (EL-BC, 95492622) assumed consolidation always left exactly one artifact per\n * message; when it legitimately leaves two, that silently hid the report behind the\n * image. Grouping by artifactId (latest version each) shows all distinct artifacts\n * while still collapsing multiple *versions* of the same artifact to one card.\n */\n private applyArtifactsToInstance(\n instance: MessageItemComponent,\n messageId: string,\n childCdRef: ChangeDetectorRef\n ): void {\n const infos = this.resolveDistinctArtifacts(messageId);\n if (infos.length === 0) {\n instance.artifacts = [];\n instance.artifact = undefined;\n instance.artifactVersion = undefined;\n return;\n }\n\n Promise.all(\n infos.map(info =>\n Promise.all([info.getArtifact(), info.getVersion()]).then(([artifact, version]) => ({ artifact, version }))\n )\n )\n .then(refs => {\n instance.artifacts = refs;\n // Keep the legacy single inputs pointed at the first entry for back-compat.\n instance.artifact = refs[0]?.artifact;\n instance.artifactVersion = refs[0]?.version;\n // zone.js 0.15: parent detectChanges doesn't propagate to dynamically created children\n childCdRef.detectChanges();\n this.cdRef.detectChanges();\n })\n .catch(err => {\n console.error('Failed to lazy-load artifacts:', err);\n });\n }\n\n /**\n * Groups a message's artifact list by artifactId, keeping the highest version of\n * each. Multiple versions of the SAME artifact collapse to one card (latest wins),\n * while genuinely distinct artifacts are all retained.\n */\n private resolveDistinctArtifacts(messageId: string): LazyArtifactInfo[] {\n const list = this.artifactMap.get(messageId);\n if (!list || list.length === 0) {\n return [];\n }\n return selectDistinctLatestArtifacts(list);\n }\n\n /**\n * Generates a unique key for a message\n * Uses ID if available, otherwise uses a temporary key\n */\n private getMessageKey(message: MJConversationDetailEntity): string {\n return message.ID && message.ID.length > 0\n ? message.ID\n : `temp_${message.__mj_CreatedAt?.getTime() || Date.now()}`;\n }\n\n /**\n * Determines whether to show the date filter dropdown\n * Only show if conversation is long and spans multiple days\n */\n private updateDateFilterVisibility(): void {\n if (!this.messages || this.messages.length < 20) {\n this.shouldShowDateFilter = false;\n return;\n }\n\n // Check if messages span more than 2 days\n const dates = this.messages\n .map(m => m.__mj_CreatedAt)\n .filter(d => d != null)\n .map(d => new Date(d!).setHours(0, 0, 0, 0));\n\n if (dates.length === 0) {\n this.shouldShowDateFilter = false;\n return;\n }\n\n const uniqueDates = new Set(dates);\n const daySpan = uniqueDates.size;\n\n // Show filter if conversation has 20+ messages and spans 3+ days\n this.shouldShowDateFilter = daySpan >= 3;\n }\n\n /**\n * Scrolls the message list to the bottom\n */\n private scrollToBottom(): void {\n if (this.scrollContainer && this.scrollContainer.nativeElement) {\n Promise.resolve().then(() => {\n const element = this.scrollContainer.nativeElement;\n element.scrollTop = element.scrollHeight;\n });\n }\n }\n\n /**\n * Removes a message from the rendered list\n * Called externally when a message is deleted\n */\n public removeMessage(message: MJConversationDetailEntity): void {\n const key = this.getMessageKey(message);\n const entry = this._renderedMessages.get(key);\n if (entry) {\n entry.ref.destroy();\n this._renderedMessages.delete(key);\n }\n }\n}","<div class=\"message-list-container\" #scrollContainer>\n <!-- Sticky Date Header (only show for long conversations spanning multiple days) -->\n @if (shouldShowDateFilter && messages.length > 0) {\n <div class=\"sticky-date-header\">\n <div class=\"sticky-date-wrapper\">\n <button class=\"sticky-date-button\" (click)=\"toggleDateNav()\">\n <span>{{ currentDateDisplay }}</span>\n <i class=\"fas fa-chevron-down\"></i>\n </button>\n <div class=\"date-nav-dropdown\" [class.show]=\"showDateNav\">\n <div class=\"date-nav-option jump-to\">Jump to...</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('today')\">Today</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('yesterday')\">Yesterday</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('last-week')\">Last week</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('last-month')\">Last month</div>\n </div>\n </div>\n </div>\n }\n\n <ng-container #messageContainer></ng-container>\n\n @if (messages.length === 0) {\n <mj-empty-state\n class=\"messages-empty-fill\"\n Icon=\"fa-solid fa-comments\"\n Title=\"No messages yet. Start a conversation!\" />\n }\n</div>"]}
|
|
1
|
+
{"version":3,"file":"message-list.component.js","sourceRoot":"","sources":["../../../../src/lib/components/message/message-list.component.ts","../../../../src/lib/components/message/message-list.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n ViewChild,\n ViewContainerRef,\n OnInit,\n OnDestroy,\n OnChanges,\n SimpleChanges,\n ChangeDetectorRef,\n ElementRef,\n AfterViewInit,\n AfterViewChecked,\n ComponentRef,\n EmbeddedViewRef,\n TemplateRef\n} from '@angular/core';\nimport { MJConversationDetailEntity, MJConversationEntity, RatingJSON } from '@memberjunction/core-entities';\nimport { UserInfo, CompositeKey } from '@memberjunction/core';\nimport { NormalizeUUID } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { MessageItemComponent, MessageAttachment } from './message-item.component';\nimport {\n BeforeResponseFormSubmittedEventArgs,\n AfterResponseFormSubmittedEventArgs,\n} from '../../events/chat-events';\nimport { RealtimeSessionTimelineCardComponent } from '../realtime/realtime-session-timeline-card.component';\nimport { LazyArtifactInfo } from '../../models/lazy-artifact-info';\nimport { selectDistinctLatestArtifacts } from '../../utils/distinct-artifacts';\nimport {\n BuildConversationTimeline,\n ConversationTimelineItem,\n RealtimeSessionTimelineGroup,\n RealtimeSessionTimelineMeta\n} from '../../utils/realtime-session-timeline';\nimport { MJAIAgentRunEntityExtended } from '@memberjunction/ai-core-plus';\n\n/** Context handed to the `messageRenderer` slot template per message. */\ninterface MessageRendererContext {\n $implicit: MJConversationDetailEntity;\n message: MJConversationDetailEntity;\n}\n\n/**\n * One per rendered message in the list. Three paths can produce an entry:\n * - `component` — the default `MessageItemComponent` (single message).\n * - `embedded` — an `EmbeddedViewRef` from the consumer's\n * `messageRenderer` slot template (full per-message\n * replacement, PR 2c slot system).\n * - `realtime-session` — a `RealtimeSessionTimelineCardComponent` collapsing\n * an entire past realtime session into one timeline\n * card (PR #2787 co-agent timeline). The grouping pass\n * (`BuildConversationTimeline`) emits these alongside\n * normal messages — see the timeline utility for the\n * rules.\n *\n * Discriminated by the `kind` tag for ergonomic narrowing.\n */\ntype RenderedMessageEntry =\n | { kind: 'component'; ref: ComponentRef<MessageItemComponent> }\n | { kind: 'embedded'; ref: EmbeddedViewRef<MessageRendererContext> }\n | { kind: 'realtime-session'; ref: ComponentRef<RealtimeSessionTimelineCardComponent> };\n\n/**\n * Container component for displaying a list of messages\n * Uses dynamic component creation (like skip-chat) to avoid Angular binding overhead\n * This dramatically improves performance when messages are added/removed\n */\n@Component({\n standalone: false,\n selector: 'mj-conversation-message-list',\n templateUrl: './message-list.component.html',\n styleUrls: ['./message-list.component.css']\n})\nexport class MessageListComponent extends BaseAngularComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit, AfterViewChecked {\n @Input() public messages: MJConversationDetailEntity[] = [];\n @Input() public conversation!: MJConversationEntity | null;\n @Input() public currentUser!: UserInfo;\n @Input() public isProcessing: boolean = false;\n /** Whether the built-in \"No messages yet\" filler renders for empty conversations. Hosts with their own empty-state chrome set false. */\n @Input() public showEmptyFill: boolean = true;\n /** Whether the sticky date header + jump-to-date dropdown render. */\n @Input() public showDateNavigation: boolean = true;\n // Per-message feature gates — forwarded onto each MessageItemComponent instance\n // (see applyMessageItemFeatureFlags). All default true.\n @Input() public showAgentRunDetails: boolean = true;\n @Input() public showReactions: boolean = true;\n @Input() public showMessageRating: boolean = true;\n @Input() public allowPinning: boolean = true;\n @Input() public allowMessageEdit: boolean = true;\n @Input() public allowMessageDelete: boolean = true;\n @Input() public artifactMap: Map<string, LazyArtifactInfo[]> = new Map();\n @Input() public agentRunMap: Map<string, MJAIAgentRunEntityExtended> = new Map();\n @Input() public ratingsMap: Map<string, RatingJSON[]> = new Map();\n @Input() public userAvatarMap: Map<string, {imageUrl: string | null; iconClass: string | null}> = new Map();\n @Input() public attachmentsMap: Map<string, MessageAttachment[]> = new Map();\n /**\n * Optional session-row enrichment for realtime SESSION BLOCKS, keyed by\n * `NormalizeUUID(sessionId)` (agent name / status / close reason). Details stamped\n * with an `AgentSessionID` collapse into one timeline card per session — see\n * `BuildConversationTimeline` — and this map dresses those cards up when present.\n */\n @Input() public sessionMetaMap: Map<string, RealtimeSessionTimelineMeta> = new Map();\n\n /**\n * Optional per-iteration custom message renderer. When set, the list renders each\n * message via this template (full replacement) instead of the default\n * `MessageItemComponent`. Forwarded by chat-area when a consumer projects\n * `mjChatSlot=\"messageRenderer\"`. The template receives the message as\n * `$implicit` and as a named `message` context binding.\n *\n * Consumers opting into a custom renderer take full responsibility for displaying\n * the message — edit/delete/retry affordances, artifacts, ratings, attachments,\n * etc. The minimal `MJChatMessageBubbleDefaultComponent` ships as a ready-to-use\n * bubble renderer.\n */\n @Input() public messageRendererTemplate: TemplateRef<unknown> | null = null;\n\n /**\n * Optional per-message additive decoration template, projected INSIDE the default\n * `MessageItemComponent` (after the message text). Forwarded by chat-area when a\n * consumer projects `mjChatSlot=\"messageExtra\"`. Ignored when\n * `messageRendererTemplate` is set (custom renderers own all per-message content).\n */\n @Input() public messageExtraTemplate: TemplateRef<unknown> | null = null;\n\n @Output() public editMessage = new EventEmitter<MJConversationDetailEntity>();\n @Output() public deleteMessage = new EventEmitter<MJConversationDetailEntity>();\n @Output() public retryMessage = new EventEmitter<MJConversationDetailEntity>();\n @Output() public testFeedbackMessage = new EventEmitter<MJConversationDetailEntity>();\n @Output() public artifactClicked = new EventEmitter<{artifactId: string; versionId?: string}>();\n @Output() public replyInThread = new EventEmitter<MJConversationDetailEntity>();\n @Output() public viewThread = new EventEmitter<MJConversationDetailEntity>();\n @Output() public messageEdited = new EventEmitter<MJConversationDetailEntity>();\n @Output() public openEntityRecord = new EventEmitter<{entityName: string; compositeKey: CompositeKey}>();\n @Output() public suggestedResponseSelected = new EventEmitter<{text: string; customInput?: string}>();\n @Output() public attachmentClicked = new EventEmitter<MessageAttachment>();\n @Output() public diagnosticRequested = new EventEmitter<string>(); // emits messageId\n @Output() public messagePinToggled = new EventEmitter<MJConversationDetailEntity>();\n /** Emitted with the `MJ: AI Agent Sessions.ID` when a realtime session block's Open affordance is clicked. */\n @Output() public realtimeSessionOpenRequested = new EventEmitter<string>();\n\n /** Forwarded from MessageItemComponent — see its docs. */\n @Output() public beforeResponseFormSubmitted = new EventEmitter<BeforeResponseFormSubmittedEventArgs>();\n /** Forwarded from MessageItemComponent — see its docs. */\n @Output() public afterResponseFormSubmitted = new EventEmitter<AfterResponseFormSubmittedEventArgs>();\n\n @ViewChild('messageContainer', { read: ViewContainerRef }) messageContainerRef!: ViewContainerRef;\n @ViewChild('scrollContainer') scrollContainer!: ElementRef;\n\n /**\n * Per-message rendered entries — see `RenderedMessageEntry` for the 3-way\n * union (default MessageItemComponent / consumer messageRenderer slot\n * embedded view / collapsed realtime session timeline card).\n */\n private _renderedMessages = new Map<string, RenderedMessageEntry>();\n private _shouldScrollToBottom = false;\n private _previousMessageCount = 0; // Track previous count to detect new messages\n\n public currentDateDisplay: string = 'Today';\n public showDateNav: boolean = false;\n public shouldShowDateFilter: boolean = false;\n\n constructor(private cdRef: ChangeDetectorRef) {\n super();\n }\n\n public toggleDateNav(): void {\n this.showDateNav = !this.showDateNav;\n }\n\n public jumpToDate(period: string): void {\n // TODO: Implement date jumping logic\n console.log('Jump to date:', period);\n this.showDateNav = false;\n\n // Update display based on period\n switch(period) {\n case 'today':\n this.currentDateDisplay = 'Today';\n break;\n case 'yesterday':\n this.currentDateDisplay = 'Yesterday';\n break;\n case 'last-week':\n this.currentDateDisplay = 'Last week';\n break;\n case 'last-month':\n this.currentDateDisplay = 'Last month';\n break;\n }\n }\n\n // Track whether initial render has happened\n private _initialRenderComplete = false;\n\n ngOnInit() {\n // Initial render will happen in ngAfterViewInit when ViewContainerRef is available\n }\n\n ngAfterViewInit() {\n // ViewContainerRef is now available - perform initial render if we have messages\n if (this.messages && this.messages.length > 0 && this.messageContainerRef && !this._initialRenderComplete) {\n this._initialRenderComplete = true;\n this.updateMessages(this.messages);\n this.updateDateFilterVisibility();\n }\n }\n\n ngOnChanges(changes: SimpleChanges) {\n // React to messages array changes\n // Note: On initial load, messageContainerRef may not be available yet (ngOnChanges runs before ngAfterViewInit)\n // In that case, ngAfterViewInit will handle the initial render\n if (changes['messages'] && this.messages && this.messageContainerRef) {\n this._initialRenderComplete = true;\n this.updateMessages(this.messages);\n this.updateDateFilterVisibility();\n }\n\n // Watch for artifactMap changes to handle newly created artifacts\n // While artifacts are pre-loaded during initial peripheral data load,\n // new artifacts can be created mid-conversation (e.g., by agent runs)\n // This ensures artifact cards appear in messages immediately without requiring a refresh\n if (changes['artifactMap'] && this.messages && this.messageContainerRef) {\n this.updateMessages(this.messages);\n }\n\n // Watch for attachmentsMap changes to handle newly created attachments\n // This ensures media attachments (e.g., images generated by agents) appear\n // immediately without requiring a page refresh\n if (changes['attachmentsMap'] && this.messages && this.messageContainerRef) {\n this.updateMessages(this.messages);\n }\n\n // Watch for session-meta changes so realtime session blocks pick up their\n // agent name / status chip once the (async, batched) session lookup lands\n if (changes['sessionMetaMap'] && this.messages && this.messageContainerRef) {\n this.updateMessages(this.messages);\n }\n }\n\n ngAfterViewChecked() {\n if (this._shouldScrollToBottom) {\n this.scrollToBottom();\n this._shouldScrollToBottom = false;\n }\n }\n\n ngOnDestroy() {\n // Clean up all dynamically created components AND embedded views (both have destroy()).\n this._renderedMessages.forEach((entry) => {\n if (entry) {\n entry.ref.destroy();\n }\n });\n this._renderedMessages.clear();\n\n if (this.messageContainerRef) {\n this.messageContainerRef.clear();\n }\n }\n\n /**\n * Called when messages array changes\n * Efficiently updates the DOM without re-rendering everything\n */\n @Input()\n set messagesUpdate(messages: MJConversationDetailEntity[]) {\n if (messages && this.messageContainerRef) {\n this.updateMessages(messages);\n }\n }\n\n /**\n * Updates the message list using dynamic component creation\n * Only adds/removes changed messages for optimal performance\n *\n * REALTIME SESSIONS: details stamped with an `AgentSessionID` (turns persisted during\n * a live realtime session) do NOT render as normal chat bubbles. The timeline pass\n * (`BuildConversationTimeline`) collapses each session's stamped rows into ONE session\n * block, rendered as a `RealtimeSessionTimelineCardComponent` at the session's\n * chronological position. Everything else renders exactly as before.\n */\n private updateMessages(messages: MJConversationDetailEntity[]): void {\n // Temporarily detach change detection for performance\n this.cdRef.detach();\n\n try {\n // Build the timeline first — collapses contiguous realtime-session rows\n // into ONE session block per session, leaving normal messages in place.\n // See `BuildConversationTimeline` in `utils/realtime-session-timeline`.\n const timeline = BuildConversationTimeline(messages);\n\n // Remove rendered items (messages AND session blocks) that no longer exist\n const currentKeys = new Set(timeline.map(item => this.getTimelineKey(item)));\n this._renderedMessages.forEach((entry, key) => {\n if (!currentKeys.has(key)) {\n entry.ref.destroy();\n this._renderedMessages.delete(key);\n }\n });\n\n // Add or update timeline items in chronological order. `renderMessageItem`\n // internally branches on the slot template (messageRenderer) vs the\n // default MessageItemComponent; `renderSessionBlock` always creates a\n // RealtimeSessionTimelineCardComponent.\n const lastMessageKey = this.findLastMessageKey(timeline);\n for (const item of timeline) {\n if (item.Kind === 'session') {\n this.renderSessionBlock(item.Group);\n } else {\n this.renderMessageItem(item.Detail, messages, this.getMessageKey(item.Detail) === lastMessageKey);\n }\n }\n\n // Only scroll to bottom if new messages were added (not just updates)\n // This prevents scrolling when the message list is merely refreshed (e.g., during agent run timer)\n const previousCount = this._previousMessageCount;\n this._previousMessageCount = messages.length;\n\n if (messages.length > previousCount) {\n this._shouldScrollToBottom = true;\n }\n } finally {\n // Re-attach change detection\n this.cdRef.reattach();\n this.cdRef.detectChanges();\n }\n }\n\n /** Stable render key for a timeline item — message ID, or a prefixed session key for session blocks. */\n private getTimelineKey(item: ConversationTimelineItem<MJConversationDetailEntity>): string {\n return item.Kind === 'session' ? this.getSessionKey(item.Group.SessionID) : this.getMessageKey(item.Detail);\n }\n\n /** Render key for a session block (case-insensitive on the session id; prefixed so it can't collide with message IDs). */\n private getSessionKey(sessionId: string): string {\n return `session:${NormalizeUUID(sessionId)}`;\n }\n\n /** The render key of the LAST normal message item in the timeline (drives `isLastMessage`), or null when none. */\n private findLastMessageKey(timeline: ConversationTimelineItem<MJConversationDetailEntity>[]): string | null {\n for (let i = timeline.length - 1; i >= 0; i--) {\n const item = timeline[i];\n if (item.Kind === 'message') {\n return this.getMessageKey(item.Detail);\n }\n }\n return null;\n }\n\n /**\n * Creates or updates the ONE timeline card a realtime session collapses to.\n * Click/Open on the card bubbles up via {@link realtimeSessionOpenRequested} so the\n * chat area can host the SESSION REVIEW overlay for it.\n */\n private renderSessionBlock(group: RealtimeSessionTimelineGroup): void {\n const key = this.getSessionKey(group.SessionID);\n const meta = this.sessionMetaMap.get(NormalizeUUID(group.SessionID)) ?? null;\n const existing = this._renderedMessages.get(key);\n\n if (existing && existing.kind === 'realtime-session') {\n // Update existing card in place.\n const instance = existing.ref.instance;\n instance.Group = group;\n instance.Meta = meta;\n existing.ref.changeDetectorRef.markForCheck();\n return;\n }\n\n // Different kind at this key (e.g. mode changed) — drop the old entry, fall through to create.\n if (existing) {\n existing.ref.destroy();\n this._renderedMessages.delete(key);\n }\n\n const componentRef = this.messageContainerRef.createComponent(RealtimeSessionTimelineCardComponent);\n componentRef.instance.Group = group;\n componentRef.instance.Meta = meta;\n componentRef.instance.UserName = this.currentUser?.Name || 'You';\n componentRef.instance.OpenRequested.subscribe((sessionId: string) => this.realtimeSessionOpenRequested.emit(sessionId));\n this._renderedMessages.set(key, { kind: 'realtime-session', ref: componentRef });\n }\n\n /**\n * Creates or updates one normal chat message item. Routes to the\n * consumer-projected `messageRenderer` slot template (EmbeddedViewRef path)\n * when present, otherwise falls back to the default `MessageItemComponent`\n * dynamic-component path. Both paths flow through the shared\n * `createRenderedEntry` / `updateMessageItemInstance` helpers.\n */\n private renderMessageItem(message: MJConversationDetailEntity, messages: MJConversationDetailEntity[], isLastMessage: boolean): void {\n const key = this.getMessageKey(message);\n // `index` is only used for the `isLastMessage` heuristic inside\n // updateMessageItemInstance — synthesize a value that produces the right\n // boolean (last index when `isLastMessage` is true, else 0 — any non-last\n // index works since it just affects that one comparison).\n const index = isLastMessage ? messages.length - 1 : 0;\n const useCustomRenderer = this.messageRendererTemplate !== null;\n const existing = this._renderedMessages.get(key);\n\n if (existing && existing.kind === 'embedded' && useCustomRenderer) {\n // Update existing embedded view from messageRenderer slot\n existing.ref.context.$implicit = message;\n existing.ref.context.message = message;\n existing.ref.markForCheck();\n return;\n }\n if (existing && existing.kind === 'component' && !useCustomRenderer) {\n // Update existing MessageItemComponent in place\n this.updateMessageItemInstance(existing.ref, message, messages, index);\n return;\n }\n if (existing) {\n // Rendering kind changed (e.g. component↔embedded, or a session block was\n // overwritten with a message). Destroy + recreate.\n existing.ref.destroy();\n this._renderedMessages.delete(key);\n }\n\n this.createRenderedEntry(message, messages, index, key, useCustomRenderer);\n }\n\n /**\n * Updates an existing `MessageItemComponent` in place — used on the default\n * (non-custom-renderer) path when a message's status / artifacts / agent-run /\n * etc. changes mid-stream.\n */\n private updateMessageItemInstance(\n ref: ComponentRef<MessageItemComponent>,\n message: MJConversationDetailEntity,\n messages: MJConversationDetailEntity[],\n index: number\n ): void {\n const instance = ref.instance;\n const previousMessage = instance.message;\n\n instance.message = message;\n instance.allMessages = messages;\n instance.isProcessing = this.isProcessing;\n instance.userAvatarMap = this.userAvatarMap;\n instance.isLastMessage = (index === messages.length - 1);\n instance.messageExtraTemplate = this.messageExtraTemplate;\n this.applyMessageItemFeatureFlags(instance);\n\n this.applyArtifactsToInstance(instance, message.ID, ref.changeDetectorRef);\n\n instance.agentRun = this.agentRunMap.get(message.ID) || null;\n instance.ratings = this.ratingsMap.get(message.ID);\n instance.attachments = this.attachmentsMap.get(message.ID) || [];\n\n // Status change requires explicit markForCheck on the OnPush dynamic child.\n if (previousMessage && previousMessage.Status !== message.Status) {\n ref.changeDetectorRef.markForCheck();\n }\n }\n\n /**\n * Forward the host-level per-message feature gates onto a MessageItemComponent\n * instance. These are static host config (not per-message data), so the same\n * values apply to every message; applied on both the create and in-place-update\n * paths so a mid-session rebind stays consistent.\n */\n private applyMessageItemFeatureFlags(instance: MessageItemComponent): void {\n instance.showAgentRunDetails = this.showAgentRunDetails;\n instance.showReactions = this.showReactions;\n instance.showMessageRating = this.showMessageRating;\n instance.allowPinning = this.allowPinning;\n instance.allowMessageEdit = this.allowMessageEdit;\n instance.allowMessageDelete = this.allowMessageDelete;\n }\n\n /**\n * Creates a new rendered entry — either a `MessageItemComponent` (default path)\n * or an `EmbeddedViewRef` from the consumer's `messageRenderer` slot template.\n * Stores the entry in `_renderedMessages` and stamps a back-reference on the\n * message entity.\n */\n private createRenderedEntry(\n message: MJConversationDetailEntity,\n messages: MJConversationDetailEntity[],\n index: number,\n key: string,\n useCustomRenderer: boolean\n ): void {\n if (useCustomRenderer && this.messageRendererTemplate) {\n // The slot directive carries TemplateRef<unknown>; assert the contract here\n // (consumers' `let-message` bindings consume the message context shape below).\n const template = this.messageRendererTemplate as TemplateRef<MessageRendererContext>;\n const viewRef = this.messageContainerRef.createEmbeddedView<MessageRendererContext>(\n template,\n { $implicit: message, message }\n );\n this._renderedMessages.set(key, { kind: 'embedded', ref: viewRef });\n // Stamp back-ref for parity with the component path.\n (message as unknown as { _viewRef?: EmbeddedViewRef<MessageRendererContext> })._viewRef = viewRef;\n return;\n }\n\n const componentRef = this.messageContainerRef.createComponent(MessageItemComponent);\n const instance = componentRef.instance;\n\n instance.message = message;\n instance.conversation = this.conversation;\n instance.currentUser = this.currentUser;\n instance.allMessages = messages;\n instance.isProcessing = this.isProcessing;\n instance.userAvatarMap = this.userAvatarMap;\n instance.isLastMessage = (index === messages.length - 1);\n instance.messageExtraTemplate = this.messageExtraTemplate;\n this.applyMessageItemFeatureFlags(instance);\n\n this.applyArtifactsToInstance(instance, message.ID, componentRef.changeDetectorRef);\n\n instance.agentRun = this.agentRunMap.get(message.ID) || null;\n instance.ratings = this.ratingsMap.get(message.ID);\n instance.attachments = this.attachmentsMap.get(message.ID) || [];\n\n instance.editClicked.subscribe((msg: MJConversationDetailEntity) => this.editMessage.emit(msg));\n instance.deleteClicked.subscribe((msg: MJConversationDetailEntity) => this.deleteMessage.emit(msg));\n instance.retryClicked.subscribe((msg: MJConversationDetailEntity) => this.retryMessage.emit(msg));\n instance.testFeedbackClicked.subscribe((msg: MJConversationDetailEntity) => this.testFeedbackMessage.emit(msg));\n instance.artifactClicked.subscribe((data: {artifactId: string; versionId?: string}) => this.artifactClicked.emit(data));\n instance.messageEdited.subscribe((msg: MJConversationDetailEntity) => this.messageEdited.emit(msg));\n instance.openEntityRecord.subscribe((data: {entityName: string; compositeKey: CompositeKey}) => this.openEntityRecord.emit(data));\n instance.suggestedResponseSelected.subscribe((data: {text: string; customInput?: string}) => this.suggestedResponseSelected.emit(data));\n instance.attachmentClicked.subscribe((attachment: MessageAttachment) => this.attachmentClicked.emit(attachment));\n instance.diagnosticRequested.subscribe((messageId: string) => this.diagnosticRequested.emit(messageId));\n instance.messagePinToggled.subscribe((msg: MJConversationDetailEntity) => this.messagePinToggled.emit(msg));\n instance.beforeResponseFormSubmitted.subscribe((e: BeforeResponseFormSubmittedEventArgs) => this.beforeResponseFormSubmitted.emit(e));\n instance.afterResponseFormSubmitted.subscribe((e: AfterResponseFormSubmittedEventArgs) => this.afterResponseFormSubmitted.emit(e));\n\n if (instance.artifactActionPerformed) {\n instance.artifactActionPerformed.subscribe((data: {action: string; artifactId: string}) => {\n // Parent can handle artifact actions (save, fork, history, share)\n console.log('Artifact action:', data);\n });\n }\n\n this._renderedMessages.set(key, { kind: 'component', ref: componentRef });\n // Preserve the existing back-ref pattern from the skip-chat performance design.\n (message as unknown as { _componentRef?: ComponentRef<MessageItemComponent> })._componentRef = componentRef;\n }\n\n /**\n * Resolves the DISTINCT artifacts for a message (one entry per artifactId at its\n * latest version), lazy-loads them all, and applies them to the rendered\n * message-item. Loads in the background so the UI never blocks.\n *\n * WHY WE SURFACE THEM ALL (design rationale — see PR discussion w/ Pranav & Ethan):\n * A single message can legitimately carry more than one DISTINCT artifact — e.g. a\n * research report PLUS a *standalone* generated infographic. This is deliberately\n * NOT in conflict with the server-side consolidation in AgentRunner\n * (Pranav, 5664b86: \"keep the report's embedded image in the report, not as a\n * duplicate artifact\"): that logic only suppresses media that is *embedded inline*\n * (base64) in another artifact's payload — a true duplicate. Genuinely standalone\n * sibling artifacts (the report uses SVG charts; the infographic is a separate JPEG)\n * are correctly kept as separate artifacts, and the UI must show every one of them.\n *\n * The earlier `artifactList[length - 1]` (\"show only the most recent\") approach\n * (EL-BC, 95492622) assumed consolidation always left exactly one artifact per\n * message; when it legitimately leaves two, that silently hid the report behind the\n * image. Grouping by artifactId (latest version each) shows all distinct artifacts\n * while still collapsing multiple *versions* of the same artifact to one card.\n */\n private applyArtifactsToInstance(\n instance: MessageItemComponent,\n messageId: string,\n childCdRef: ChangeDetectorRef\n ): void {\n const infos = this.resolveDistinctArtifacts(messageId);\n if (infos.length === 0) {\n instance.artifacts = [];\n instance.artifact = undefined;\n instance.artifactVersion = undefined;\n return;\n }\n\n Promise.all(\n infos.map(info =>\n Promise.all([info.getArtifact(), info.getVersion()]).then(([artifact, version]) => ({ artifact, version }))\n )\n )\n .then(refs => {\n instance.artifacts = refs;\n // Keep the legacy single inputs pointed at the first entry for back-compat.\n instance.artifact = refs[0]?.artifact;\n instance.artifactVersion = refs[0]?.version;\n // zone.js 0.15: parent detectChanges doesn't propagate to dynamically created children\n childCdRef.detectChanges();\n this.cdRef.detectChanges();\n })\n .catch(err => {\n console.error('Failed to lazy-load artifacts:', err);\n });\n }\n\n /**\n * Groups a message's artifact list by artifactId, keeping the highest version of\n * each. Multiple versions of the SAME artifact collapse to one card (latest wins),\n * while genuinely distinct artifacts are all retained.\n */\n private resolveDistinctArtifacts(messageId: string): LazyArtifactInfo[] {\n const list = this.artifactMap.get(messageId);\n if (!list || list.length === 0) {\n return [];\n }\n return selectDistinctLatestArtifacts(list);\n }\n\n /**\n * Generates a unique key for a message\n * Uses ID if available, otherwise uses a temporary key\n */\n private getMessageKey(message: MJConversationDetailEntity): string {\n return message.ID && message.ID.length > 0\n ? message.ID\n : `temp_${message.__mj_CreatedAt?.getTime() || Date.now()}`;\n }\n\n /**\n * Determines whether to show the date filter dropdown\n * Only show if conversation is long and spans multiple days\n */\n private updateDateFilterVisibility(): void {\n if (!this.messages || this.messages.length < 20) {\n this.shouldShowDateFilter = false;\n return;\n }\n\n // Check if messages span more than 2 days\n const dates = this.messages\n .map(m => m.__mj_CreatedAt)\n .filter(d => d != null)\n .map(d => new Date(d!).setHours(0, 0, 0, 0));\n\n if (dates.length === 0) {\n this.shouldShowDateFilter = false;\n return;\n }\n\n const uniqueDates = new Set(dates);\n const daySpan = uniqueDates.size;\n\n // Show filter if conversation has 20+ messages and spans 3+ days\n this.shouldShowDateFilter = daySpan >= 3;\n }\n\n /**\n * Scrolls the message list to the bottom\n */\n private scrollToBottom(): void {\n if (this.scrollContainer && this.scrollContainer.nativeElement) {\n Promise.resolve().then(() => {\n const element = this.scrollContainer.nativeElement;\n element.scrollTop = element.scrollHeight;\n });\n }\n }\n\n /**\n * Removes a message from the rendered list\n * Called externally when a message is deleted\n */\n public removeMessage(message: MJConversationDetailEntity): void {\n const key = this.getMessageKey(message);\n const entry = this._renderedMessages.get(key);\n if (entry) {\n entry.ref.destroy();\n this._renderedMessages.delete(key);\n }\n }\n}","<div class=\"message-list-container\" #scrollContainer>\n <!-- Sticky Date Header (only show for long conversations spanning multiple days) -->\n @if (showDateNavigation && shouldShowDateFilter && messages.length > 0) {\n <div class=\"sticky-date-header\">\n <div class=\"sticky-date-wrapper\">\n <button class=\"sticky-date-button\" (click)=\"toggleDateNav()\">\n <span>{{ currentDateDisplay }}</span>\n <i class=\"fas fa-chevron-down\"></i>\n </button>\n <div class=\"date-nav-dropdown\" [class.show]=\"showDateNav\">\n <div class=\"date-nav-option jump-to\">Jump to...</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('today')\">Today</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('yesterday')\">Yesterday</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('last-week')\">Last week</div>\n <div class=\"date-nav-option\" (click)=\"jumpToDate('last-month')\">Last month</div>\n </div>\n </div>\n </div>\n }\n\n <ng-container #messageContainer></ng-container>\n\n @if (messages.length === 0 && showEmptyFill) {\n <mj-empty-state\n class=\"messages-empty-fill\"\n Icon=\"fa-solid fa-comments\"\n Title=\"No messages yet. Start a conversation!\" />\n }\n</div>"]}
|
package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.js
CHANGED
|
@@ -374,11 +374,11 @@ export class RealtimeEvidencePlaybackComponent {
|
|
|
374
374
|
i0.ɵɵconditional(ctx.AudioUrl ? 2 : 3);
|
|
375
375
|
i0.ɵɵadvance(3);
|
|
376
376
|
i0.ɵɵconditional(ctx.Turns.length === 0 ? 5 : 6);
|
|
377
|
-
} }, dependencies: [CommonModule, i1.DecimalPipe], styles: ["[_nghost-%COMP%] {\n display: block;\n height: 100%;\n}\n\n.evp[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-surface-sunken);\n}\n\n\n\n.evp-player[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 0 0 auto;\n padding: 12px 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n}\n\n.evp-play[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border: none;\n border-radius: var(--mj-radius-full, 50%);\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n cursor: pointer;\n font-size: 0.9375rem;\n transition: background 0.12s ease, transform 0.12s ease;\n\n
|
|
377
|
+
} }, dependencies: [CommonModule, i1.DecimalPipe], styles: ["\n\n\n\n\n\n\n\n\n\n\n[_nghost-%COMP%] {\n display: block;\n height: 100%;\n}\n\n.evp[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-surface-sunken);\n}\n\n\n\n.evp-player[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 0 0 auto;\n padding: 12px 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n}\n\n.evp-play[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border: none;\n border-radius: var(--mj-radius-full, 50%);\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n cursor: pointer;\n font-size: 0.9375rem;\n transition: background 0.12s ease, transform 0.12s ease;\n}\n\n.evp-play[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n}\n\n.evp-play[_ngcontent-%COMP%]:active {\n transform: scale(0.94);\n}\n\n.evp-play[_ngcontent-%COMP%]:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n.evp-transport[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.evp-scrubber[_ngcontent-%COMP%] {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n height: 36px;\n cursor: pointer;\n border-radius: var(--mj-radius-sm, 4px);\n}\n\n.evp-scrubber[_ngcontent-%COMP%]:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n.evp-waveform[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n gap: 2px;\n height: 100%;\n pointer-events: none;\n}\n\n.evp-bar[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-width: 1px;\n border-radius: 1px;\n background: var(--mj-border-strong);\n transition: background 0.1s ease;\n}\n\n.evp-bar--played[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n}\n\n.evp-progress[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n pointer-events: none;\n border-radius: var(--mj-radius-sm, 4px) 0 0 var(--mj-radius-sm, 4px);\n}\n\n.evp-thumb[_ngcontent-%COMP%] {\n position: absolute;\n top: 50%;\n width: 10px;\n height: 10px;\n border-radius: var(--mj-radius-full, 50%);\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n\n.evp-time[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.75rem;\n font-variant-numeric: tabular-nums;\n color: var(--mj-text-secondary);\n}\n\n.evp-time__current[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n.evp-time__sep[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}\n\n.evp-noaudio[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 0.8125rem;\n color: var(--mj-text-muted);\n}\n\n.evp-noaudio[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}\n\n\n\n.evp-transcript[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n scrollbar-width: thin;\n}\n\n.evp-transcript-empty[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n color: var(--mj-text-muted);\n font-size: 0.8125rem;\n}\n\n.evp-transcript-empty[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1.75rem;\n color: var(--mj-text-disabled);\n}\n\n.evp-transcript-empty[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n}\n\n.evp-turn[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 3px;\n width: 100%;\n margin: 0;\n padding: 8px 10px 8px 14px;\n border: none;\n border-left: 3px solid transparent;\n border-radius: var(--mj-radius-md, 6px);\n background: transparent;\n text-align: left;\n font: inherit;\n color: inherit;\n cursor: pointer;\n transition: background 0.12s ease, border-color 0.12s ease;\n}\n\n.evp-turn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.evp-turn[_ngcontent-%COMP%]:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.evp-turn--active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-left-color: var(--mj-brand-primary);\n}\n\n.evp-turn__meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n\n.evp-turn__speaker[_ngcontent-%COMP%] {\n font-size: 0.75rem;\n font-weight: 700;\n letter-spacing: 0.01em;\n}\n\n.evp-turn--user[_ngcontent-%COMP%] .evp-turn__speaker[_ngcontent-%COMP%] {\n color: var(--mj-status-info, var(--mj-brand-primary));\n}\n\n.evp-turn--agent[_ngcontent-%COMP%] .evp-turn__speaker[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.evp-turn__time[_ngcontent-%COMP%] {\n font-size: 0.6875rem;\n font-variant-numeric: tabular-nums;\n color: var(--mj-text-muted);\n}\n\n.evp-turn__text[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 0.8125rem;\n line-height: 1.45;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .evp-play[_ngcontent-%COMP%], \n .evp-bar[_ngcontent-%COMP%], \n .evp-turn[_ngcontent-%COMP%] {\n transition: none;\n }\n}"], changeDetection: 0 });
|
|
378
378
|
}
|
|
379
379
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RealtimeEvidencePlaybackComponent, [{
|
|
380
380
|
type: Component,
|
|
381
|
-
args: [{ standalone: true, selector: 'mj-realtime-evidence-playback', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"evp\">\n <!-- ---------- Audio transport ---------- -->\n <div class=\"evp-player\">\n @if (AudioUrl) {\n <audio\n #audio\n [src]=\"AudioUrl\"\n preload=\"metadata\"\n (loadedmetadata)=\"OnLoadedMetadata()\"\n (timeupdate)=\"OnTimeUpdate()\"\n (play)=\"OnPlay()\"\n (pause)=\"OnPause()\"\n (ended)=\"OnEnded()\"\n ></audio>\n\n <button\n type=\"button\"\n class=\"evp-play\"\n [attr.aria-label]=\"IsPlaying ? 'Pause recording' : 'Play recording'\"\n [title]=\"IsPlaying ? 'Pause' : 'Play'\"\n (click)=\"TogglePlay()\"\n >\n <i class=\"fa-solid\" [class.fa-pause]=\"IsPlaying\" [class.fa-play]=\"!IsPlaying\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"evp-transport\">\n <div\n #track\n class=\"evp-scrubber\"\n role=\"slider\"\n tabindex=\"0\"\n aria-label=\"Seek through the recording\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"100\"\n [attr.aria-valuenow]=\"(Progress * 100) | number: '1.0-0'\"\n [attr.aria-valuetext]=\"CurrentLabel + ' of ' + DurationLabel\"\n (click)=\"OnScrubberClick($event, track)\"\n (keydown)=\"OnScrubberKeydown($event)\"\n >\n <div class=\"evp-waveform\" aria-hidden=\"true\">\n @for (bar of WaveformBars; track $index) {\n <span\n class=\"evp-bar\"\n [class.evp-bar--played]=\"($index / WaveformBars.length) <= Progress\"\n [style.height.%]=\"bar * 100\"\n ></span>\n }\n </div>\n <div class=\"evp-progress\" [style.width.%]=\"Progress * 100\" aria-hidden=\"true\"></div>\n <div class=\"evp-thumb\" [style.left.%]=\"Progress * 100\" aria-hidden=\"true\"></div>\n </div>\n <div class=\"evp-time\">\n <span class=\"evp-time__current\">{{ CurrentLabel }}</span>\n <span class=\"evp-time__sep\">/</span>\n <span class=\"evp-time__total\">{{ DurationLabel }}</span>\n </div>\n </div>\n } @else {\n <div class=\"evp-noaudio\">\n <i class=\"fa-solid fa-microphone-slash\" aria-hidden=\"true\"></i>\n <span>No audio recording is available for this session.</span>\n </div>\n }\n </div>\n\n <!-- ---------- Transcript ---------- -->\n <div class=\"evp-transcript\" role=\"list\" aria-label=\"Session transcript\">\n @if (Turns.length === 0) {\n <div class=\"evp-transcript-empty\">\n <i class=\"fa-solid fa-comment-dots\" aria-hidden=\"true\"></i>\n <p>No transcript turns to show.</p>\n </div>\n } @else {\n @for (turn of Turns; track TrackByTurn($index, turn)) {\n <button\n type=\"button\"\n class=\"evp-turn\"\n role=\"listitem\"\n tabindex=\"0\"\n [class.evp-turn--user]=\"IsUserTurn(turn)\"\n [class.evp-turn--agent]=\"!IsUserTurn(turn)\"\n [class.evp-turn--active]=\"IsTurnActive(turn)\"\n [attr.aria-label]=\"SpeakerLabel(turn) + ' at ' + TurnTimestamp(turn) + ': ' + turn.Message\"\n [attr.aria-current]=\"IsTurnActive(turn) ? 'true' : null\"\n (click)=\"SeekToTurn(turn)\"\n >\n <div class=\"evp-turn__meta\">\n <span class=\"evp-turn__speaker\">{{ SpeakerLabel(turn) }}</span>\n <span class=\"evp-turn__time\">{{ TurnTimestamp(turn) }}</span>\n </div>\n <p class=\"evp-turn__text\">{{ turn.Message }}</p>\n </button>\n }\n }\n </div>\n</div>\n", styles: [":host {\n display: block;\n height: 100%;\n}\n\n.evp {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-surface-sunken);\n}\n\n/* ---------- Audio transport ---------- */\n.evp-player {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 0 0 auto;\n padding: 12px 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n}\n\n.evp-play {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border: none;\n border-radius: var(--mj-radius-full, 50%);\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n cursor: pointer;\n font-size: 0.9375rem;\n transition: background 0.12s ease, transform 0.12s ease;\n\n
|
|
381
|
+
args: [{ standalone: true, selector: 'mj-realtime-evidence-playback', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"evp\">\n <!-- ---------- Audio transport ---------- -->\n <div class=\"evp-player\">\n @if (AudioUrl) {\n <audio\n #audio\n [src]=\"AudioUrl\"\n preload=\"metadata\"\n (loadedmetadata)=\"OnLoadedMetadata()\"\n (timeupdate)=\"OnTimeUpdate()\"\n (play)=\"OnPlay()\"\n (pause)=\"OnPause()\"\n (ended)=\"OnEnded()\"\n ></audio>\n\n <button\n type=\"button\"\n class=\"evp-play\"\n [attr.aria-label]=\"IsPlaying ? 'Pause recording' : 'Play recording'\"\n [title]=\"IsPlaying ? 'Pause' : 'Play'\"\n (click)=\"TogglePlay()\"\n >\n <i class=\"fa-solid\" [class.fa-pause]=\"IsPlaying\" [class.fa-play]=\"!IsPlaying\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"evp-transport\">\n <div\n #track\n class=\"evp-scrubber\"\n role=\"slider\"\n tabindex=\"0\"\n aria-label=\"Seek through the recording\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"100\"\n [attr.aria-valuenow]=\"(Progress * 100) | number: '1.0-0'\"\n [attr.aria-valuetext]=\"CurrentLabel + ' of ' + DurationLabel\"\n (click)=\"OnScrubberClick($event, track)\"\n (keydown)=\"OnScrubberKeydown($event)\"\n >\n <div class=\"evp-waveform\" aria-hidden=\"true\">\n @for (bar of WaveformBars; track $index) {\n <span\n class=\"evp-bar\"\n [class.evp-bar--played]=\"($index / WaveformBars.length) <= Progress\"\n [style.height.%]=\"bar * 100\"\n ></span>\n }\n </div>\n <div class=\"evp-progress\" [style.width.%]=\"Progress * 100\" aria-hidden=\"true\"></div>\n <div class=\"evp-thumb\" [style.left.%]=\"Progress * 100\" aria-hidden=\"true\"></div>\n </div>\n <div class=\"evp-time\">\n <span class=\"evp-time__current\">{{ CurrentLabel }}</span>\n <span class=\"evp-time__sep\">/</span>\n <span class=\"evp-time__total\">{{ DurationLabel }}</span>\n </div>\n </div>\n } @else {\n <div class=\"evp-noaudio\">\n <i class=\"fa-solid fa-microphone-slash\" aria-hidden=\"true\"></i>\n <span>No audio recording is available for this session.</span>\n </div>\n }\n </div>\n\n <!-- ---------- Transcript ---------- -->\n <div class=\"evp-transcript\" role=\"list\" aria-label=\"Session transcript\">\n @if (Turns.length === 0) {\n <div class=\"evp-transcript-empty\">\n <i class=\"fa-solid fa-comment-dots\" aria-hidden=\"true\"></i>\n <p>No transcript turns to show.</p>\n </div>\n } @else {\n @for (turn of Turns; track TrackByTurn($index, turn)) {\n <button\n type=\"button\"\n class=\"evp-turn\"\n role=\"listitem\"\n tabindex=\"0\"\n [class.evp-turn--user]=\"IsUserTurn(turn)\"\n [class.evp-turn--agent]=\"!IsUserTurn(turn)\"\n [class.evp-turn--active]=\"IsTurnActive(turn)\"\n [attr.aria-label]=\"SpeakerLabel(turn) + ' at ' + TurnTimestamp(turn) + ': ' + turn.Message\"\n [attr.aria-current]=\"IsTurnActive(turn) ? 'true' : null\"\n (click)=\"SeekToTurn(turn)\"\n >\n <div class=\"evp-turn__meta\">\n <span class=\"evp-turn__speaker\">{{ SpeakerLabel(turn) }}</span>\n <span class=\"evp-turn__time\">{{ TurnTimestamp(turn) }}</span>\n </div>\n <p class=\"evp-turn__text\">{{ turn.Message }}</p>\n </button>\n }\n }\n </div>\n</div>\n", styles: ["/**\n * Realtime evidence playback \u2014 synced audio transport + transcript.\n *\n * NOTE: this package builds with bare `ngc` (no Sass step) \u2014 component styles\n * are embedded RAW into the compiled JS. Keep this file FLAT, plain CSS.\n * Sass nesting / BEM selector concatenation would be silently dropped by the\n * browser and the rules would be DEAD. See .github/scripts/check-ngc-scss.sh\n * (CI gate) and MemberJunction/MJ#3105.\n */\n\n:host {\n display: block;\n height: 100%;\n}\n\n.evp {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-surface-sunken);\n}\n\n/* ---------- Audio transport ---------- */\n.evp-player {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 0 0 auto;\n padding: 12px 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n}\n\n.evp-play {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border: none;\n border-radius: var(--mj-radius-full, 50%);\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n cursor: pointer;\n font-size: 0.9375rem;\n transition: background 0.12s ease, transform 0.12s ease;\n}\n\n.evp-play:hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n}\n\n.evp-play:active {\n transform: scale(0.94);\n}\n\n.evp-play:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n.evp-transport {\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.evp-scrubber {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n height: 36px;\n cursor: pointer;\n border-radius: var(--mj-radius-sm, 4px);\n}\n\n.evp-scrubber:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n.evp-waveform {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n gap: 2px;\n height: 100%;\n pointer-events: none;\n}\n\n.evp-bar {\n flex: 1 1 auto;\n min-width: 1px;\n border-radius: 1px;\n background: var(--mj-border-strong);\n transition: background 0.1s ease;\n}\n\n.evp-bar--played {\n background: var(--mj-brand-primary);\n}\n\n.evp-progress {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n pointer-events: none;\n border-radius: var(--mj-radius-sm, 4px) 0 0 var(--mj-radius-sm, 4px);\n}\n\n.evp-thumb {\n position: absolute;\n top: 50%;\n width: 10px;\n height: 10px;\n border-radius: var(--mj-radius-full, 50%);\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n\n.evp-time {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.75rem;\n font-variant-numeric: tabular-nums;\n color: var(--mj-text-secondary);\n}\n\n.evp-time__current {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n\n.evp-time__sep {\n color: var(--mj-text-disabled);\n}\n\n.evp-noaudio {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 0.8125rem;\n color: var(--mj-text-muted);\n}\n\n.evp-noaudio i {\n color: var(--mj-text-disabled);\n}\n\n/* ---------- Transcript ---------- */\n.evp-transcript {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n scrollbar-width: thin;\n}\n\n.evp-transcript-empty {\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n color: var(--mj-text-muted);\n font-size: 0.8125rem;\n}\n\n.evp-transcript-empty i {\n font-size: 1.75rem;\n color: var(--mj-text-disabled);\n}\n\n.evp-transcript-empty p {\n margin: 0;\n}\n\n.evp-turn {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 3px;\n width: 100%;\n margin: 0;\n padding: 8px 10px 8px 14px;\n border: none;\n border-left: 3px solid transparent;\n border-radius: var(--mj-radius-md, 6px);\n background: transparent;\n text-align: left;\n font: inherit;\n color: inherit;\n cursor: pointer;\n transition: background 0.12s ease, border-color 0.12s ease;\n}\n\n.evp-turn:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.evp-turn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.evp-turn--active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-left-color: var(--mj-brand-primary);\n}\n\n.evp-turn__meta {\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n\n.evp-turn__speaker {\n font-size: 0.75rem;\n font-weight: 700;\n letter-spacing: 0.01em;\n}\n\n.evp-turn--user .evp-turn__speaker {\n color: var(--mj-status-info, var(--mj-brand-primary));\n}\n\n.evp-turn--agent .evp-turn__speaker {\n color: var(--mj-brand-primary);\n}\n\n.evp-turn__time {\n font-size: 0.6875rem;\n font-variant-numeric: tabular-nums;\n color: var(--mj-text-muted);\n}\n\n.evp-turn__text {\n margin: 0;\n font-size: 0.8125rem;\n line-height: 1.45;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .evp-play,\n .evp-bar,\n .evp-turn {\n transition: none;\n }\n}\n"] }]
|
|
382
382
|
}], null, { Turns: [{
|
|
383
383
|
type: Input
|
|
384
384
|
}], AudioUrl: [{
|