@memberjunction/ng-conversations 5.39.0 → 5.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/distinct-artifacts.test.d.ts +2 -0
- package/dist/__tests__/distinct-artifacts.test.d.ts.map +1 -0
- package/dist/__tests__/distinct-artifacts.test.js +56 -0
- package/dist/__tests__/distinct-artifacts.test.js.map +1 -0
- package/dist/lib/components/conversation/conversation-chat-area.component.js +2 -2
- package/dist/lib/components/conversation/conversation-empty-state.component.js +70 -80
- package/dist/lib/components/conversation/conversation-empty-state.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +23 -1
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +73 -49
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +28 -0
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +62 -49
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/utils/distinct-artifacts.d.ts +22 -0
- package/dist/lib/utils/distinct-artifacts.d.ts.map +1 -0
- package/dist/lib/utils/distinct-artifacts.js +26 -0
- package/dist/lib/utils/distinct-artifacts.js.map +1 -0
- package/package.json +23 -23
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Component, Input, Output, EventEmitter, ViewChild, ViewContainerRef } from '@angular/core';
|
|
2
2
|
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
3
3
|
import { MessageItemComponent } from './message-item.component';
|
|
4
|
+
import { selectDistinctLatestArtifacts } from '../../utils/distinct-artifacts';
|
|
4
5
|
import * as i0 from "@angular/core";
|
|
5
6
|
const _c0 = ["messageContainer"];
|
|
6
7
|
const _c1 = ["scrollContainer"];
|
|
@@ -203,32 +204,9 @@ export class MessageListComponent extends BaseAngularComponent {
|
|
|
203
204
|
instance.isProcessing = this.isProcessing;
|
|
204
205
|
instance.userAvatarMap = this.userAvatarMap;
|
|
205
206
|
instance.isLastMessage = (index === messages.length - 1); // Update last message flag
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const lastArtifact = artifactList && artifactList.length > 0
|
|
210
|
-
? artifactList[artifactList.length - 1]
|
|
211
|
-
: undefined;
|
|
212
|
-
// Trigger lazy load and set properties
|
|
213
|
-
if (lastArtifact) {
|
|
214
|
-
// Lazy load in background - don't block UI
|
|
215
|
-
Promise.all([
|
|
216
|
-
lastArtifact.getArtifact(),
|
|
217
|
-
lastArtifact.getVersion()
|
|
218
|
-
]).then(([artifact, version]) => {
|
|
219
|
-
instance.artifact = artifact;
|
|
220
|
-
instance.artifactVersion = version;
|
|
221
|
-
// zone.js 0.15: parent detectChanges doesn't propagate to dynamically created children
|
|
222
|
-
existing.changeDetectorRef.detectChanges();
|
|
223
|
-
this.cdRef.detectChanges();
|
|
224
|
-
}).catch(err => {
|
|
225
|
-
console.error('Failed to lazy-load artifact:', err);
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
instance.artifact = undefined;
|
|
230
|
-
instance.artifactVersion = undefined;
|
|
231
|
-
}
|
|
207
|
+
// Surface ALL distinct artifacts on this message (latest version each),
|
|
208
|
+
// not just the most recent one. Loads lazily in the background.
|
|
209
|
+
this.applyArtifactsToInstance(instance, message.ID, existing.changeDetectorRef);
|
|
232
210
|
// Update agent run from map
|
|
233
211
|
instance.agentRun = this.agentRunMap.get(message.ID) || null;
|
|
234
212
|
// Update ratings from map
|
|
@@ -255,28 +233,8 @@ export class MessageListComponent extends BaseAngularComponent {
|
|
|
255
233
|
instance.isProcessing = this.isProcessing;
|
|
256
234
|
instance.userAvatarMap = this.userAvatarMap;
|
|
257
235
|
instance.isLastMessage = (index === messages.length - 1); // Mark last message
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
// Use LAST artifact (most recent) instead of first for display
|
|
261
|
-
const lastArtifact = artifactList && artifactList.length > 0
|
|
262
|
-
? artifactList[artifactList.length - 1]
|
|
263
|
-
: undefined;
|
|
264
|
-
// Trigger lazy load and set properties
|
|
265
|
-
if (lastArtifact) {
|
|
266
|
-
// Lazy load in background - don't block UI
|
|
267
|
-
Promise.all([
|
|
268
|
-
lastArtifact.getArtifact(),
|
|
269
|
-
lastArtifact.getVersion()
|
|
270
|
-
]).then(([artifact, version]) => {
|
|
271
|
-
instance.artifact = artifact;
|
|
272
|
-
instance.artifactVersion = version;
|
|
273
|
-
// zone.js 0.15: parent detectChanges doesn't propagate to dynamically created children
|
|
274
|
-
componentRef.changeDetectorRef.detectChanges();
|
|
275
|
-
this.cdRef.detectChanges();
|
|
276
|
-
}).catch(err => {
|
|
277
|
-
console.error('Failed to lazy-load artifact:', err);
|
|
278
|
-
});
|
|
279
|
-
}
|
|
236
|
+
// Surface ALL distinct artifacts on this message (latest version each).
|
|
237
|
+
this.applyArtifactsToInstance(instance, message.ID, componentRef.changeDetectorRef);
|
|
280
238
|
// Pass agent run from map (loaded once per conversation)
|
|
281
239
|
instance.agentRun = this.agentRunMap.get(message.ID) || null;
|
|
282
240
|
// Pass ratings from map (parsed once per conversation)
|
|
@@ -322,6 +280,61 @@ export class MessageListComponent extends BaseAngularComponent {
|
|
|
322
280
|
this.cdRef.detectChanges();
|
|
323
281
|
}
|
|
324
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Resolves the DISTINCT artifacts for a message (one entry per artifactId at its
|
|
285
|
+
* latest version), lazy-loads them all, and applies them to the rendered
|
|
286
|
+
* message-item. Loads in the background so the UI never blocks.
|
|
287
|
+
*
|
|
288
|
+
* WHY WE SURFACE THEM ALL (design rationale — see PR discussion w/ Pranav & Ethan):
|
|
289
|
+
* A single message can legitimately carry more than one DISTINCT artifact — e.g. a
|
|
290
|
+
* research report PLUS a *standalone* generated infographic. This is deliberately
|
|
291
|
+
* NOT in conflict with the server-side consolidation in AgentRunner
|
|
292
|
+
* (Pranav, 5664b86: "keep the report's embedded image in the report, not as a
|
|
293
|
+
* duplicate artifact"): that logic only suppresses media that is *embedded inline*
|
|
294
|
+
* (base64) in another artifact's payload — a true duplicate. Genuinely standalone
|
|
295
|
+
* sibling artifacts (the report uses SVG charts; the infographic is a separate JPEG)
|
|
296
|
+
* are correctly kept as separate artifacts, and the UI must show every one of them.
|
|
297
|
+
*
|
|
298
|
+
* The earlier `artifactList[length - 1]` ("show only the most recent") approach
|
|
299
|
+
* (EL-BC, 95492622) assumed consolidation always left exactly one artifact per
|
|
300
|
+
* message; when it legitimately leaves two, that silently hid the report behind the
|
|
301
|
+
* image. Grouping by artifactId (latest version each) shows all distinct artifacts
|
|
302
|
+
* while still collapsing multiple *versions* of the same artifact to one card.
|
|
303
|
+
*/
|
|
304
|
+
applyArtifactsToInstance(instance, messageId, childCdRef) {
|
|
305
|
+
const infos = this.resolveDistinctArtifacts(messageId);
|
|
306
|
+
if (infos.length === 0) {
|
|
307
|
+
instance.artifacts = [];
|
|
308
|
+
instance.artifact = undefined;
|
|
309
|
+
instance.artifactVersion = undefined;
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
Promise.all(infos.map(info => Promise.all([info.getArtifact(), info.getVersion()]).then(([artifact, version]) => ({ artifact, version }))))
|
|
313
|
+
.then(refs => {
|
|
314
|
+
instance.artifacts = refs;
|
|
315
|
+
// Keep the legacy single inputs pointed at the first entry for back-compat.
|
|
316
|
+
instance.artifact = refs[0]?.artifact;
|
|
317
|
+
instance.artifactVersion = refs[0]?.version;
|
|
318
|
+
// zone.js 0.15: parent detectChanges doesn't propagate to dynamically created children
|
|
319
|
+
childCdRef.detectChanges();
|
|
320
|
+
this.cdRef.detectChanges();
|
|
321
|
+
})
|
|
322
|
+
.catch(err => {
|
|
323
|
+
console.error('Failed to lazy-load artifacts:', err);
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Groups a message's artifact list by artifactId, keeping the highest version of
|
|
328
|
+
* each. Multiple versions of the SAME artifact collapse to one card (latest wins),
|
|
329
|
+
* while genuinely distinct artifacts are all retained.
|
|
330
|
+
*/
|
|
331
|
+
resolveDistinctArtifacts(messageId) {
|
|
332
|
+
const list = this.artifactMap.get(messageId);
|
|
333
|
+
if (!list || list.length === 0) {
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
return selectDistinctLatestArtifacts(list);
|
|
337
|
+
}
|
|
325
338
|
/**
|
|
326
339
|
* Generates a unique key for a message
|
|
327
340
|
* Uses ID if available, otherwise uses a temporary key
|
|
@@ -453,5 +466,5 @@ export class MessageListComponent extends BaseAngularComponent {
|
|
|
453
466
|
}], messagesUpdate: [{
|
|
454
467
|
type: Input
|
|
455
468
|
}] }); })();
|
|
456
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MessageListComponent, { className: "MessageListComponent", filePath: "src/lib/components/message/message-list.component.ts", lineNumber:
|
|
469
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MessageListComponent, { className: "MessageListComponent", filePath: "src/lib/components/message/message-list.component.ts", lineNumber: 36 }); })();
|
|
457
470
|
//# sourceMappingURL=message-list.component.js.map
|
|
@@ -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":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,EACL,MAAM,EACN,YAAY,EACZ,SAAS,EACT,gBAAgB,EASjB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAqB,MAAM,0BAA0B,CAAC;;;;;;ICd3E,AADF,AADF,8BAAgC,aACG,gBAC8B;IAA1B,wLAAS,sBAAe,KAAC;IAC1D,4BAAM;IAAA,YAAwB;IAAA,iBAAO;IACrC,uBAAmC;IACrC,iBAAS;IAEP,AADF,8BAA0D,aACnB;IAAA,0BAAU;IAAA,iBAAM;IACrD,+BAA2D;IAA9B,qLAAS,kBAAW,OAAO,CAAC,KAAC;IAAC,sBAAK;IAAA,iBAAM;IACtE,gCAA+D;IAAlC,sLAAS,kBAAW,WAAW,CAAC,KAAC;IAAC,0BAAS;IAAA,iBAAM;IAC9E,gCAA+D;IAAlC,sLAAS,kBAAW,WAAW,CAAC,KAAC;IAAC,0BAAS;IAAA,iBAAM;IAC9E,gCAAgE;IAAnC,sLAAS,kBAAW,YAAY,CAAC,KAAC;IAAC,2BAAU;IAGhF,AADE,AADE,AAD4E,iBAAM,EAC5E,EACF,EACF;;;IAXM,eAAwB;IAAxB,+CAAwB;IAGD,eAA0B;IAA1B,0CAA0B;;;IAc7D,8BAAyB;IACvB,wBAA+B;IAC/B,yBAAG;IAAA,sDAAsC;IAC3C,AAD2C,iBAAI,EACzC;;ADHV;;;;GAIG;AAOH,MAAM,OAAO,oBAAqB,SAAQ,oBAAoB;IAoCxC;IAnCJ,QAAQ,GAAiC,EAAE,CAAC;IAC5C,YAAY,CAA+B;IAC3C,WAAW,CAAY;IACvB,YAAY,GAAY,KAAK,CAAC;IAC9B,WAAW,GAAoC,IAAI,GAAG,EAAE,CAAC;IACzD,WAAW,GAA4C,IAAI,GAAG,EAAE,CAAC;IACjE,UAAU,GAA8B,IAAI,GAAG,EAAE,CAAC;IAClD,aAAa,GAAqE,IAAI,GAAG,EAAE,CAAC;IAC5F,cAAc,GAAqC,IAAI,GAAG,EAAE,CAAC;IAE5D,WAAW,GAAG,IAAI,YAAY,EAA8B,CAAC;IAC7D,aAAa,GAAG,IAAI,YAAY,EAA8B,CAAC;IAC/D,YAAY,GAAG,IAAI,YAAY,EAA8B,CAAC;IAC9D,mBAAmB,GAAG,IAAI,YAAY,EAA8B,CAAC;IACrE,eAAe,GAAG,IAAI,YAAY,EAA4C,CAAC;IAC/E,aAAa,GAAG,IAAI,YAAY,EAA8B,CAAC;IAC/D,UAAU,GAAG,IAAI,YAAY,EAA8B,CAAC;IAC5D,aAAa,GAAG,IAAI,YAAY,EAA8B,CAAC;IAC/D,gBAAgB,GAAG,IAAI,YAAY,EAAoD,CAAC;IACxF,yBAAyB,GAAG,IAAI,YAAY,EAAwC,CAAC;IACrF,iBAAiB,GAAG,IAAI,YAAY,EAAqB,CAAC;IAC1D,mBAAmB,GAAG,IAAI,YAAY,EAAU,CAAC,CAAC,kBAAkB;IACpE,iBAAiB,GAAG,IAAI,YAAY,EAA8B,CAAC;IAEzB,mBAAmB,CAAoB;IACpE,eAAe,CAAc;IAEnD,iBAAiB,GAAG,IAAI,GAAG,EAAe,CAAC;IAC3C,qBAAqB,GAAG,KAAK,CAAC;IAC9B,qBAAqB,GAAG,CAAC,CAAC,CAAC,8CAA8C;IAE1E,kBAAkB,GAAW,OAAO,CAAC;IACrC,WAAW,GAAY,KAAK,CAAC;IAC7B,oBAAoB,GAAY,KAAK,CAAC;IAE7C,YAAoB,KAAwB;QAC1C,KAAK,EAAE,CAAC;QADU,UAAK,GAAL,KAAK,CAAmB;IAE5C,CAAC;IAEM,aAAa;QAClB,IAAI,CAAC,WAAW,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC;IACvC,CAAC;IAEM,UAAU,CAAC,MAAc;QAC9B,qCAAqC;QACrC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QAEzB,iCAAiC;QACjC,QAAO,MAAM,EAAE,CAAC;YACd,KAAK,OAAO;gBACV,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC;gBAClC,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,CAAC,kBAAkB,GAAG,WAAW,CAAC;gBACtC,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,CAAC,kBAAkB,GAAG,WAAW,CAAC;gBACtC,MAAM;YACR,KAAK,YAAY;gBACf,IAAI,CAAC,kBAAkB,GAAG,YAAY,CAAC;gBACvC,MAAM;QACV,CAAC;IACH,CAAC;IAED,4CAA4C;IACpC,sBAAsB,GAAG,KAAK,CAAC;IAEvC,QAAQ;QACN,mFAAmF;IACrF,CAAC;IAED,eAAe;QACb,iFAAiF;QACjF,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,mBAAmB,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAC1G,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC;YACnC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACnC,IAAI,CAAC,0BAA0B,EAAE,CAAC;QACpC,CAAC;IACH,CAAC;IAED,WAAW,CAAC,OAAsB;QAChC,kCAAkC;QAClC,gHAAgH;QAChH,+DAA+D;QAC/D,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACrE,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC;YACnC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACnC,IAAI,CAAC,0BAA0B,EAAE,CAAC;QACpC,CAAC;QAED,kEAAkE;QAClE,sEAAsE;QACtE,sEAAsE;QACtE,yFAAyF;QACzF,IAAI,OAAO,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACxE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC;QAED,uEAAuE;QACvE,2EAA2E;QAC3E,+CAA+C;QAC/C,IAAI,OAAO,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3E,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,kBAAkB;QAChB,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC/B,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,IAAI,CAAC,qBAAqB,GAAG,KAAK,CAAC;QACrC,CAAC;IACH,CAAC;IAED,WAAW;QACT,8CAA8C;QAC9C,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;YAC9C,IAAI,YAAY,EAAE,CAAC;gBACjB,YAAY,CAAC,OAAO,EAAE,CAAC;YACzB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAE/B,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,CAAC;QACnC,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,IACI,cAAc,CAAC,QAAsC;QACvD,IAAI,QAAQ,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACzC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,cAAc,CAAC,QAAsC;QAC3D,sDAAsD;QACtD,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAEpB,IAAI,CAAC;YACH,uCAAuC;YACvC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrE,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,GAAG,EAAE,EAAE;gBACnD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBACzB,YAAY,CAAC,OAAO,EAAE,CAAC;oBACvB,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACrC,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,yBAAyB;YACzB,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;gBAClC,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;gBACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAEjD,IAAI,QAAQ,EAAE,CAAC;oBACb,4BAA4B;oBAC5B,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAgC,CAAC;oBAE3D,wCAAwC;oBACxC,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC;oBAEzC,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC;oBAC3B,QAAQ,CAAC,WAAW,GAAG,QAAQ,CAAC;oBAChC,QAAQ,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;oBAC1C,QAAQ,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC;oBAC5C,QAAQ,CAAC,aAAa,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,2BAA2B;oBAErF,qCAAqC;oBACrC,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBACtD,+DAA+D;oBAC/D,MAAM,YAAY,GAAG,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC;wBAC1D,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;wBACvC,CAAC,CAAC,SAAS,CAAC;oBAEd,uCAAuC;oBACvC,IAAI,YAAY,EAAE,CAAC;wBACjB,2CAA2C;wBAC3C,OAAO,CAAC,GAAG,CAAC;4BACV,YAAY,CAAC,WAAW,EAAE;4BAC1B,YAAY,CAAC,UAAU,EAAE;yBAC1B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,EAAE;4BAC9B,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC;4BAC7B,QAAQ,CAAC,eAAe,GAAG,OAAO,CAAC;4BACnC,uFAAuF;4BACvF,QAAQ,CAAC,iBAAiB,CAAC,aAAa,EAAE,CAAC;4BAC3C,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;wBAC7B,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;4BACb,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,GAAG,CAAC,CAAC;wBACtD,CAAC,CAAC,CAAC;oBACL,CAAC;yBAAM,CAAC;wBACN,QAAQ,CAAC,QAAQ,GAAG,SAAS,CAAC;wBAC9B,QAAQ,CAAC,eAAe,GAAG,SAAS,CAAC;oBACvC,CAAC;oBAED,4BAA4B;oBAC5B,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;oBAE7D,0BAA0B;oBAC1B,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBAEnD,8BAA8B;oBAC9B,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;oBAEjE,mFAAmF;oBACnF,+FAA+F;oBAC/F,0DAA0D;oBAC1D,IAAI,eAAe,IAAI,eAAe,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;wBACjE,sEAAsE;wBACtE,QAAQ,CAAC,iBAAiB,CAAC,YAAY,EAAE,CAAC;oBAC5C,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,uBAAuB;oBACvB,MAAM,YAAY,GAAG,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAAC;oBACpF,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC;oBAEvC,aAAa;oBACb,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC;oBAC3B,QAAQ,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;oBAC1C,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;oBACxC,QAAQ,CAAC,WAAW,GAAG,QAAQ,CAAC;oBAChC,QAAQ,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;oBAC1C,QAAQ,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC;oBAC5C,QAAQ,CAAC,aAAa,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,oBAAoB;oBAE9E,qCAAqC;oBACrC,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBACtD,+DAA+D;oBAC/D,MAAM,YAAY,GAAG,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC;wBAC1D,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;wBACvC,CAAC,CAAC,SAAS,CAAC;oBAEd,uCAAuC;oBACvC,IAAI,YAAY,EAAE,CAAC;wBACjB,2CAA2C;wBAC3C,OAAO,CAAC,GAAG,CAAC;4BACV,YAAY,CAAC,WAAW,EAAE;4BAC1B,YAAY,CAAC,UAAU,EAAE;yBAC1B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,EAAE;4BAC9B,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC;4BAC7B,QAAQ,CAAC,eAAe,GAAG,OAAO,CAAC;4BACnC,uFAAuF;4BACvF,YAAY,CAAC,iBAAiB,CAAC,aAAa,EAAE,CAAC;4BAC/C,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;wBAC7B,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;4BACb,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,GAAG,CAAC,CAAC;wBACtD,CAAC,CAAC,CAAC;oBACL,CAAC;oBAED,yDAAyD;oBACzD,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;oBAE7D,uDAAuD;oBACvD,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBAEnD,4BAA4B;oBAC5B,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;oBAEjE,uBAAuB;oBACvB,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,GAA+B,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;oBAChG,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,GAA+B,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;oBACpG,QAAQ,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,GAA+B,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;oBAClG,QAAQ,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,GAA+B,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;oBAChH,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,IAA8C,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;oBACxH,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,GAA+B,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;oBACpG,QAAQ,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,IAAsD,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;oBAClI,QAAQ,CAAC,yBAAyB,CAAC,SAAS,CAAC,CAAC,IAA0C,EAAE,EAAE,CAAC,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;oBACxI,QAAQ,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,UAA6B,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;oBACjH,QAAQ,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,SAAiB,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;oBACxG,QAAQ,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,GAA+B,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;oBAE5G,+CAA+C;oBAC/C,IAAI,QAAQ,CAAC,uBAAuB,EAAE,CAAC;wBACrC,QAAQ,CAAC,uBAAuB,CAAC,SAAS,CAAC,CAAC,IAA0C,EAAE,EAAE;4BACxF,kEAAkE;4BAClE,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;wBACxC,CAAC,CAAC,CAAC;oBACL,CAAC;oBAED,kBAAkB;oBAClB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;oBAE9C,kFAAkF;oBACjF,OAAe,CAAC,aAAa,GAAG,YAAY,CAAC;gBAChD,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,sEAAsE;YACtE,mGAAmG;YACnG,MAAM,aAAa,GAAG,IAAI,CAAC,qBAAqB,CAAC;YACjD,IAAI,CAAC,qBAAqB,GAAG,QAAQ,CAAC,MAAM,CAAC;YAE7C,IAAI,QAAQ,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;gBACpC,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC;YACpC,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,6BAA6B;YAC7B,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,aAAa,CAAC,OAAmC;QACvD,OAAO,OAAO,CAAC,EAAE,IAAI,OAAO,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC;YACxC,CAAC,CAAC,OAAO,CAAC,EAAE;YACZ,CAAC,CAAC,QAAQ,OAAO,CAAC,cAAc,EAAE,OAAO,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAChE,CAAC;IAED;;;OAGG;IACK,0BAA0B;QAChC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAChD,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;YAClC,OAAO;QACT,CAAC;QAED,0CAA0C;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ;aACxB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC;aAC1B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC;aACtB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAE/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;YAClC,OAAO;QACT,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC;QAEjC,iEAAiE;QACjE,IAAI,CAAC,oBAAoB,GAAG,OAAO,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED;;OAEG;IACK,cAAc;QACpB,IAAI,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,CAAC;YAC/D,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;gBAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC;gBACnD,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC;YAC3C,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,aAAa,CAAC,OAAmC;QACtD,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,YAAY,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrD,IAAI,YAAY,EAAE,CAAC;YACjB,YAAY,CAAC,OAAO,EAAE,CAAC;YACvB,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;8GAnXU,oBAAoB;6DAApB,oBAAoB;mCAyBQ,gBAAgB;;;;;;YC3DzD,iCAAqD;YAEnD,uFAAmD;YAkBnD,iCAA+C;YAE/C,sFAA6B;YAM/B,iBAAM;;YA1BJ,eAgBC;YAhBD,8EAgBC;YAID,eAKC;YALD,oDAKC;;;iFDOU,oBAAoB;cANhC,SAAS;6BACI,KAAK,YACP,8BAA8B;;kBAKvC,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBAEL,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBAEN,SAAS;mBAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE;;kBACxD,SAAS;mBAAC,iBAAiB;;kBA2G3B,KAAK;;kFArIK,oBAAoB","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} from '@angular/core';\nimport { MJConversationDetailEntity, MJConversationEntity, RatingJSON } from '@memberjunction/core-entities';\nimport { UserInfo, CompositeKey } from '@memberjunction/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { MessageItemComponent, MessageAttachment } from './message-item.component';\nimport { LazyArtifactInfo } from '../../models/lazy-artifact-info';\nimport { MJAIAgentRunEntityExtended } from '@memberjunction/ai-core-plus';\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 @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\n @ViewChild('messageContainer', { read: ViewContainerRef }) messageContainerRef!: ViewContainerRef;\n @ViewChild('scrollContainer') scrollContainer!: ElementRef;\n\n private _renderedMessages = new Map<string, any>();\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\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\n this._renderedMessages.forEach((componentRef) => {\n if (componentRef) {\n componentRef.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 private updateMessages(messages: MJConversationDetailEntity[]): void {\n // Temporarily detach change detection for performance\n this.cdRef.detach();\n\n try {\n // Remove messages that no longer exist\n const currentIds = new Set(messages.map(m => this.getMessageKey(m)));\n this._renderedMessages.forEach((componentRef, key) => {\n if (!currentIds.has(key)) {\n componentRef.destroy();\n this._renderedMessages.delete(key);\n }\n });\n\n // Add or update messages\n messages.forEach((message, index) => {\n const key = this.getMessageKey(message);\n const existing = this._renderedMessages.get(key);\n\n if (existing) {\n // Update existing component\n const instance = existing.instance as MessageItemComponent;\n\n // Store previous message for comparison\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); // Update last message flag\n\n // Get artifact from lazy-loading map\n const artifactList = this.artifactMap.get(message.ID);\n // Use LAST artifact (most recent) instead of first for display\n const lastArtifact = artifactList && artifactList.length > 0\n ? artifactList[artifactList.length - 1]\n : undefined;\n\n // Trigger lazy load and set properties\n if (lastArtifact) {\n // Lazy load in background - don't block UI\n Promise.all([\n lastArtifact.getArtifact(),\n lastArtifact.getVersion()\n ]).then(([artifact, version]) => {\n instance.artifact = artifact;\n instance.artifactVersion = version;\n // zone.js 0.15: parent detectChanges doesn't propagate to dynamically created children\n existing.changeDetectorRef.detectChanges();\n this.cdRef.detectChanges();\n }).catch(err => {\n console.error('Failed to lazy-load artifact:', err);\n });\n } else {\n instance.artifact = undefined;\n instance.artifactVersion = undefined;\n }\n\n // Update agent run from map\n instance.agentRun = this.agentRunMap.get(message.ID) || null;\n\n // Update ratings from map\n instance.ratings = this.ratingsMap.get(message.ID);\n\n // Update attachments from map\n instance.attachments = this.attachmentsMap.get(message.ID) || [];\n\n // Manually trigger change detection in child component when message status changes\n // This is necessary because we're using OnPush change detection and direct property assignment\n // doesn't trigger ngOnChanges (only reference changes do)\n if (previousMessage && previousMessage.Status !== message.Status) {\n // Use ComponentRef.changeDetectorRef to force update on dynamic child\n existing.changeDetectorRef.markForCheck();\n }\n } else {\n // Create new component\n const componentRef = this.messageContainerRef.createComponent(MessageItemComponent);\n const instance = componentRef.instance;\n\n // Set inputs\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); // Mark last message\n\n // Get artifact from lazy-loading map\n const artifactList = this.artifactMap.get(message.ID);\n // Use LAST artifact (most recent) instead of first for display\n const lastArtifact = artifactList && artifactList.length > 0\n ? artifactList[artifactList.length - 1]\n : undefined;\n\n // Trigger lazy load and set properties\n if (lastArtifact) {\n // Lazy load in background - don't block UI\n Promise.all([\n lastArtifact.getArtifact(),\n lastArtifact.getVersion()\n ]).then(([artifact, version]) => {\n instance.artifact = artifact;\n instance.artifactVersion = version;\n // zone.js 0.15: parent detectChanges doesn't propagate to dynamically created children\n componentRef.changeDetectorRef.detectChanges();\n this.cdRef.detectChanges();\n }).catch(err => {\n console.error('Failed to lazy-load artifact:', err);\n });\n }\n\n // Pass agent run from map (loaded once per conversation)\n instance.agentRun = this.agentRunMap.get(message.ID) || null;\n\n // Pass ratings from map (parsed once per conversation)\n instance.ratings = this.ratingsMap.get(message.ID);\n\n // Pass attachments from map\n instance.attachments = this.attachmentsMap.get(message.ID) || [];\n\n // Subscribe to outputs\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\n // Handle artifact actions if the output exists\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 // Store reference\n this._renderedMessages.set(key, componentRef);\n\n // Store reference on the message entity for later access (like skip-chat pattern)\n (message as any)._componentRef = componentRef;\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 /**\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 componentRef = this._renderedMessages.get(key);\n if (componentRef) {\n componentRef.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 <div class=\"empty-state\">\n <i class=\"fas fa-comments\"></i>\n <p>No messages yet. Start a conversation!</p>\n </div>\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} from '@angular/core';\nimport { MJConversationDetailEntity, MJConversationEntity, RatingJSON } from '@memberjunction/core-entities';\nimport { UserInfo, CompositeKey } from '@memberjunction/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { MessageItemComponent, MessageAttachment } from './message-item.component';\nimport { LazyArtifactInfo } from '../../models/lazy-artifact-info';\nimport { selectDistinctLatestArtifacts } from '../../utils/distinct-artifacts';\nimport { MJAIAgentRunEntityExtended } from '@memberjunction/ai-core-plus';\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 @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\n @ViewChild('messageContainer', { read: ViewContainerRef }) messageContainerRef!: ViewContainerRef;\n @ViewChild('scrollContainer') scrollContainer!: ElementRef;\n\n private _renderedMessages = new Map<string, any>();\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\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\n this._renderedMessages.forEach((componentRef) => {\n if (componentRef) {\n componentRef.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 private updateMessages(messages: MJConversationDetailEntity[]): void {\n // Temporarily detach change detection for performance\n this.cdRef.detach();\n\n try {\n // Remove messages that no longer exist\n const currentIds = new Set(messages.map(m => this.getMessageKey(m)));\n this._renderedMessages.forEach((componentRef, key) => {\n if (!currentIds.has(key)) {\n componentRef.destroy();\n this._renderedMessages.delete(key);\n }\n });\n\n // Add or update messages\n messages.forEach((message, index) => {\n const key = this.getMessageKey(message);\n const existing = this._renderedMessages.get(key);\n\n if (existing) {\n // Update existing component\n const instance = existing.instance as MessageItemComponent;\n\n // Store previous message for comparison\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); // Update last message flag\n\n // Surface ALL distinct artifacts on this message (latest version each),\n // not just the most recent one. Loads lazily in the background.\n this.applyArtifactsToInstance(instance, message.ID, existing.changeDetectorRef);\n\n // Update agent run from map\n instance.agentRun = this.agentRunMap.get(message.ID) || null;\n\n // Update ratings from map\n instance.ratings = this.ratingsMap.get(message.ID);\n\n // Update attachments from map\n instance.attachments = this.attachmentsMap.get(message.ID) || [];\n\n // Manually trigger change detection in child component when message status changes\n // This is necessary because we're using OnPush change detection and direct property assignment\n // doesn't trigger ngOnChanges (only reference changes do)\n if (previousMessage && previousMessage.Status !== message.Status) {\n // Use ComponentRef.changeDetectorRef to force update on dynamic child\n existing.changeDetectorRef.markForCheck();\n }\n } else {\n // Create new component\n const componentRef = this.messageContainerRef.createComponent(MessageItemComponent);\n const instance = componentRef.instance;\n\n // Set inputs\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); // Mark last message\n\n // Surface ALL distinct artifacts on this message (latest version each).\n this.applyArtifactsToInstance(instance, message.ID, componentRef.changeDetectorRef);\n\n // Pass agent run from map (loaded once per conversation)\n instance.agentRun = this.agentRunMap.get(message.ID) || null;\n\n // Pass ratings from map (parsed once per conversation)\n instance.ratings = this.ratingsMap.get(message.ID);\n\n // Pass attachments from map\n instance.attachments = this.attachmentsMap.get(message.ID) || [];\n\n // Subscribe to outputs\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\n // Handle artifact actions if the output exists\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 // Store reference\n this._renderedMessages.set(key, componentRef);\n\n // Store reference on the message entity for later access (like skip-chat pattern)\n (message as any)._componentRef = componentRef;\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 /**\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 componentRef = this._renderedMessages.get(key);\n if (componentRef) {\n componentRef.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 <div class=\"empty-state\">\n <i class=\"fas fa-comments\"></i>\n <p>No messages yet. Start a conversation!</p>\n </div>\n }\n</div>"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact-grouping utilities for rendering a message's attached artifacts.
|
|
3
|
+
*
|
|
4
|
+
* A single conversation message can carry more than one DISTINCT artifact — for
|
|
5
|
+
* example a research report PLUS a standalone generated infographic. The UI must
|
|
6
|
+
* surface all of them. At the same time, a single artifact can have multiple
|
|
7
|
+
* versions, and only the latest should render. This helper reconciles both.
|
|
8
|
+
*/
|
|
9
|
+
/** Minimal shape needed to dedupe artifacts — satisfied by `LazyArtifactInfo`. */
|
|
10
|
+
export interface DistinctArtifactKey {
|
|
11
|
+
artifactId: string;
|
|
12
|
+
versionNumber: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Collapses a message's artifact list to one entry per distinct `artifactId`,
|
|
16
|
+
* keeping the highest `versionNumber` for each. Distinct artifacts are all
|
|
17
|
+
* retained (so a report and an image both surface); multiple versions of the
|
|
18
|
+
* SAME artifact collapse to the latest. Input order of distinct artifacts is
|
|
19
|
+
* preserved (first-seen wins for ordering).
|
|
20
|
+
*/
|
|
21
|
+
export declare function selectDistinctLatestArtifacts<T extends DistinctArtifactKey>(list: readonly T[]): T[];
|
|
22
|
+
//# sourceMappingURL=distinct-artifacts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distinct-artifacts.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/distinct-artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,CAAC,SAAS,mBAAmB,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CASpG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact-grouping utilities for rendering a message's attached artifacts.
|
|
3
|
+
*
|
|
4
|
+
* A single conversation message can carry more than one DISTINCT artifact — for
|
|
5
|
+
* example a research report PLUS a standalone generated infographic. The UI must
|
|
6
|
+
* surface all of them. At the same time, a single artifact can have multiple
|
|
7
|
+
* versions, and only the latest should render. This helper reconciles both.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Collapses a message's artifact list to one entry per distinct `artifactId`,
|
|
11
|
+
* keeping the highest `versionNumber` for each. Distinct artifacts are all
|
|
12
|
+
* retained (so a report and an image both surface); multiple versions of the
|
|
13
|
+
* SAME artifact collapse to the latest. Input order of distinct artifacts is
|
|
14
|
+
* preserved (first-seen wins for ordering).
|
|
15
|
+
*/
|
|
16
|
+
export function selectDistinctLatestArtifacts(list) {
|
|
17
|
+
const latestByArtifact = new Map();
|
|
18
|
+
for (const info of list) {
|
|
19
|
+
const existing = latestByArtifact.get(info.artifactId);
|
|
20
|
+
if (!existing || info.versionNumber > existing.versionNumber) {
|
|
21
|
+
latestByArtifact.set(info.artifactId, info);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return Array.from(latestByArtifact.values());
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=distinct-artifacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distinct-artifacts.js","sourceRoot":"","sources":["../../../src/lib/utils/distinct-artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH;;;;;;GAMG;AACH,MAAM,UAAU,6BAA6B,CAAgC,IAAkB;IAC7F,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAa,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC,aAAa,EAAE,CAAC;YAC7D,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;AAC/C,CAAC","sourcesContent":["/**\n * Artifact-grouping utilities for rendering a message's attached artifacts.\n *\n * A single conversation message can carry more than one DISTINCT artifact — for\n * example a research report PLUS a standalone generated infographic. The UI must\n * surface all of them. At the same time, a single artifact can have multiple\n * versions, and only the latest should render. This helper reconciles both.\n */\n\n/** Minimal shape needed to dedupe artifacts — satisfied by `LazyArtifactInfo`. */\nexport interface DistinctArtifactKey {\n artifactId: string;\n versionNumber: number;\n}\n\n/**\n * Collapses a message's artifact list to one entry per distinct `artifactId`,\n * keeping the highest `versionNumber` for each. Distinct artifacts are all\n * retained (so a report and an image both surface); multiple versions of the\n * SAME artifact collapse to the latest. Input order of distinct artifacts is\n * preserved (first-seen wins for ordering).\n */\nexport function selectDistinctLatestArtifacts<T extends DistinctArtifactKey>(list: readonly T[]): T[] {\n const latestByArtifact = new Map<string, T>();\n for (const info of list) {\n const existing = latestByArtifact.get(info.artifactId);\n if (!existing || info.versionNumber > existing.versionNumber) {\n latestByArtifact.set(info.artifactId, info);\n }\n }\n return Array.from(latestByArtifact.values());\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-conversations",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.40.0",
|
|
4
4
|
"description": "MemberJunction: Conversation, Collection, and Artifact management components for any Angular application",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -31,28 +31,28 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@angular/animations": "21.1.3",
|
|
33
33
|
"@angular/cdk": "21.1.3",
|
|
34
|
-
"@memberjunction/ai": "5.
|
|
35
|
-
"@memberjunction/ai-agent-client": "5.
|
|
36
|
-
"@memberjunction/ai-core-plus": "5.
|
|
37
|
-
"@memberjunction/ai-engine-base": "5.
|
|
38
|
-
"@memberjunction/core": "5.
|
|
39
|
-
"@memberjunction/core-entities": "5.
|
|
40
|
-
"@memberjunction/global": "5.
|
|
41
|
-
"@memberjunction/graphql-dataprovider": "5.
|
|
42
|
-
"@memberjunction/interactive-component-types": "5.
|
|
43
|
-
"@memberjunction/ng-agent-client": "5.
|
|
44
|
-
"@memberjunction/ng-artifacts": "5.
|
|
45
|
-
"@memberjunction/ng-base-types": "5.
|
|
46
|
-
"@memberjunction/ng-code-editor": "5.
|
|
47
|
-
"@memberjunction/ng-container-directives": "5.
|
|
48
|
-
"@memberjunction/ng-forms": "5.
|
|
49
|
-
"@memberjunction/ng-markdown": "5.
|
|
50
|
-
"@memberjunction/ng-notifications": "5.
|
|
51
|
-
"@memberjunction/ng-resource-permissions": "5.
|
|
52
|
-
"@memberjunction/ng-shared-generic": "5.
|
|
53
|
-
"@memberjunction/ng-tasks": "5.
|
|
54
|
-
"@memberjunction/ng-testing": "5.
|
|
55
|
-
"@memberjunction/ng-ui-components": "5.
|
|
34
|
+
"@memberjunction/ai": "5.40.0",
|
|
35
|
+
"@memberjunction/ai-agent-client": "5.40.0",
|
|
36
|
+
"@memberjunction/ai-core-plus": "5.40.0",
|
|
37
|
+
"@memberjunction/ai-engine-base": "5.40.0",
|
|
38
|
+
"@memberjunction/core": "5.40.0",
|
|
39
|
+
"@memberjunction/core-entities": "5.40.0",
|
|
40
|
+
"@memberjunction/global": "5.40.0",
|
|
41
|
+
"@memberjunction/graphql-dataprovider": "5.40.0",
|
|
42
|
+
"@memberjunction/interactive-component-types": "5.40.0",
|
|
43
|
+
"@memberjunction/ng-agent-client": "5.40.0",
|
|
44
|
+
"@memberjunction/ng-artifacts": "5.40.0",
|
|
45
|
+
"@memberjunction/ng-base-types": "5.40.0",
|
|
46
|
+
"@memberjunction/ng-code-editor": "5.40.0",
|
|
47
|
+
"@memberjunction/ng-container-directives": "5.40.0",
|
|
48
|
+
"@memberjunction/ng-forms": "5.40.0",
|
|
49
|
+
"@memberjunction/ng-markdown": "5.40.0",
|
|
50
|
+
"@memberjunction/ng-notifications": "5.40.0",
|
|
51
|
+
"@memberjunction/ng-resource-permissions": "5.40.0",
|
|
52
|
+
"@memberjunction/ng-shared-generic": "5.40.0",
|
|
53
|
+
"@memberjunction/ng-tasks": "5.40.0",
|
|
54
|
+
"@memberjunction/ng-testing": "5.40.0",
|
|
55
|
+
"@memberjunction/ng-ui-components": "5.40.0",
|
|
56
56
|
"rxjs": "^7.8.2",
|
|
57
57
|
"tslib": "^2.8.1"
|
|
58
58
|
},
|