@copilotz/chat-ui 0.9.44 → 0.9.45

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/index.js CHANGED
@@ -840,6 +840,10 @@ var hasRenderableAssistantBody = (message) => {
840
840
  if (Array.isArray(message.attachments) && message.attachments.length > 0) return true;
841
841
  return Boolean(message.activity?.items.length);
842
842
  };
843
+ var filterActivity = (message, placement) => {
844
+ const items = message.activity?.items.filter((item) => placement === "after-content" ? item.kind === "tool" : item.kind !== "tool") ?? [];
845
+ return items.length > 0 ? { items } : void 0;
846
+ };
843
847
  var defaultMarkdownComponents = {
844
848
  code: ({ node, className, children, ...props }) => {
845
849
  const inline = props.inline;
@@ -1106,6 +1110,7 @@ var MediaRenderer = memo2(function MediaRenderer2({ attachment }) {
1106
1110
  });
1107
1111
  var arePropsEqual = (prevProps, nextProps) => {
1108
1112
  if (prevProps.message !== nextProps.message) return false;
1113
+ if (prevProps.fragments !== nextProps.fragments) return false;
1109
1114
  if (prevProps.isUser !== nextProps.isUser) return false;
1110
1115
  if (prevProps.userAvatar !== nextProps.userAvatar) return false;
1111
1116
  if (prevProps.userName !== nextProps.userName) return false;
@@ -1135,6 +1140,7 @@ var arePropsEqual = (prevProps, nextProps) => {
1135
1140
  };
1136
1141
  var Message = memo2(({
1137
1142
  message,
1143
+ fragments,
1138
1144
  isUser,
1139
1145
  userAvatar,
1140
1146
  userName = "Voc\xEA",
@@ -1167,12 +1173,15 @@ var Message = memo2(({
1167
1173
  const [showActions, setShowActions] = useState2(false);
1168
1174
  const [actionsFocused, setActionsFocused] = useState2(false);
1169
1175
  const [copied, setCopied] = useState2(false);
1176
+ const sourceMessages = fragments?.length ? fragments : [message];
1177
+ const headerMessage = sourceMessages[0];
1178
+ const groupContent = sourceMessages.map((sourceMessage) => sourceMessage.content.trim()).filter(Boolean).join("\n\n");
1170
1179
  const messageIsUser = isUser ?? message.role === "user";
1171
- if (!hasRenderableAssistantBody(message)) {
1180
+ if (!sourceMessages.some(hasRenderableAssistantBody)) {
1172
1181
  return null;
1173
1182
  }
1174
1183
  const senderDisplay = resolveMessageSenderDisplay({
1175
- sender: message.sender,
1184
+ sender: headerMessage.sender,
1176
1185
  fallbackName: messageIsUser ? userName : assistantName,
1177
1186
  fallbackAvatar: messageIsUser ? void 0 : assistantAvatar,
1178
1187
  fallbackAvatarUrl: messageIsUser ? userAvatar : void 0,
@@ -1182,17 +1191,17 @@ var Message = memo2(({
1182
1191
  const normalizedPreviewChars = Math.max(longMessagePreviewChars, 1);
1183
1192
  const normalizedChunkChars = Math.max(longMessageChunkChars, 1);
1184
1193
  const previewOverride = typeof message.metadata?.previewContent === "string" ? message.metadata.previewContent : void 0;
1185
- const canCollapseMessage = collapseLongMessages && !message.isStreaming && message.content.length > normalizedPreviewChars && (!collapseLongMessagesForUserOnly || messageIsUser);
1194
+ const canCollapseMessage = collapseLongMessages && sourceMessages.length === 1 && !sourceMessages.some((sourceMessage) => sourceMessage.isStreaming) && groupContent.length > normalizedPreviewChars && (!collapseLongMessagesForUserOnly || messageIsUser);
1186
1195
  const isCollapsed = canCollapseMessage && !isExpanded;
1187
- const contentToRender = isCollapsed ? getCollapsedPreview(message.content, normalizedPreviewChars, previewOverride) : message.content;
1196
+ const contentToRender = isCollapsed ? getCollapsedPreview(groupContent, normalizedPreviewChars, previewOverride) : groupContent;
1188
1197
  const shouldRenderMarkdown = !isCollapsed && (!messageIsUser || renderUserMarkdown);
1189
1198
  const horizontalOffsetClass = showAvatar ? messageIsUser ? compactMode ? "mr-9" : "mr-11" : compactMode ? "ml-9" : "ml-11" : "";
1190
1199
  const handleCopy = async () => {
1191
1200
  try {
1192
- await navigator.clipboard.writeText(message.content);
1201
+ await navigator.clipboard.writeText(groupContent);
1193
1202
  setCopied(true);
1194
1203
  setTimeout(() => setCopied(false), 2e3);
1195
- onAction?.({ action: "copy", messageId: message.id, content: message.content });
1204
+ onAction?.({ action: "copy", messageId: message.id, content: groupContent });
1196
1205
  } catch (error) {
1197
1206
  console.error("Failed to copy message:", error);
1198
1207
  }
@@ -1238,7 +1247,7 @@ var Message = memo2(({
1238
1247
  showAvatar && /* @__PURE__ */ jsx9("div", { className: `flex-shrink-0 ${compactMode ? "mt-1" : "mt-0"}`, children: /* @__PURE__ */ jsx9(
1239
1248
  MessageSenderAvatar,
1240
1249
  {
1241
- sender: message.sender,
1250
+ sender: headerMessage.sender,
1242
1251
  fallbackName: messageIsUser ? userName : assistantName,
1243
1252
  fallbackAvatar: messageIsUser ? void 0 : assistantAvatar,
1244
1253
  fallbackAvatarUrl: messageIsUser ? userAvatar : void 0,
@@ -1254,8 +1263,8 @@ var Message = memo2(({
1254
1263
  children: senderDisplay.name
1255
1264
  }
1256
1265
  ),
1257
- showTimestamp && /* @__PURE__ */ jsx9("span", { className: "text-xs text-muted-foreground", children: formatTime(message.timestamp) }),
1258
- message.isEdited && /* @__PURE__ */ jsx9(Badge, { variant: "outline", className: "text-xs", children: "editado" })
1266
+ showTimestamp && /* @__PURE__ */ jsx9("span", { className: "text-xs text-muted-foreground", children: formatTime(headerMessage.timestamp) }),
1267
+ sourceMessages.some((sourceMessage) => sourceMessage.isEdited) && /* @__PURE__ */ jsx9(Badge, { variant: "outline", className: "text-xs", children: "editado" })
1259
1268
  ] })
1260
1269
  ] }),
1261
1270
  /* @__PURE__ */ jsxs5("div", { className: `flex-1 min-w-0 ${messageIsUser ? "text-right" : "text-left"} ${horizontalOffsetClass}`, children: [
@@ -1300,25 +1309,42 @@ var Message = memo2(({
1300
1309
  )
1301
1310
  ] })
1302
1311
  ] }) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
1303
- !messageIsUser && /* @__PURE__ */ jsx9(
1304
- AssistantActivity,
1305
- {
1306
- activity: message.activity,
1307
- showActivity,
1308
- showActivityDetails,
1309
- labels
1310
- }
1311
- ),
1312
- /* @__PURE__ */ jsx9(
1313
- StreamingText,
1314
- {
1315
- content: contentToRender,
1316
- isStreaming: message.isStreaming,
1317
- renderMarkdown: shouldRenderMarkdown,
1318
- markdown,
1319
- plainTextChunkChars: normalizedChunkChars
1320
- }
1321
- ),
1312
+ sourceMessages.map((sourceMessage) => {
1313
+ const beforeContentActivity = filterActivity(sourceMessage, "before-content");
1314
+ const afterContentActivity = filterActivity(sourceMessage, "after-content");
1315
+ const fragmentContent = sourceMessages.length === 1 ? contentToRender : sourceMessage.content;
1316
+ return /* @__PURE__ */ jsxs5(React3.Fragment, { children: [
1317
+ !messageIsUser && /* @__PURE__ */ jsx9(
1318
+ AssistantActivity,
1319
+ {
1320
+ activity: beforeContentActivity,
1321
+ showActivity,
1322
+ showActivityDetails,
1323
+ labels
1324
+ }
1325
+ ),
1326
+ fragmentContent.length > 0 && /* @__PURE__ */ jsx9(
1327
+ StreamingText,
1328
+ {
1329
+ content: fragmentContent,
1330
+ isStreaming: sourceMessage.isStreaming,
1331
+ renderMarkdown: shouldRenderMarkdown,
1332
+ markdown,
1333
+ plainTextChunkChars: normalizedChunkChars
1334
+ }
1335
+ ),
1336
+ !messageIsUser && /* @__PURE__ */ jsx9(
1337
+ AssistantActivity,
1338
+ {
1339
+ activity: afterContentActivity,
1340
+ showActivity,
1341
+ showActivityDetails,
1342
+ labels
1343
+ }
1344
+ ),
1345
+ sourceMessage.attachments && sourceMessage.attachments.length > 0 && /* @__PURE__ */ jsx9("div", { className: "mt-3 space-y-2", children: sourceMessage.attachments.map((attachment, index) => /* @__PURE__ */ jsx9(MediaRenderer, { attachment }, `${sourceMessage.id}:${index}`)) })
1346
+ ] }, sourceMessage.id);
1347
+ }),
1322
1348
  canCollapseMessage && /* @__PURE__ */ jsx9("div", { className: "mt-3", children: /* @__PURE__ */ jsx9(
1323
1349
  Button,
1324
1350
  {
@@ -1330,8 +1356,7 @@ var Message = memo2(({
1330
1356
  onClick: handleToggleExpanded,
1331
1357
  children: isCollapsed ? showMoreLabel : showLessLabel
1332
1358
  }
1333
- ) }),
1334
- message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsx9("div", { className: "mt-3 space-y-2", children: message.attachments.map((attachment, index) => /* @__PURE__ */ jsx9(MediaRenderer, { attachment }, index)) })
1359
+ ) })
1335
1360
  ] }) }),
1336
1361
  !isEditing && (enableCopy || canEdit) && /* @__PURE__ */ jsxs5(
1337
1362
  "div",
@@ -5970,72 +5995,27 @@ var getMessageSpeakerKey = (message) => {
5970
5995
  }
5971
5996
  return message.role;
5972
5997
  };
5973
- var getAssistantSpeakerTokens = (message) => {
5974
- if (!message || message.role !== "assistant") return [];
5975
- if (message.sender) {
5976
- return Array.from(new Set([
5977
- `${message.sender.type}:${message.sender.id}`,
5978
- message.sender.agentId ? `agent:${message.sender.agentId}` : "",
5979
- message.sender.externalId ? `external:${message.sender.externalId}` : ""
5980
- ].filter(Boolean).map((value) => value.toLowerCase())));
5981
- }
5982
- return ["assistant"];
5983
- };
5984
- var hasAssistantContent = (message) => message.role === "assistant" && (message.content.trim().length > 0 || Boolean(message.attachments?.length));
5985
- var hasActivity = (message) => Boolean(message.activity?.items.length);
5986
5998
  var canGroupMessages = (previous, next) => {
5987
5999
  if (previous.role !== next.role) {
5988
6000
  return false;
5989
6001
  }
5990
6002
  if (previous.role !== "assistant") {
5991
- return getMessageSpeakerKey(previous) === getMessageSpeakerKey(next);
5992
- }
5993
- const previousTokens = getAssistantSpeakerTokens(previous);
5994
- const nextTokens = getAssistantSpeakerTokens(next);
5995
- if (!previousTokens.some((token) => nextTokens.includes(token))) return false;
5996
- return !(hasAssistantContent(previous) && hasActivity(next));
5997
- };
5998
- var mergeGroupActivity = (messages) => {
5999
- const merged = /* @__PURE__ */ new Map();
6000
- for (const activity of messages.map((message) => message.activity).filter((activity2) => Boolean(activity2))) {
6001
- for (const item of activity.items) merged.set(item.id, item);
6002
- }
6003
- return merged.size > 0 ? { items: Array.from(merged.values()) } : void 0;
6004
- };
6005
- var mergeMessageGroup = (messages) => {
6006
- if (messages.length === 1) {
6007
- return messages[0];
6003
+ return false;
6008
6004
  }
6009
- const firstMessage = messages[0];
6010
- const lastMessage = messages[messages.length - 1];
6011
- const content = messages.map((message) => message.content.trim()).filter((value) => value.length > 0).join("\n\n");
6012
- const attachments = messages.flatMap((message) => message.attachments ?? []);
6013
- return {
6014
- ...lastMessage,
6015
- id: lastMessage.id,
6016
- content,
6017
- timestamp: firstMessage.timestamp,
6018
- attachments: attachments.length > 0 ? attachments : void 0,
6019
- isStreaming: lastMessage.isStreaming,
6020
- isComplete: lastMessage.isComplete,
6021
- isEdited: messages.some((message) => message.isEdited),
6022
- originalContent: void 0,
6023
- editedAt: lastMessage.editedAt,
6024
- activity: mergeGroupActivity(messages),
6025
- sender: lastMessage.sender ?? firstMessage.sender,
6026
- metadata: lastMessage.metadata
6027
- };
6005
+ return getMessageSpeakerKey(previous) === getMessageSpeakerKey(next);
6028
6006
  };
6029
6007
  var groupMessagesForRender = (messages) => {
6030
6008
  if (messages.length === 0) return [];
6031
6009
  const groups = [];
6032
6010
  let currentGroup = [messages[0]];
6033
6011
  const flushGroup = () => {
6034
- const mergedMessage = mergeMessageGroup(currentGroup);
6012
+ const firstMessage = currentGroup[0];
6013
+ const lastMessage = currentGroup[currentGroup.length - 1];
6035
6014
  groups.push({
6036
- id: mergedMessage.id,
6037
- message: mergedMessage,
6038
- suggestionMessageId: currentGroup[currentGroup.length - 1].id
6015
+ id: firstMessage.id,
6016
+ messages: currentGroup,
6017
+ primaryMessage: lastMessage,
6018
+ suggestionMessageId: lastMessage.id
6039
6019
  });
6040
6020
  };
6041
6021
  for (let index = 1; index < messages.length; index++) {
@@ -6686,7 +6666,7 @@ var ChatUI = ({
6686
6666
  },
6687
6667
  children: virtualizer.getVirtualItems().map((virtualRow) => {
6688
6668
  const group = groupedMessages[virtualRow.index];
6689
- const message = group.message;
6669
+ const message = group.primaryMessage;
6690
6670
  return /* @__PURE__ */ jsx28(
6691
6671
  "div",
6692
6672
  {
@@ -6708,6 +6688,7 @@ var ChatUI = ({
6708
6688
  Message,
6709
6689
  {
6710
6690
  message,
6691
+ fragments: group.messages,
6711
6692
  ...messageProps,
6712
6693
  isExpanded: Boolean(
6713
6694
  expandedMessageIds[message.id]