cevro-messenger-sdk 0.1.19 → 0.1.21

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/esm/index.js CHANGED
@@ -5065,10 +5065,13 @@ class MessageList {
5065
5065
  __publicField(this, "element", null);
5066
5066
  __publicField(this, "messagesContainer", null);
5067
5067
  __publicField(this, "messages", /* @__PURE__ */ new Map());
5068
+ __publicField(this, "messageData", /* @__PURE__ */ new Map());
5068
5069
  __publicField(this, "onQuickAction", null);
5069
5070
  __publicField(this, "autoScroll", true);
5070
5071
  __publicField(this, "closedMessageElement", null);
5071
5072
  __publicField(this, "currentAgent", null);
5073
+ __publicField(this, "brandLogoUrl");
5074
+ __publicField(this, "firstAdminMessageId", null);
5072
5075
  __publicField(this, "lastMessageBody");
5073
5076
  __publicField(this, "lastMessageAt");
5074
5077
  __publicField(this, "onScrollTopCallback", null);
@@ -5110,6 +5113,26 @@ class MessageList {
5110
5113
  setAgent(agent) {
5111
5114
  this.currentAgent = agent;
5112
5115
  }
5116
+ /**
5117
+ * Set the brand logo shown as the avatar of the conversation's first message only
5118
+ */
5119
+ setBrandLogoUrl(url) {
5120
+ this.brandLogoUrl = url;
5121
+ this.refreshFirstAdminMessageAvatar();
5122
+ }
5123
+ /**
5124
+ * Re-render the message currently holding the first-admin-message slot, e.g.
5125
+ * after the brand logo changes mid-session. No-ops if none is tracked yet.
5126
+ */
5127
+ refreshFirstAdminMessageAvatar() {
5128
+ if (!this.firstAdminMessageId) return;
5129
+ const message = this.messageData.get(this.firstAdminMessageId);
5130
+ const el = this.messages.get(this.firstAdminMessageId);
5131
+ if (!message || !el) return;
5132
+ const rebuiltEl = this.createMessageElement(message, true);
5133
+ el.replaceWith(rebuiltEl);
5134
+ this.messages.set(this.firstAdminMessageId, rebuiltEl);
5135
+ }
5113
5136
  /**
5114
5137
  * Render the message list
5115
5138
  */
@@ -5188,7 +5211,12 @@ class MessageList {
5188
5211
  this.updateMessage(message);
5189
5212
  return;
5190
5213
  }
5191
- const messageEl = this.createMessageElement(message);
5214
+ this.messageData.set(message.id, message);
5215
+ const isFirst = message.inbound !== true && this.firstAdminMessageId === null;
5216
+ if (isFirst) {
5217
+ this.firstAdminMessageId = message.id;
5218
+ }
5219
+ const messageEl = this.createMessageElement(message, isFirst);
5192
5220
  if (animate) {
5193
5221
  messageEl.classList.add("cevro-message--animate");
5194
5222
  }
@@ -5209,11 +5237,28 @@ class MessageList {
5209
5237
  */
5210
5238
  prependMessages(messages) {
5211
5239
  if (!this.messagesContainer || messages.length === 0) return;
5212
- const anchor = this.messagesContainer.firstElementChild;
5240
+ let anchor = this.messagesContainer.firstElementChild;
5213
5241
  const anchorOffset = anchor ? anchor.offsetTop - this.messagesContainer.scrollTop : 0;
5242
+ const earliestAdminMessage = messages.find((m) => m.inbound !== true);
5243
+ if (earliestAdminMessage && earliestAdminMessage.id !== this.firstAdminMessageId) {
5244
+ const previousFirstId = this.firstAdminMessageId;
5245
+ this.firstAdminMessageId = earliestAdminMessage.id;
5246
+ const previousFirstMessage = previousFirstId ? this.messageData.get(previousFirstId) : void 0;
5247
+ const previousFirstEl = previousFirstId ? this.messages.get(previousFirstId) : void 0;
5248
+ if (previousFirstId && previousFirstMessage && previousFirstEl) {
5249
+ const rebuiltEl = this.createMessageElement(previousFirstMessage, false);
5250
+ previousFirstEl.replaceWith(rebuiltEl);
5251
+ this.messages.set(previousFirstId, rebuiltEl);
5252
+ if (anchor === previousFirstEl) {
5253
+ anchor = rebuiltEl;
5254
+ }
5255
+ }
5256
+ }
5214
5257
  for (const message of messages) {
5215
5258
  if (this.messages.has(message.id)) continue;
5216
- const messageEl = this.createMessageElement(message);
5259
+ this.messageData.set(message.id, message);
5260
+ const isFirst = message.id === this.firstAdminMessageId;
5261
+ const messageEl = this.createMessageElement(message, isFirst);
5217
5262
  this.messagesContainer.insertBefore(messageEl, anchor);
5218
5263
  this.messages.set(message.id, messageEl);
5219
5264
  }
@@ -5240,10 +5285,16 @@ class MessageList {
5240
5285
  const lookupId = oldId || message.id;
5241
5286
  const existingEl = this.messages.get(lookupId);
5242
5287
  if (!existingEl || !this.messagesContainer) return;
5243
- const newEl = this.createMessageElement(message);
5288
+ if (this.firstAdminMessageId === lookupId) {
5289
+ this.firstAdminMessageId = message.id;
5290
+ }
5291
+ const isFirst = this.firstAdminMessageId === message.id;
5292
+ this.messageData.set(message.id, message);
5293
+ const newEl = this.createMessageElement(message, isFirst);
5244
5294
  this.messagesContainer.replaceChild(newEl, existingEl);
5245
5295
  if (oldId && oldId !== message.id) {
5246
5296
  this.messages.delete(oldId);
5297
+ this.messageData.delete(oldId);
5247
5298
  }
5248
5299
  this.messages.set(message.id, newEl);
5249
5300
  }
@@ -5270,20 +5321,44 @@ class MessageList {
5270
5321
  if (!messageEl) return;
5271
5322
  messageEl.remove();
5272
5323
  this.messages.delete(messageId);
5324
+ this.messageData.delete(messageId);
5325
+ if (this.firstAdminMessageId === messageId) {
5326
+ this.firstAdminMessageId = null;
5327
+ this.promoteNextAdminMessageToFirst();
5328
+ }
5329
+ }
5330
+ /**
5331
+ * Give the first-message treatment to the earliest remaining admin message
5332
+ * (by DOM position), e.g. after the previous holder is removed.
5333
+ */
5334
+ promoteNextAdminMessageToFirst() {
5335
+ if (!this.messagesContainer) return;
5336
+ for (const child of Array.from(this.messagesContainer.children)) {
5337
+ const id = child.dataset.messageId;
5338
+ if (!id) continue;
5339
+ const message = this.messageData.get(id);
5340
+ if (!message || message.inbound === true) continue;
5341
+ this.firstAdminMessageId = id;
5342
+ const rebuiltEl = this.createMessageElement(message, true);
5343
+ child.replaceWith(rebuiltEl);
5344
+ this.messages.set(id, rebuiltEl);
5345
+ return;
5346
+ }
5273
5347
  }
5274
5348
  /**
5275
5349
  * Create message element
5276
5350
  */
5277
- createMessageElement(message) {
5278
- var _a, _b, _c, _d, _e;
5351
+ createMessageElement(message, isFirst = false) {
5352
+ var _a, _b, _c, _d, _e, _f;
5279
5353
  const isMyMessage = message.inbound === true;
5280
5354
  const wrapper = createElement("div", {
5281
5355
  className: `cevro-message ${isMyMessage ? "cevro-message--outbound" : "cevro-message--inbound"}`,
5282
5356
  "data-message-id": message.id,
5283
5357
  "data-testid": `message-${message.id}`
5284
5358
  });
5359
+ const showBrandLogo = !isMyMessage && isFirst && !!this.brandLogoUrl && isValidUrl(this.brandLogoUrl);
5360
+ const isHuman = !isMyMessage && !showBrandLogo && message.senderType === "human";
5285
5361
  if (!isMyMessage) {
5286
- const isHuman = message.senderType === "human";
5287
5362
  const avatar = createElement("div", {
5288
5363
  className: `cevro-message__avatar${isHuman ? " cevro-message__avatar--human" : ""}`
5289
5364
  });
@@ -5295,22 +5370,26 @@ class MessageList {
5295
5370
  });
5296
5371
  initialsEl.textContent = initials;
5297
5372
  avatar.appendChild(initialsEl);
5298
- } else if (((_a = this.currentAgent) == null ? void 0 : _a.avatarUrl) && isValidUrl(this.currentAgent.avatarUrl)) {
5299
- const img = createElement("img", {
5300
- className: "cevro-message__avatar-img"
5301
- });
5302
- img.src = this.currentAgent.avatarUrl;
5303
- img.alt = this.currentAgent.name || "Agent";
5304
- avatar.appendChild(img);
5305
5373
  } else {
5306
- avatar.innerHTML = this.createBotAvatarSvg();
5374
+ const validAgentAvatarUrl = ((_a = this.currentAgent) == null ? void 0 : _a.avatarUrl) && isValidUrl(this.currentAgent.avatarUrl) ? this.currentAgent.avatarUrl : null;
5375
+ const avatarSrc = showBrandLogo ? this.brandLogoUrl : validAgentAvatarUrl;
5376
+ if (avatarSrc) {
5377
+ const img = createElement("img", {
5378
+ className: "cevro-message__avatar-img",
5379
+ src: avatarSrc,
5380
+ alt: showBrandLogo ? "Logo" : ((_b = this.currentAgent) == null ? void 0 : _b.name) || "Agent"
5381
+ });
5382
+ avatar.appendChild(img);
5383
+ } else {
5384
+ avatar.innerHTML = this.createBotAvatarSvg();
5385
+ }
5307
5386
  }
5308
5387
  wrapper.appendChild(avatar);
5309
5388
  }
5310
5389
  const contentWrapper = createElement("div", {
5311
5390
  className: "cevro-message__content-wrapper"
5312
5391
  });
5313
- if (!isMyMessage && message.senderType === "human" && message.agentName) {
5392
+ if (isHuman && message.agentName) {
5314
5393
  const senderLabel = createElement("div", {
5315
5394
  className: "cevro-message__sender-name"
5316
5395
  });
@@ -5320,7 +5399,7 @@ class MessageList {
5320
5399
  const bubble = createElement("div", {
5321
5400
  className: "cevro-message__bubble"
5322
5401
  });
5323
- const isImage = message.type === "image" || ((_b = message.attachment) == null ? void 0 : _b.category) === "image";
5402
+ const isImage = message.type === "image" || ((_c = message.attachment) == null ? void 0 : _c.category) === "image";
5324
5403
  if (isImage && message.mediaUrl && isValidUrl(message.mediaUrl)) {
5325
5404
  const imageContainer = createElement("div", {
5326
5405
  className: "cevro-message__image-container"
@@ -5329,7 +5408,7 @@ class MessageList {
5329
5408
  className: "cevro-message__image"
5330
5409
  });
5331
5410
  img.src = message.mediaUrl;
5332
- img.alt = ((_c = message.attachment) == null ? void 0 : _c.filename) || "Image";
5411
+ img.alt = ((_d = message.attachment) == null ? void 0 : _d.filename) || "Image";
5333
5412
  img.loading = "lazy";
5334
5413
  imageContainer.appendChild(img);
5335
5414
  if (message.status === "sending") {
@@ -5361,7 +5440,7 @@ class MessageList {
5361
5440
  bubble.appendChild(textContent);
5362
5441
  }
5363
5442
  contentWrapper.appendChild(bubble);
5364
- if ((_e = (_d = message.richContent) == null ? void 0 : _d.quickActions) == null ? void 0 : _e.length) {
5443
+ if ((_f = (_e = message.richContent) == null ? void 0 : _e.quickActions) == null ? void 0 : _f.length) {
5365
5444
  const actionsContainer = createElement("div", {
5366
5445
  className: "cevro-message__quick-actions"
5367
5446
  });
@@ -5628,6 +5707,8 @@ class MessageList {
5628
5707
  this.messagesContainer.innerHTML = "";
5629
5708
  }
5630
5709
  this.messages.clear();
5710
+ this.messageData.clear();
5711
+ this.firstAdminMessageId = null;
5631
5712
  this.lastMessageBody = void 0;
5632
5713
  this.lastMessageAt = void 0;
5633
5714
  this.autoScroll = true;
@@ -5687,6 +5768,8 @@ class MessageList {
5687
5768
  this.element = null;
5688
5769
  this.messagesContainer = null;
5689
5770
  this.messages.clear();
5771
+ this.messageData.clear();
5772
+ this.firstAdminMessageId = null;
5690
5773
  }
5691
5774
  }
5692
5775
  class ChatWindow {
@@ -6539,6 +6622,14 @@ class ChatWindow {
6539
6622
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
6540
6623
  return emailRegex.test(email);
6541
6624
  }
6625
+ /**
6626
+ * Set the brand logo shown as the avatar of the conversation's first message only.
6627
+ * Distinct from the header logo (`updateCustomLogo`) — callers decide independently
6628
+ * whether the first-message avatar should follow the header logo.
6629
+ */
6630
+ setBrandLogoUrl(logoUrl) {
6631
+ this.messageList.setBrandLogoUrl(logoUrl);
6632
+ }
6542
6633
  updateCustomLogo(logoUrl) {
6543
6634
  this.customLogoUrl = logoUrl;
6544
6635
  if (!this.logoElement) return;
@@ -6878,6 +6969,19 @@ class PastConversation {
6878
6969
  this.avatarUrl = url;
6879
6970
  this.renderAvatar();
6880
6971
  }
6972
+ /**
6973
+ * Set agent info for message avatars (the AI-persona photo fallback when
6974
+ * no brand logo applies)
6975
+ */
6976
+ setAgent(agent) {
6977
+ this.messageList.setAgent(agent);
6978
+ }
6979
+ /**
6980
+ * Set the brand logo shown as the avatar of the conversation's first message only
6981
+ */
6982
+ setBrandLogoUrl(url) {
6983
+ this.messageList.setBrandLogoUrl(url);
6984
+ }
6881
6985
  renderAvatar() {
6882
6986
  if (!this.avatarElement) return;
6883
6987
  if (this.avatarUrl) {
@@ -7173,6 +7277,7 @@ const _CevroMessenger = class _CevroMessenger {
7173
7277
  strings,
7174
7278
  this.widgetAppearance.customLogoUrl
7175
7279
  );
7280
+ this.chatWindow.setBrandLogoUrl(this.firstMessageBrandLogoUrl());
7176
7281
  this.container.appendChild(this.chatWindow.render());
7177
7282
  this.createConversationList(strings);
7178
7283
  this.createPastConversation(strings);
@@ -7389,6 +7494,7 @@ const _CevroMessenger = class _CevroMessenger {
7389
7494
  );
7390
7495
  const el = this.pastConversation.render();
7391
7496
  this.pastConversation.setAvatarUrl(this.widgetAppearance.customLogoUrl);
7497
+ this.pastConversation.setBrandLogoUrl(this.firstMessageBrandLogoUrl());
7392
7498
  el.style.display = "none";
7393
7499
  this.container.appendChild(el);
7394
7500
  }
@@ -7604,6 +7710,9 @@ const _CevroMessenger = class _CevroMessenger {
7604
7710
  if (result.agent && this.chatWindow) {
7605
7711
  this.chatWindow.setAgent(result.agent);
7606
7712
  }
7713
+ if (result.agent && this.pastConversation) {
7714
+ this.pastConversation.setAgent(result.agent);
7715
+ }
7607
7716
  if (result.typingConfig) {
7608
7717
  this.typingConfig = result.typingConfig;
7609
7718
  }
@@ -7911,6 +8020,15 @@ const _CevroMessenger = class _CevroMessenger {
7911
8020
  (_a = this.launcher) == null ? void 0 : _a.setUnreadCount(count);
7912
8021
  this.events.emit("unread_count_change", { count });
7913
8022
  }
8023
+ /**
8024
+ * The brand logo pairs with the global, workspace-voiced welcome
8025
+ * (`instantGreeting`) — pairing it with the personalized "Hi, I'm
8026
+ * {agentName}" welcome would put a company avatar next to agent-attributed
8027
+ * text.
8028
+ */
8029
+ firstMessageBrandLogoUrl() {
8030
+ return this.widgetAppearance.instantGreeting ? this.widgetAppearance.customLogoUrl : void 0;
8031
+ }
7914
8032
  updateAppearance(config) {
7915
8033
  this.widgetAppearance = {
7916
8034
  ...this.widgetAppearance,
@@ -7928,6 +8046,12 @@ const _CevroMessenger = class _CevroMessenger {
7928
8046
  if ("customLogoUrl" in config && this.chatWindow) {
7929
8047
  this.chatWindow.updateCustomLogo(config.customLogoUrl);
7930
8048
  }
8049
+ if (("customLogoUrl" in config || "instantGreeting" in config) && this.chatWindow) {
8050
+ this.chatWindow.setBrandLogoUrl(this.firstMessageBrandLogoUrl());
8051
+ }
8052
+ if (("customLogoUrl" in config || "instantGreeting" in config) && this.pastConversation) {
8053
+ this.pastConversation.setBrandLogoUrl(this.firstMessageBrandLogoUrl());
8054
+ }
7931
8055
  this.events.emit("appearance:updated", this.widgetAppearance);
7932
8056
  }
7933
8057
  /**
@@ -8056,8 +8180,10 @@ const _CevroMessenger = class _CevroMessenger {
8056
8180
  * @param avatarUrl - Agent avatar URL (optional)
8057
8181
  */
8058
8182
  setPreviewAgent(name, avatarUrl) {
8183
+ var _a;
8059
8184
  if (!this.config.preview || !this.chatWindow) return;
8060
8185
  this.chatWindow.setAgent({ name, avatarUrl });
8186
+ (_a = this.pastConversation) == null ? void 0 : _a.setAgent({ name, avatarUrl });
8061
8187
  }
8062
8188
  /**
8063
8189
  * Start typing indicator cycle (5s on, 5s off, repeat)