@xinghunm/ai-chat 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ __export(src_exports, {
33
33
  AiChat: () => AiChat,
34
34
  AiChatProvider: () => AiChatProvider,
35
35
  CHAT_AGENT_MODES: () => CHAT_AGENT_MODES,
36
+ CHAT_MESSAGE_RENDER_ORDERS: () => CHAT_MESSAGE_RENDER_ORDERS,
36
37
  ChatComposer: () => ChatComposer,
37
38
  ChatConversationList: () => ChatConversationList,
38
39
  ChatThread: () => ChatThread,
@@ -45,7 +46,7 @@ __export(src_exports, {
45
46
  module.exports = __toCommonJS(src_exports);
46
47
 
47
48
  // src/components/ai-chat/index.tsx
48
- var import_styled18 = __toESM(require("@emotion/styled"));
49
+ var import_styled17 = __toESM(require("@emotion/styled"));
49
50
  var import_compass_ui4 = require("@xinghunm/compass-ui");
50
51
 
51
52
  // src/components/ai-chat-provider/index.tsx
@@ -62,6 +63,7 @@ var import_vanilla = require("zustand/vanilla");
62
63
  // src/types/index.ts
63
64
  var CHAT_AGENT_MODES = ["ask", "plan", "agent"];
64
65
  var DEFAULT_CHAT_AGENT_MODE = "agent";
66
+ var CHAT_MESSAGE_RENDER_ORDERS = ["blocks-first", "timeline"];
65
67
  var DEFAULT_AI_CHAT_LABELS = {
66
68
  sendButton: "Send",
67
69
  stopButton: "Stop",
@@ -563,7 +565,14 @@ var createDefaultChatTransport = ({
563
565
  // src/components/ai-chat-provider/index.tsx
564
566
  var import_jsx_runtime = require("@emotion/react/jsx-runtime");
565
567
  var AiChatProvider = (props) => {
566
- const { defaultMode, labels, renderMessageBlock, enableImageAttachments = true, children } = props;
568
+ const {
569
+ defaultMode,
570
+ labels,
571
+ renderMessageBlock,
572
+ messageRenderOrder,
573
+ enableImageAttachments = true,
574
+ children
575
+ } = props;
567
576
  const [store] = (0, import_react2.useState)(
568
577
  () => createChatStore(defaultMode ? { preferredMode: defaultMode } : void 0)
569
578
  );
@@ -615,6 +624,7 @@ var AiChatProvider = (props) => {
615
624
  sendRef,
616
625
  retryRef,
617
626
  renderMessageBlock,
627
+ messageRenderOrder,
618
628
  transformStreamPacket: defaultTransformStreamPacket,
619
629
  enableImageAttachments
620
630
  }),
@@ -625,6 +635,7 @@ var AiChatProvider = (props) => {
625
635
  defaultTransformStreamPacket,
626
636
  enableImageAttachments,
627
637
  labels,
638
+ messageRenderOrder,
628
639
  renderMessageBlock,
629
640
  sendRef,
630
641
  retryRef,
@@ -637,7 +648,7 @@ var AiChatProvider = (props) => {
637
648
 
638
649
  // src/components/chat-thread/index.tsx
639
650
  var import_react11 = require("react");
640
- var import_styled10 = __toESM(require("@emotion/styled"));
651
+ var import_styled9 = __toESM(require("@emotion/styled"));
641
652
 
642
653
  // src/context/use-chat-context.ts
643
654
  var import_react3 = require("react");
@@ -655,27 +666,11 @@ var useChatStore = (selector) => {
655
666
 
656
667
  // src/components/chat-thread/lib/chat-thread.ts
657
668
  var CHAT_THREAD_SCROLL_TOP_GAP = 16;
658
- var findLatestUserMessageId = (historyMessages) => {
659
- for (let index = historyMessages.length - 1; index >= 0; index -= 1) {
660
- if (historyMessages[index]?.role === "user") {
661
- return historyMessages[index]?.id;
662
- }
663
- }
664
- return void 0;
665
- };
666
- var calculateChatThreadScrollSpacerHeight = ({
667
- containerClientHeight,
668
- containerScrollHeight,
669
- targetOffsetTop
670
- }) => Math.max(
671
- 0,
672
- targetOffsetTop - CHAT_THREAD_SCROLL_TOP_GAP - (containerScrollHeight - containerClientHeight)
673
- );
674
669
 
675
670
  // src/components/chat-thread/components/chat-message-item.tsx
676
- var import_react8 = require("react");
671
+ var import_react9 = require("react");
677
672
  var import_styled7 = __toESM(require("@emotion/styled"));
678
- var import_react9 = require("@emotion/react");
673
+ var import_react10 = require("@emotion/react");
679
674
  var import_react_markdown = __toESM(require("react-markdown"));
680
675
  var import_remark_gfm = __toESM(require("remark-gfm"));
681
676
  var import_remark_math = __toESM(require("remark-math"));
@@ -902,11 +897,319 @@ var useChatMessageReveal = (message) => {
902
897
  }, [batchedTargetUnitCount, displayedUnitCount, isAssistantStreaming, message.role]);
903
898
  const settledContent = isFreshBlockActive ? contentBlocks.slice(0, -1).join("\n\n") : displayedContent;
904
899
  const freshContent = isFreshBlockActive ? contentBlocks[contentBlocks.length - 1] ?? "" : "";
900
+ const displayedBlocks = isAssistantStreaming && contentBlocks.length > 1 ? contentBlocks.map((content, index) => ({
901
+ content,
902
+ tone: isFreshBlockActive && index === contentBlocks.length - 1 ? "fresh" : "settled"
903
+ })) : [
904
+ {
905
+ content: displayedContent,
906
+ tone: "settled"
907
+ }
908
+ ];
905
909
  return {
906
910
  isAssistantStreaming,
911
+ isFreshBlockActive,
907
912
  displayedContent,
908
913
  settledContent,
909
- freshContent
914
+ freshContent,
915
+ displayedBlocks
916
+ };
917
+ };
918
+
919
+ // src/components/chat-thread/hooks/use-timeline-block-anchors.ts
920
+ var import_react6 = require("react");
921
+
922
+ // src/components/chat-thread/lib/chat-message-timeline.ts
923
+ var stringifyTimelineKeyPart = (value) => {
924
+ if (value === null || value === void 0) {
925
+ return String(value);
926
+ }
927
+ if (Array.isArray(value)) {
928
+ return `[${value.map((item) => stringifyTimelineKeyPart(item)).join(",")}]`;
929
+ }
930
+ if (typeof value === "object") {
931
+ return `{${Object.entries(value).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)).map(([key, nestedValue]) => `${key}:${stringifyTimelineKeyPart(nestedValue)}`).join(",")}}`;
932
+ }
933
+ return String(value);
934
+ };
935
+ var getTimelineBlockKey = (block, index) => {
936
+ switch (block.type) {
937
+ case "markdown":
938
+ return null;
939
+ case "notice":
940
+ return `${index}:notice:${block.tone}:${block.text}`;
941
+ case "parameter_summary":
942
+ return `${index}:parameter_summary:${block.items.map((item) => `${item.label}:${item.value}:${item.fieldPath ?? ""}`).join("|")}`;
943
+ case "confirmation_card":
944
+ return `${index}:confirmation_card:${block.proposal.proposalId}`;
945
+ case "result_summary":
946
+ return `${index}:result_summary:${block.summary.taskId}:${block.summary.status}`;
947
+ case "questionnaire":
948
+ return `${index}:questionnaire:${block.questionnaire.questionnaireId}`;
949
+ case "custom":
950
+ return `${index}:custom:${block.kind}:${stringifyTimelineKeyPart(block.data)}`;
951
+ default:
952
+ return null;
953
+ }
954
+ };
955
+ var getTimelineConsumedText = (blocks) => blocks.filter(
956
+ (block) => block.type === "markdown"
957
+ ).map((block) => block.text).join("\n\n");
958
+ var getTimelineTextStream = (content, blocks) => {
959
+ const consumedText = getTimelineConsumedText(blocks);
960
+ if (consumedText.length > 0 && content.startsWith(consumedText)) {
961
+ return content.slice(consumedText.length);
962
+ }
963
+ return content;
964
+ };
965
+ var buildTimelineTextDisplay = (content, isAssistantStreaming, isFreshBlockActive = isAssistantStreaming) => {
966
+ const contentBlocks = splitMarkdownBlocks(content);
967
+ const settledContent = isAssistantStreaming && isFreshBlockActive && contentBlocks.length > 1 ? contentBlocks.slice(0, -1).join("\n\n") : content;
968
+ const freshContent = isAssistantStreaming && isFreshBlockActive && contentBlocks.length > 1 ? contentBlocks[contentBlocks.length - 1] ?? "" : "";
969
+ const displayedBlocks = contentBlocks.length > 1 ? contentBlocks.map((blockContent, index) => ({
970
+ content: blockContent,
971
+ tone: isAssistantStreaming && isFreshBlockActive && freshContent && index === contentBlocks.length - 1 ? "fresh" : "settled"
972
+ })) : [{ content, tone: "settled" }];
973
+ return {
974
+ settledContent,
975
+ freshContent,
976
+ displayedBlocks
977
+ };
978
+ };
979
+ var getTimelineDisplayUnitCount = (content) => splitMarkdownBlocks(content).reduce((count, block) => count + Array.from(block).length, 0);
980
+ var buildAnchoredTimelineSegments = ({
981
+ blocks,
982
+ timelineBlockAnchors,
983
+ timelineDisplayedBlocks,
984
+ visibleTimelineBlockKeys
985
+ }) => {
986
+ const orderedTimelineSegments = [];
987
+ const totalTimelineUnits = timelineDisplayedBlocks.reduce(
988
+ (count, block) => count + Array.from(block.content).length,
989
+ 0
990
+ );
991
+ let textCursor = 0;
992
+ const buildTextSegment = (start, end, options) => {
993
+ if (end <= start) {
994
+ return null;
995
+ }
996
+ const displayedBlocks = [];
997
+ let blockCursor = 0;
998
+ for (const block of timelineDisplayedBlocks) {
999
+ const blockUnits = Array.from(block.content);
1000
+ const blockStart = blockCursor;
1001
+ const blockEnd = blockCursor + blockUnits.length;
1002
+ if (blockEnd <= start) {
1003
+ blockCursor = blockEnd;
1004
+ continue;
1005
+ }
1006
+ if (blockStart >= end) {
1007
+ break;
1008
+ }
1009
+ const sliceStart = Math.max(0, start - blockStart);
1010
+ const sliceEnd = Math.min(blockUnits.length, end - blockStart);
1011
+ const slicedContent = blockUnits.slice(sliceStart, sliceEnd).join("");
1012
+ if (slicedContent) {
1013
+ displayedBlocks.push({
1014
+ content: slicedContent,
1015
+ tone: options?.forceSettled ? "settled" : block.tone
1016
+ });
1017
+ }
1018
+ blockCursor = blockEnd;
1019
+ }
1020
+ const content = displayedBlocks.map((block) => block.content).join("\n\n");
1021
+ if (!content) {
1022
+ return null;
1023
+ }
1024
+ return {
1025
+ type: "text",
1026
+ content,
1027
+ displayedBlocks,
1028
+ useTimelineSegmentation: true
1029
+ };
1030
+ };
1031
+ let trailingCutoff = totalTimelineUnits;
1032
+ for (const [index, block] of blocks.entries()) {
1033
+ if (block.type === "markdown") {
1034
+ orderedTimelineSegments.push({
1035
+ type: "markdown",
1036
+ content: block.text
1037
+ });
1038
+ continue;
1039
+ }
1040
+ const blockKey = getTimelineBlockKey(block, index);
1041
+ const anchor = blockKey !== null ? timelineBlockAnchors[blockKey] ?? totalTimelineUnits : totalTimelineUnits;
1042
+ const isBlockVisible = blockKey !== null && visibleTimelineBlockKeys?.[blockKey] ? true : anchor <= totalTimelineUnits;
1043
+ if (anchor > textCursor) {
1044
+ const textSegment = buildTextSegment(textCursor, Math.min(anchor, totalTimelineUnits), {
1045
+ forceSettled: isBlockVisible
1046
+ });
1047
+ if (textSegment) {
1048
+ orderedTimelineSegments.push(textSegment);
1049
+ }
1050
+ }
1051
+ if (!isBlockVisible) {
1052
+ textCursor = Math.min(anchor, totalTimelineUnits);
1053
+ trailingCutoff = Math.min(trailingCutoff, textCursor);
1054
+ continue;
1055
+ }
1056
+ trailingCutoff = totalTimelineUnits;
1057
+ orderedTimelineSegments.push({
1058
+ type: "block",
1059
+ block,
1060
+ index
1061
+ });
1062
+ textCursor = Math.max(textCursor, anchor);
1063
+ }
1064
+ const trailingTextSegment = buildTextSegment(textCursor, trailingCutoff);
1065
+ if (trailingTextSegment) {
1066
+ orderedTimelineSegments.push(trailingTextSegment);
1067
+ }
1068
+ return orderedTimelineSegments;
1069
+ };
1070
+
1071
+ // src/components/chat-thread/hooks/use-timeline-block-anchors.ts
1072
+ var useTimelineBlockAnchors = ({
1073
+ blocks,
1074
+ displayedTimelineTextLength,
1075
+ isAssistantStreaming,
1076
+ message,
1077
+ messageRenderOrder
1078
+ }) => {
1079
+ const [timelineBlockAnchors, setTimelineBlockAnchors] = (0, import_react6.useState)({});
1080
+ const [visibleTimelineBlockKeys, setVisibleTimelineBlockKeys] = (0, import_react6.useState)({});
1081
+ const currentTimelineBlockKeys = (0, import_react6.useMemo)(
1082
+ () => blocks.map((block, index) => getTimelineBlockKey(block, index)).filter((blockKey) => Boolean(blockKey)),
1083
+ [blocks]
1084
+ );
1085
+ const timelineTextStreamLength = (0, import_react6.useMemo)(
1086
+ () => getTimelineDisplayUnitCount(getTimelineTextStream(message.content, blocks)),
1087
+ [blocks, message.content]
1088
+ );
1089
+ const previousTimelineStateRef = (0, import_react6.useRef)({
1090
+ messageId: message.id,
1091
+ blockKeys: currentTimelineBlockKeys,
1092
+ textLength: timelineTextStreamLength
1093
+ });
1094
+ const effectiveTimelineBlockAnchors = (0, import_react6.useMemo)(() => {
1095
+ if (messageRenderOrder !== "timeline" || !isAssistantStreaming) {
1096
+ return timelineBlockAnchors;
1097
+ }
1098
+ const previousTimelineState = previousTimelineStateRef.current;
1099
+ const previousBlockKeys = new Set(previousTimelineState.blockKeys);
1100
+ return currentTimelineBlockKeys.reduce(
1101
+ (acc, blockKey) => {
1102
+ const existingAnchor = timelineBlockAnchors[blockKey];
1103
+ if (existingAnchor !== void 0) {
1104
+ acc[blockKey] = existingAnchor;
1105
+ return acc;
1106
+ }
1107
+ if (!previousBlockKeys.has(blockKey)) {
1108
+ acc[blockKey] = timelineTextStreamLength;
1109
+ }
1110
+ return acc;
1111
+ },
1112
+ { ...timelineBlockAnchors }
1113
+ );
1114
+ }, [
1115
+ currentTimelineBlockKeys,
1116
+ isAssistantStreaming,
1117
+ messageRenderOrder,
1118
+ timelineBlockAnchors,
1119
+ timelineTextStreamLength
1120
+ ]);
1121
+ (0, import_react6.useEffect)(() => {
1122
+ const previousTimelineState = previousTimelineStateRef.current;
1123
+ if (previousTimelineState.messageId !== message.id) {
1124
+ if (Object.keys(timelineBlockAnchors).length > 0) {
1125
+ setTimelineBlockAnchors({});
1126
+ }
1127
+ if (Object.keys(visibleTimelineBlockKeys).length > 0) {
1128
+ setVisibleTimelineBlockKeys({});
1129
+ }
1130
+ previousTimelineStateRef.current = {
1131
+ messageId: message.id,
1132
+ blockKeys: currentTimelineBlockKeys,
1133
+ textLength: timelineTextStreamLength
1134
+ };
1135
+ return;
1136
+ }
1137
+ if (messageRenderOrder === "timeline" && isAssistantStreaming) {
1138
+ const previousBlockKeys = new Set(previousTimelineState.blockKeys);
1139
+ const nextAnchors = currentTimelineBlockKeys.reduce(
1140
+ (acc, blockKey) => {
1141
+ const existingAnchor = timelineBlockAnchors[blockKey];
1142
+ if (existingAnchor !== void 0) {
1143
+ acc[blockKey] = existingAnchor;
1144
+ return acc;
1145
+ }
1146
+ if (!previousBlockKeys.has(blockKey)) {
1147
+ acc[blockKey] = timelineTextStreamLength;
1148
+ }
1149
+ return acc;
1150
+ },
1151
+ {}
1152
+ );
1153
+ const hasAnchorChanged = Object.keys(nextAnchors).length !== Object.keys(timelineBlockAnchors).length || Object.entries(nextAnchors).some(
1154
+ ([blockKey, anchor]) => timelineBlockAnchors[blockKey] !== anchor
1155
+ );
1156
+ if (hasAnchorChanged) {
1157
+ setTimelineBlockAnchors(nextAnchors);
1158
+ }
1159
+ } else if (messageRenderOrder !== "timeline" && Object.keys(timelineBlockAnchors).length > 0) {
1160
+ setTimelineBlockAnchors({});
1161
+ }
1162
+ previousTimelineStateRef.current = {
1163
+ messageId: message.id,
1164
+ blockKeys: currentTimelineBlockKeys,
1165
+ textLength: timelineTextStreamLength
1166
+ };
1167
+ }, [
1168
+ currentTimelineBlockKeys,
1169
+ isAssistantStreaming,
1170
+ message.id,
1171
+ message.content,
1172
+ messageRenderOrder,
1173
+ timelineBlockAnchors,
1174
+ timelineTextStreamLength,
1175
+ visibleTimelineBlockKeys
1176
+ ]);
1177
+ (0, import_react6.useEffect)(() => {
1178
+ if (messageRenderOrder !== "timeline") {
1179
+ if (Object.keys(visibleTimelineBlockKeys).length > 0) {
1180
+ setVisibleTimelineBlockKeys({});
1181
+ }
1182
+ return;
1183
+ }
1184
+ const nextVisibleBlockKeys = currentTimelineBlockKeys.reduce(
1185
+ (acc, blockKey) => {
1186
+ if (visibleTimelineBlockKeys[blockKey]) {
1187
+ acc[blockKey] = true;
1188
+ return acc;
1189
+ }
1190
+ const anchor = effectiveTimelineBlockAnchors[blockKey];
1191
+ if (anchor !== void 0 && anchor <= displayedTimelineTextLength) {
1192
+ acc[blockKey] = true;
1193
+ }
1194
+ return acc;
1195
+ },
1196
+ {}
1197
+ );
1198
+ const hasVisibleBlockChanged = Object.keys(nextVisibleBlockKeys).length !== Object.keys(visibleTimelineBlockKeys).length || Object.keys(nextVisibleBlockKeys).some((blockKey) => !visibleTimelineBlockKeys[blockKey]);
1199
+ if (hasVisibleBlockChanged) {
1200
+ setVisibleTimelineBlockKeys(nextVisibleBlockKeys);
1201
+ }
1202
+ }, [
1203
+ currentTimelineBlockKeys,
1204
+ displayedTimelineTextLength,
1205
+ effectiveTimelineBlockAnchors,
1206
+ messageRenderOrder,
1207
+ timelineBlockAnchors,
1208
+ visibleTimelineBlockKeys
1209
+ ]);
1210
+ return {
1211
+ timelineBlockAnchors: effectiveTimelineBlockAnchors,
1212
+ visibleTimelineBlockKeys
910
1213
  };
911
1214
  };
912
1215
 
@@ -1120,7 +1423,7 @@ var Value = import_styled3.default.span`
1120
1423
  `;
1121
1424
 
1122
1425
  // src/components/chat-thread/components/pde-ai-questionnaire-card.tsx
1123
- var import_react6 = require("react");
1426
+ var import_react7 = require("react");
1124
1427
  var import_styled4 = __toESM(require("@emotion/styled"));
1125
1428
  var import_jsx_runtime5 = require("@emotion/react/jsx-runtime");
1126
1429
  var OTHER_OPTION_VALUE = "__other__";
@@ -1222,10 +1525,10 @@ var PDEAIQuestionnaireCardInner = ({
1222
1525
  interactive = false,
1223
1526
  onSubmit
1224
1527
  }) => {
1225
- const [answers, setAnswers] = (0, import_react6.useState)(
1528
+ const [answers, setAnswers] = (0, import_react7.useState)(
1226
1529
  () => createInitialAnswers(questionnaire)
1227
1530
  );
1228
- const [errorMessage, setErrorMessage] = (0, import_react6.useState)(null);
1531
+ const [errorMessage, setErrorMessage] = (0, import_react7.useState)(null);
1229
1532
  const handleSubmit = () => {
1230
1533
  const missingQuestions = questionnaire.questions.filter(
1231
1534
  (question) => question.required && isMissingRequiredAnswer(question, answers)
@@ -1661,7 +1964,7 @@ var Detail = import_styled5.default.li`
1661
1964
 
1662
1965
  // src/components/chat-thread/components/image-viewer.tsx
1663
1966
  var import_styled6 = __toESM(require("@emotion/styled"));
1664
- var import_react7 = require("react");
1967
+ var import_react8 = require("react");
1665
1968
  var import_jsx_runtime7 = require("@emotion/react/jsx-runtime");
1666
1969
  var Overlay = import_styled6.default.div`
1667
1970
  position: fixed;
@@ -1680,8 +1983,8 @@ var Img = import_styled6.default.img`
1680
1983
  border-radius: 4px;
1681
1984
  `;
1682
1985
  var ImageViewer = ({ src, alt, onClose }) => {
1683
- const overlayRef = (0, import_react7.useRef)(null);
1684
- (0, import_react7.useEffect)(() => {
1986
+ const overlayRef = (0, import_react8.useRef)(null);
1987
+ (0, import_react8.useEffect)(() => {
1685
1988
  const handleKey = (e) => {
1686
1989
  if (e.key === "Escape")
1687
1990
  onClose();
@@ -1689,7 +1992,7 @@ var ImageViewer = ({ src, alt, onClose }) => {
1689
1992
  document.addEventListener("keydown", handleKey);
1690
1993
  return () => document.removeEventListener("keydown", handleKey);
1691
1994
  }, [onClose]);
1692
- (0, import_react7.useEffect)(() => {
1995
+ (0, import_react8.useEffect)(() => {
1693
1996
  overlayRef.current?.focus();
1694
1997
  }, []);
1695
1998
  const stopPropagation = (e) => e.stopPropagation();
@@ -1852,9 +2155,16 @@ var ChatMessageItemView = ({
1852
2155
  onQuestionnaireSubmit,
1853
2156
  renderMessageBlock
1854
2157
  }) => {
1855
- const { labels } = useChatContext();
1856
- const [activeImage, setActiveImage] = (0, import_react8.useState)(void 0);
1857
- const { displayedContent, freshContent, isAssistantStreaming, settledContent } = useChatMessageReveal(message);
2158
+ const { labels, messageRenderOrder = "blocks-first" } = useChatContext();
2159
+ const [activeImage, setActiveImage] = (0, import_react9.useState)(void 0);
2160
+ const {
2161
+ displayedBlocks,
2162
+ displayedContent,
2163
+ freshContent,
2164
+ isAssistantStreaming,
2165
+ isFreshBlockActive,
2166
+ settledContent
2167
+ } = useChatMessageReveal(message);
1858
2168
  const isStoppedAssistant = message.role === "assistant" && message.status === "stopped";
1859
2169
  const attachments = message.attachments ?? [];
1860
2170
  const blocks = message.blocks ?? [];
@@ -1866,6 +2176,22 @@ var ChatMessageItemView = ({
1866
2176
  const canSubmitConfirmation = isPlanMode && typeof onConfirmationSubmit === "function";
1867
2177
  const canSubmitQuestionnaire = isPlanMode && typeof onQuestionnaireSubmit === "function";
1868
2178
  const shouldShowStreamingCaret = isAssistantStreaming && (!shouldRenderStructuredBlocks || hasTextContent);
2179
+ const timelineConsumedText = messageRenderOrder === "timeline" ? getTimelineConsumedText(blocks) : "";
2180
+ const hasConsumedTimelineText = timelineConsumedText.length > 0 && displayedContent.startsWith(timelineConsumedText);
2181
+ const timelineDisplayedContent = hasConsumedTimelineText ? displayedContent.slice(timelineConsumedText.length) : displayedContent;
2182
+ const timelineTextDisplay = buildTimelineTextDisplay(
2183
+ timelineDisplayedContent,
2184
+ isAssistantStreaming,
2185
+ isFreshBlockActive
2186
+ );
2187
+ const displayedTimelineTextLength = getTimelineDisplayUnitCount(timelineDisplayedContent);
2188
+ const { timelineBlockAnchors, visibleTimelineBlockKeys } = useTimelineBlockAnchors({
2189
+ blocks,
2190
+ displayedTimelineTextLength,
2191
+ isAssistantStreaming,
2192
+ message,
2193
+ messageRenderOrder
2194
+ });
1869
2195
  const renderChatMessageBlock = (block, index) => {
1870
2196
  switch (block.type) {
1871
2197
  case "markdown":
@@ -1879,11 +2205,11 @@ var ChatMessageItemView = ({
1879
2205
  `markdown-${index}`
1880
2206
  );
1881
2207
  case "notice":
1882
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react8.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PDEAINoticeCard, { text: block.text, tone: block.tone }) }, `notice-${index}`);
2208
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PDEAINoticeCard, { text: block.text, tone: block.tone }) }, `notice-${index}`);
1883
2209
  case "parameter_summary":
1884
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react8.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PDEAIParameterSummaryCard, { items: block.items }) }, `parameter-summary-${index}`);
2210
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PDEAIParameterSummaryCard, { items: block.items }) }, `parameter-summary-${index}`);
1885
2211
  case "confirmation_card":
1886
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react8.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2212
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1887
2213
  PDEAIExecutionConfirmationCard,
1888
2214
  {
1889
2215
  proposal: block.proposal,
@@ -1896,9 +2222,9 @@ var ChatMessageItemView = ({
1896
2222
  }
1897
2223
  ) }, `confirmation-card-${index}`);
1898
2224
  case "result_summary":
1899
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react8.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PDEAIResultSummaryCard, { summary: block.summary }) }, `result-summary-${index}`);
2225
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PDEAIResultSummaryCard, { summary: block.summary }) }, `result-summary-${index}`);
1900
2226
  case "questionnaire":
1901
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react8.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2227
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1902
2228
  PDEAIQuestionnaireCard,
1903
2229
  {
1904
2230
  questionnaire: block.questionnaire,
@@ -1910,7 +2236,7 @@ var ChatMessageItemView = ({
1910
2236
  }
1911
2237
  ) }, `questionnaire-${index}`);
1912
2238
  case "custom":
1913
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react8.Fragment, { children: renderMessageBlock?.({
2239
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react9.Fragment, { children: renderMessageBlock?.({
1914
2240
  block,
1915
2241
  index,
1916
2242
  message,
@@ -1922,11 +2248,69 @@ var ChatMessageItemView = ({
1922
2248
  return null;
1923
2249
  }
1924
2250
  };
1925
- const renderTextContent = () => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1926
- settledContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentBlock, { "data-testid": "chat-message-settled-block", "data-block-tone": "settled", children: renderMarkdownContent(settledContent) }) : null,
1927
- freshContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentBlock, { "data-testid": "chat-message-fresh-block", "data-block-tone": "fresh", children: renderMarkdownContent(freshContent) }) : null,
1928
- !settledContent && !freshContent && hasTextContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentBlock, { "data-testid": "chat-message-settled-block", "data-block-tone": "settled", children: renderMarkdownContent(displayedContent) }) : null
1929
- ] });
2251
+ const renderTextContent = (options) => {
2252
+ const textContent = options?.content ?? displayedContent;
2253
+ const localTimelineTextDisplay = options?.displayedBlocks ? void 0 : options?.useTimelineSegmentation && options.content !== void 0 ? buildTimelineTextDisplay(options.content, isAssistantStreaming, isFreshBlockActive) : void 0;
2254
+ const textBlocks = options?.displayedBlocks ?? localTimelineTextDisplay?.displayedBlocks ?? displayedBlocks;
2255
+ const settledText = localTimelineTextDisplay?.settledContent ?? settledContent;
2256
+ const freshText = localTimelineTextDisplay?.freshContent ?? freshContent;
2257
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2258
+ textBlocks.filter((block) => block.content).map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2259
+ ContentBlock,
2260
+ {
2261
+ "data-testid": block.tone === "fresh" ? "chat-message-fresh-block" : "chat-message-settled-block",
2262
+ "data-block-tone": block.tone,
2263
+ "data-block-index": index,
2264
+ children: renderMarkdownContent(block.content)
2265
+ },
2266
+ `${block.tone}-${index}`
2267
+ )),
2268
+ !textBlocks.some((block) => block.content) && !settledText && !freshText && Boolean(textContent) ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentBlock, { "data-testid": "chat-message-settled-block", "data-block-tone": "settled", children: renderMarkdownContent(textContent) }) : null
2269
+ ] });
2270
+ };
2271
+ const renderStaticTextSegment = (content) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentBlock, { "data-testid": "chat-message-settled-block", "data-block-tone": "settled", children: renderMarkdownContent(content) });
2272
+ const bodySegments = (() => {
2273
+ if (!shouldRenderStructuredBlocks && hasTextContent) {
2274
+ return [{ type: "text" }];
2275
+ }
2276
+ if (!shouldRenderStructuredBlocks) {
2277
+ return [];
2278
+ }
2279
+ if (messageRenderOrder === "timeline" && hasTextContent) {
2280
+ const hasAnchoredStructuredBlocks = blocks.some((block, index) => {
2281
+ const blockKey = getTimelineBlockKey(block, index);
2282
+ return blockKey ? timelineBlockAnchors[blockKey] !== void 0 : false;
2283
+ });
2284
+ if (hasAnchoredStructuredBlocks) {
2285
+ return buildAnchoredTimelineSegments({
2286
+ blocks,
2287
+ timelineBlockAnchors,
2288
+ timelineDisplayedBlocks: timelineTextDisplay.displayedBlocks,
2289
+ visibleTimelineBlockKeys
2290
+ });
2291
+ }
2292
+ const orderedTimelineSegments = blocks.map(
2293
+ (block, index) => block.type === "markdown" ? {
2294
+ type: "markdown",
2295
+ content: block.text
2296
+ } : {
2297
+ type: "block",
2298
+ block,
2299
+ index
2300
+ }
2301
+ );
2302
+ if (!timelineConsumedText) {
2303
+ return displayedContent ? [{ type: "text", content: displayedContent }, ...orderedTimelineSegments] : orderedTimelineSegments;
2304
+ }
2305
+ return timelineDisplayedContent ? [...orderedTimelineSegments, { type: "text", content: timelineDisplayedContent }] : orderedTimelineSegments;
2306
+ }
2307
+ const orderedBlocks = blocks.map((block, index) => ({
2308
+ type: "block",
2309
+ block,
2310
+ index
2311
+ }));
2312
+ return hasTextContent ? [...orderedBlocks, { type: "text" }] : orderedBlocks;
2313
+ })();
1930
2314
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1931
2315
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Bubble, { "data-role": message.role, "data-status": message.status ?? "done", children: [
1932
2316
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Header2, { children: [
@@ -1945,17 +2329,18 @@ var ChatMessageItemView = ({
1945
2329
  isStoppedAssistant ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusTag, { "data-testid": "chat-message-stopped-tag", children: labels.stoppedResponse }) : null
1946
2330
  ] }),
1947
2331
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Content, { "data-testid": "chat-message-content", children: [
1948
- shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ContentStack, { "data-testid": "chat-message-body-stack", children: [
1949
- shouldRenderStructuredBlocks ? blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1950
- ContentSegment,
1951
- {
1952
- "data-testid": "chat-message-content-segment",
1953
- children: renderChatMessageBlock(block, index)
1954
- },
1955
- `${block.type}-${index}`
1956
- )) : null,
1957
- hasTextContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentSegment, { "data-testid": "chat-message-content-segment", children: renderTextContent() }) : null
1958
- ] }) : null,
2332
+ shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentStack, { "data-testid": "chat-message-body-stack", children: bodySegments.map((segment, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2333
+ ContentSegment,
2334
+ {
2335
+ "data-testid": "chat-message-content-segment",
2336
+ children: segment.type === "block" ? renderChatMessageBlock(segment.block, segment.index) : segment.type === "text" ? segment.content !== void 0 ? segment.useTimelineSegmentation ? renderTextContent({
2337
+ content: segment.content,
2338
+ displayedBlocks: segment.displayedBlocks,
2339
+ useTimelineSegmentation: true
2340
+ }) : renderStaticTextSegment(segment.content) : renderTextContent() : renderStaticTextSegment(segment.content)
2341
+ },
2342
+ segment.type === "text" ? `text-${index}` : segment.type === "markdown" ? `markdown-${index}` : `${segment.block.type}-${segment.index}`
2343
+ )) }) : null,
1959
2344
  attachments.length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AttachmentGrid, { "data-testid": "chat-message-attachment-grid", children: attachments.map((attachment) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1960
2345
  AttachmentButton,
1961
2346
  {
@@ -1982,7 +2367,7 @@ var ChatMessageItemView = ({
1982
2367
  ) : null
1983
2368
  ] });
1984
2369
  };
1985
- var ChatMessageItem = (0, import_react8.memo)(
2370
+ var ChatMessageItem = (0, import_react9.memo)(
1986
2371
  ChatMessageItemView,
1987
2372
  (previousProps, nextProps) => isSameMessage(
1988
2373
  previousProps.message,
@@ -2127,7 +2512,7 @@ var AttachmentImage = import_styled7.default.img`
2127
2512
  var TableWrapper = import_styled7.default.div`
2128
2513
  overflow-x: auto;
2129
2514
  `;
2130
- var caretBlink = import_react9.keyframes`
2515
+ var caretBlink = import_react10.keyframes`
2131
2516
  0%, 49% {
2132
2517
  opacity: 1;
2133
2518
  }
@@ -2136,7 +2521,7 @@ var caretBlink = import_react9.keyframes`
2136
2521
  opacity: 0.2;
2137
2522
  }
2138
2523
  `;
2139
- var shimmer = import_react9.keyframes`
2524
+ var shimmer = import_react10.keyframes`
2140
2525
  0%, 100% {
2141
2526
  transform: scale(0.9) rotate(0deg);
2142
2527
  opacity: 0.55;
@@ -2189,65 +2574,21 @@ var StreamingCaret = import_styled7.default.span`
2189
2574
  animation: ${caretBlink} 0.9s steps(1) infinite;
2190
2575
  `;
2191
2576
 
2192
- // src/components/chat-thread/components/chat-thread-history-list.tsx
2193
- var import_react10 = require("react");
2577
+ // src/components/chat-thread/components/chat-thread-empty-state.tsx
2194
2578
  var import_styled8 = __toESM(require("@emotion/styled"));
2195
2579
  var import_jsx_runtime9 = require("@emotion/react/jsx-runtime");
2196
- var ChatThreadHistoryList = (0, import_react10.memo)(
2197
- ({
2198
- mode,
2199
- historyMessages,
2200
- latestUserMessageId,
2201
- latestUserMessageRef,
2202
- onConfirmationSubmit,
2203
- onQuestionnaireSubmit,
2204
- renderMessageBlock
2205
- }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(HistoryGroup, { "data-testid": "chat-thread-history", children: historyMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2206
- MessageSlot,
2207
- {
2208
- ref: message.id === latestUserMessageId ? latestUserMessageRef : null,
2209
- "data-testid": message.id === latestUserMessageId ? "chat-latest-user-anchor" : void 0,
2210
- style: message.id === latestUserMessageId ? {
2211
- scrollMarginTop: `${CHAT_THREAD_SCROLL_TOP_GAP}px`
2212
- } : void 0,
2213
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2214
- ChatMessageItem,
2215
- {
2216
- mode,
2217
- message,
2218
- onConfirmationSubmit,
2219
- onQuestionnaireSubmit,
2220
- renderMessageBlock
2221
- }
2222
- )
2223
- },
2224
- message.id
2225
- )) })
2226
- );
2227
- ChatThreadHistoryList.displayName = "ChatThreadHistoryList";
2228
- var HistoryGroup = import_styled8.default.div`
2229
- display: contents;
2230
- `;
2231
- var MessageSlot = import_styled8.default.div`
2232
- display: flex;
2233
- align-items: flex-start;
2234
- `;
2235
-
2236
- // src/components/chat-thread/components/chat-thread-empty-state.tsx
2237
- var import_styled9 = __toESM(require("@emotion/styled"));
2238
- var import_jsx_runtime10 = require("@emotion/react/jsx-runtime");
2239
2580
  var ChatThreadEmptyState = () => {
2240
2581
  const { labels } = useChatContext();
2241
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(EmptyShell, { "data-testid": "chat-empty-hero", children: [
2242
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(HeroMark, { children: [
2243
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(HeroOrbit, {}),
2244
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(HeroCore, { children: "AI" })
2582
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(EmptyShell, { "data-testid": "chat-empty-hero", children: [
2583
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(HeroMark, { children: [
2584
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(HeroOrbit, {}),
2585
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(HeroCore, { children: "AI" })
2245
2586
  ] }),
2246
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(HeroTitle, { children: labels.emptyStateTitle }),
2247
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(HeroSubtitle, { children: labels.emptyStateSubtitle })
2587
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(HeroTitle, { children: labels.emptyStateTitle }),
2588
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(HeroSubtitle, { children: labels.emptyStateSubtitle })
2248
2589
  ] });
2249
2590
  };
2250
- var EmptyShell = import_styled9.default.div`
2591
+ var EmptyShell = import_styled8.default.div`
2251
2592
  flex: 1;
2252
2593
  min-height: 0;
2253
2594
  display: flex;
@@ -2257,7 +2598,7 @@ var EmptyShell = import_styled9.default.div`
2257
2598
  gap: 16px;
2258
2599
  padding: 48px 24px 24px;
2259
2600
  `;
2260
- var HeroMark = import_styled9.default.div`
2601
+ var HeroMark = import_styled8.default.div`
2261
2602
  position: relative;
2262
2603
  width: 108px;
2263
2604
  height: 108px;
@@ -2265,7 +2606,7 @@ var HeroMark = import_styled9.default.div`
2265
2606
  display: grid;
2266
2607
  place-items: center;
2267
2608
  `;
2268
- var HeroOrbit = import_styled9.default.div`
2609
+ var HeroOrbit = import_styled8.default.div`
2269
2610
  position: absolute;
2270
2611
  inset: 20px;
2271
2612
  border-radius: 50%;
@@ -2288,7 +2629,7 @@ var HeroOrbit = import_styled9.default.div`
2288
2629
  transform: rotate(-22deg);
2289
2630
  }
2290
2631
  `;
2291
- var HeroCore = import_styled9.default.div`
2632
+ var HeroCore = import_styled8.default.div`
2292
2633
  position: relative;
2293
2634
  z-index: 1;
2294
2635
  font-size: 28px;
@@ -2298,13 +2639,13 @@ var HeroCore = import_styled9.default.div`
2298
2639
  color: rgba(242, 244, 255, 0.96);
2299
2640
  text-shadow: 0 0 16px rgba(98, 116, 255, 0.65);
2300
2641
  `;
2301
- var HeroTitle = import_styled9.default.p`
2642
+ var HeroTitle = import_styled8.default.p`
2302
2643
  margin: 0;
2303
2644
  color: rgba(255, 255, 255, 0.88);
2304
2645
  font-size: 16px;
2305
2646
  line-height: 24px;
2306
2647
  `;
2307
- var HeroSubtitle = import_styled9.default.p`
2648
+ var HeroSubtitle = import_styled8.default.p`
2308
2649
  margin: 0;
2309
2650
  color: rgba(255, 255, 255, 0.72);
2310
2651
  font-size: 14px;
@@ -2312,7 +2653,74 @@ var HeroSubtitle = import_styled9.default.p`
2312
2653
  `;
2313
2654
 
2314
2655
  // src/components/chat-thread/index.tsx
2315
- var import_jsx_runtime11 = require("@emotion/react/jsx-runtime");
2656
+ var import_jsx_runtime10 = require("@emotion/react/jsx-runtime");
2657
+ var renderChatMessage = ({
2658
+ message,
2659
+ mode,
2660
+ onConfirmationSubmit,
2661
+ onQuestionnaireSubmit,
2662
+ renderMessageBlock
2663
+ }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2664
+ ChatMessageItem,
2665
+ {
2666
+ mode,
2667
+ message,
2668
+ onConfirmationSubmit,
2669
+ onQuestionnaireSubmit,
2670
+ renderMessageBlock
2671
+ }
2672
+ );
2673
+ var renderErrorState = ({
2674
+ error,
2675
+ onRetry,
2676
+ retryButtonLabel
2677
+ }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ErrorState, { "data-testid": "chat-thread-error-state", children: [
2678
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ErrorText, { children: error }),
2679
+ onRetry ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ErrorActions, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RetryButton, { type: "button", "data-testid": "chat-thread-retry", onClick: onRetry, children: retryButtonLabel }) }) : null
2680
+ ] });
2681
+ var groupConversationTurns = (historyMessages, streamingMessage) => {
2682
+ const turns = [];
2683
+ let currentTurn = null;
2684
+ historyMessages.forEach((message) => {
2685
+ if (message.role === "user") {
2686
+ currentTurn = {
2687
+ id: message.id,
2688
+ userMessage: message,
2689
+ responseMessages: []
2690
+ };
2691
+ turns.push(currentTurn);
2692
+ return;
2693
+ }
2694
+ if (!currentTurn) {
2695
+ currentTurn = {
2696
+ id: `assistant-turn-${message.id}`,
2697
+ responseMessages: [message]
2698
+ };
2699
+ turns.push(currentTurn);
2700
+ return;
2701
+ }
2702
+ currentTurn.responseMessages.push(message);
2703
+ });
2704
+ if (!streamingMessage) {
2705
+ return turns;
2706
+ }
2707
+ const lastTurn = turns[turns.length - 1];
2708
+ if (lastTurn) {
2709
+ return [
2710
+ ...turns.slice(0, -1),
2711
+ {
2712
+ ...lastTurn,
2713
+ responseMessages: [...lastTurn.responseMessages, streamingMessage]
2714
+ }
2715
+ ];
2716
+ }
2717
+ return [
2718
+ {
2719
+ id: `assistant-turn-${streamingMessage.id}`,
2720
+ responseMessages: [streamingMessage]
2721
+ }
2722
+ ];
2723
+ };
2316
2724
  var ChatThreadView = ({
2317
2725
  activeSessionMode = DEFAULT_CHAT_AGENT_MODE,
2318
2726
  historyMessages,
@@ -2325,31 +2733,45 @@ var ChatThreadView = ({
2325
2733
  renderMessageBlock
2326
2734
  }) => {
2327
2735
  const containerRef = (0, import_react11.useRef)(null);
2328
- const latestUserMessageId = (0, import_react11.useMemo)(
2329
- () => findLatestUserMessageId(historyMessages),
2330
- [historyMessages]
2736
+ const conversationTurns = (0, import_react11.useMemo)(
2737
+ () => groupConversationTurns(historyMessages, streamingMessage),
2738
+ [historyMessages, streamingMessage]
2331
2739
  );
2740
+ const latestTurn = conversationTurns[conversationTurns.length - 1];
2741
+ const previousTurns = conversationTurns.slice(0, -1);
2742
+ const latestUserMessageId = latestTurn?.userMessage?.id;
2332
2743
  const latestUserMessageRef = (0, import_react11.useRef)(null);
2333
- const pendingScrollUserMessageIdRef = (0, import_react11.useRef)(void 0);
2334
2744
  const reservedSpaceFrameRef = (0, import_react11.useRef)(null);
2335
- const [latestUserMessageReservedSpace, setLatestUserMessageReservedSpace] = (0, import_react11.useState)({ messageId: void 0, value: 0 });
2336
- const reservedPaddingBottom = 24 + (latestUserMessageReservedSpace.messageId === latestUserMessageId ? latestUserMessageReservedSpace.value : 0);
2337
- const measureLatestUserMessageReservedSpace = (0, import_react11.useCallback)((messageId) => {
2745
+ const [latestTurnMinHeight, setLatestTurnMinHeight] = (0, import_react11.useState)(0);
2746
+ const measureLatestTurnMinHeight = (0, import_react11.useCallback)(() => {
2747
+ const container = containerRef.current;
2748
+ if (!container)
2749
+ return;
2750
+ const computedStyle = window.getComputedStyle(container);
2751
+ const paddingTop = Number.parseFloat(computedStyle.paddingTop || "0") || 0;
2752
+ const paddingBottom = Number.parseFloat(computedStyle.paddingBottom || "0") || 0;
2753
+ const nextMinHeight = Math.max(0, container.clientHeight - paddingTop - paddingBottom);
2754
+ setLatestTurnMinHeight((current) => current === nextMinHeight ? current : nextMinHeight);
2755
+ }, []);
2756
+ const scrollLatestUserMessageToTop = (0, import_react11.useCallback)(() => {
2338
2757
  const container = containerRef.current;
2339
2758
  const target = latestUserMessageRef.current;
2340
2759
  if (!container || !target)
2341
2760
  return;
2342
- const reservedHeight = calculateChatThreadScrollSpacerHeight({
2343
- containerClientHeight: container.clientHeight,
2344
- containerScrollHeight: container.scrollHeight,
2345
- targetOffsetTop: target.offsetTop
2346
- });
2347
- setLatestUserMessageReservedSpace((current) => {
2348
- const next = reservedHeight > 0 ? reservedHeight : 0;
2349
- if (current.messageId === messageId && current.value === next)
2350
- return current;
2351
- return { messageId, value: next };
2352
- });
2761
+ const containerRect = container.getBoundingClientRect();
2762
+ const targetRect = target.getBoundingClientRect();
2763
+ const nextScrollTop = Math.max(
2764
+ 0,
2765
+ container.scrollTop + (targetRect.top - containerRect.top) - CHAT_THREAD_SCROLL_TOP_GAP
2766
+ );
2767
+ if (typeof container.scrollTo === "function") {
2768
+ container.scrollTo({
2769
+ top: nextScrollTop,
2770
+ behavior: "auto"
2771
+ });
2772
+ return;
2773
+ }
2774
+ container.scrollTop = nextScrollTop;
2353
2775
  }, []);
2354
2776
  (0, import_react11.useLayoutEffect)(() => {
2355
2777
  if (reservedSpaceFrameRef.current !== null) {
@@ -2357,12 +2779,9 @@ var ChatThreadView = ({
2357
2779
  reservedSpaceFrameRef.current = null;
2358
2780
  }
2359
2781
  if (!latestUserMessageId) {
2360
- pendingScrollUserMessageIdRef.current = void 0;
2361
2782
  reservedSpaceFrameRef.current = window.requestAnimationFrame(() => {
2362
2783
  reservedSpaceFrameRef.current = null;
2363
- setLatestUserMessageReservedSpace(
2364
- (current) => current.messageId === void 0 && current.value === 0 ? current : { messageId: void 0, value: 0 }
2365
- );
2784
+ setLatestTurnMinHeight((current) => current === 0 ? current : 0);
2366
2785
  });
2367
2786
  return () => {
2368
2787
  if (reservedSpaceFrameRef.current !== null) {
@@ -2371,10 +2790,10 @@ var ChatThreadView = ({
2371
2790
  }
2372
2791
  };
2373
2792
  }
2374
- pendingScrollUserMessageIdRef.current = latestUserMessageId;
2375
2793
  reservedSpaceFrameRef.current = window.requestAnimationFrame(() => {
2376
2794
  reservedSpaceFrameRef.current = null;
2377
- measureLatestUserMessageReservedSpace(latestUserMessageId);
2795
+ measureLatestTurnMinHeight();
2796
+ scrollLatestUserMessageToTop();
2378
2797
  });
2379
2798
  return () => {
2380
2799
  if (reservedSpaceFrameRef.current !== null) {
@@ -2382,51 +2801,79 @@ var ChatThreadView = ({
2382
2801
  reservedSpaceFrameRef.current = null;
2383
2802
  }
2384
2803
  };
2385
- }, [latestUserMessageId, measureLatestUserMessageReservedSpace]);
2804
+ }, [latestUserMessageId, measureLatestTurnMinHeight, scrollLatestUserMessageToTop]);
2386
2805
  (0, import_react11.useLayoutEffect)(() => {
2387
- if (!latestUserMessageId || pendingScrollUserMessageIdRef.current !== latestUserMessageId)
2388
- return;
2389
- if (latestUserMessageReservedSpace.messageId !== latestUserMessageId)
2806
+ if (!latestUserMessageId)
2390
2807
  return;
2391
- latestUserMessageRef.current?.scrollIntoView({ block: "start", behavior: "smooth" });
2392
- pendingScrollUserMessageIdRef.current = void 0;
2393
- }, [latestUserMessageId, latestUserMessageReservedSpace]);
2394
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2395
- Container,
2396
- {
2397
- ref: containerRef,
2398
- "data-testid": "chat-thread",
2399
- style: { paddingBottom: `${reservedPaddingBottom}px` },
2400
- children: [
2401
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2402
- ChatThreadHistoryList,
2403
- {
2404
- mode: activeSessionMode,
2405
- historyMessages,
2406
- latestUserMessageId,
2407
- latestUserMessageRef,
2408
- onConfirmationSubmit,
2409
- onQuestionnaireSubmit,
2410
- renderMessageBlock
2411
- }
2412
- ),
2413
- streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StreamingGroup, { "data-testid": "chat-thread-streaming", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MessageSlot2, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2414
- ChatMessageItem,
2415
- {
2808
+ const handleResize = () => {
2809
+ measureLatestTurnMinHeight();
2810
+ scrollLatestUserMessageToTop();
2811
+ };
2812
+ const container = containerRef.current;
2813
+ let resizeObserver = null;
2814
+ if (container && typeof ResizeObserver !== "undefined") {
2815
+ resizeObserver = new ResizeObserver(() => {
2816
+ handleResize();
2817
+ });
2818
+ resizeObserver.observe(container);
2819
+ }
2820
+ window.addEventListener("resize", handleResize);
2821
+ return () => {
2822
+ resizeObserver?.disconnect();
2823
+ window.removeEventListener("resize", handleResize);
2824
+ };
2825
+ }, [latestUserMessageId, measureLatestTurnMinHeight, scrollLatestUserMessageToTop]);
2826
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Container, { ref: containerRef, "data-testid": "chat-thread", children: [
2827
+ previousTurns.map((turn) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ConversationTurn, { "data-testid": "chat-thread-turn", children: [
2828
+ turn.userMessage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageSlot, { children: renderChatMessage({
2829
+ message: turn.userMessage,
2830
+ mode: activeSessionMode,
2831
+ onConfirmationSubmit,
2832
+ onQuestionnaireSubmit,
2833
+ renderMessageBlock
2834
+ }) }) : null,
2835
+ turn.responseMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageSlot, { children: renderChatMessage({
2836
+ message,
2837
+ mode: activeSessionMode,
2838
+ onConfirmationSubmit,
2839
+ onQuestionnaireSubmit,
2840
+ renderMessageBlock
2841
+ }) }, message.id))
2842
+ ] }, turn.id)),
2843
+ latestTurn ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2844
+ ConversationTurn,
2845
+ {
2846
+ "data-testid": "chat-thread-latest-turn",
2847
+ style: latestTurnMinHeight > 0 ? { minHeight: `${latestTurnMinHeight}px` } : void 0,
2848
+ children: [
2849
+ latestTurn.userMessage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2850
+ MessageSlot,
2851
+ {
2852
+ ref: latestUserMessageRef,
2853
+ "data-testid": "chat-latest-user-anchor",
2854
+ style: { scrollMarginTop: `${CHAT_THREAD_SCROLL_TOP_GAP}px` },
2855
+ children: renderChatMessage({
2856
+ message: latestTurn.userMessage,
2857
+ mode: activeSessionMode,
2858
+ onConfirmationSubmit,
2859
+ onQuestionnaireSubmit,
2860
+ renderMessageBlock
2861
+ })
2862
+ }
2863
+ ) : null,
2864
+ latestTurn.responseMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageSlot, { children: renderChatMessage({
2865
+ message,
2416
2866
  mode: activeSessionMode,
2417
- message: streamingMessage,
2418
2867
  onConfirmationSubmit,
2419
2868
  onQuestionnaireSubmit,
2420
2869
  renderMessageBlock
2421
- }
2422
- ) }) }) : null,
2423
- error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ErrorState, { "data-testid": "chat-thread-error-state", children: [
2424
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ErrorText, { children: error }),
2425
- onRetry ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ErrorActions, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(RetryButton, { type: "button", "data-testid": "chat-thread-retry", onClick: onRetry, children: retryButtonLabel }) }) : null
2426
- ] }) : null
2427
- ]
2428
- }
2429
- );
2870
+ }) }, message.id)),
2871
+ error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
2872
+ ]
2873
+ }
2874
+ ) : null,
2875
+ !latestTurn && error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
2876
+ ] });
2430
2877
  };
2431
2878
  var EMPTY_MESSAGES = [];
2432
2879
  var ChatThread = () => {
@@ -2470,9 +2917,9 @@ var ChatThread = () => {
2470
2917
  [sendRef]
2471
2918
  );
2472
2919
  if (!hasSessions || messages.length === 0 && !streamingMessage) {
2473
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChatThreadEmptyState, {});
2920
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatThreadEmptyState, {});
2474
2921
  }
2475
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2922
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2476
2923
  ChatThreadView,
2477
2924
  {
2478
2925
  activeSessionMode,
@@ -2487,7 +2934,7 @@ var ChatThread = () => {
2487
2934
  }
2488
2935
  );
2489
2936
  };
2490
- var Container = import_styled10.default.div`
2937
+ var Container = import_styled9.default.div`
2491
2938
  display: flex;
2492
2939
  flex: 1;
2493
2940
  flex-direction: column;
@@ -2510,27 +2957,29 @@ var Container = import_styled10.default.div`
2510
2957
  background: transparent;
2511
2958
  }
2512
2959
  `;
2513
- var MessageSlot2 = import_styled10.default.div`
2960
+ var MessageSlot = import_styled9.default.div`
2514
2961
  display: flex;
2515
2962
  `;
2516
- var StreamingGroup = import_styled10.default.div`
2517
- display: contents;
2963
+ var ConversationTurn = import_styled9.default.div`
2964
+ display: flex;
2965
+ flex-direction: column;
2966
+ gap: 18px;
2518
2967
  `;
2519
- var ErrorText = import_styled10.default.div`
2968
+ var ErrorText = import_styled9.default.div`
2520
2969
  color: #ff7b72;
2521
2970
  font-size: 14px;
2522
2971
  `;
2523
- var ErrorState = import_styled10.default.div`
2972
+ var ErrorState = import_styled9.default.div`
2524
2973
  display: flex;
2525
2974
  flex-direction: column;
2526
2975
  align-items: flex-start;
2527
2976
  gap: 10px;
2528
2977
  `;
2529
- var ErrorActions = import_styled10.default.div`
2978
+ var ErrorActions = import_styled9.default.div`
2530
2979
  display: flex;
2531
2980
  align-items: center;
2532
2981
  `;
2533
- var RetryButton = import_styled10.default.button`
2982
+ var RetryButton = import_styled9.default.button`
2534
2983
  border: 1px solid rgba(255, 255, 255, 0.14);
2535
2984
  border-radius: 999px;
2536
2985
  background: rgba(255, 255, 255, 0.04);
@@ -2547,7 +2996,7 @@ var RetryButton = import_styled10.default.button`
2547
2996
 
2548
2997
  // src/components/chat-composer/index.tsx
2549
2998
  var import_react15 = require("react");
2550
- var import_styled15 = __toESM(require("@emotion/styled"));
2999
+ var import_styled14 = __toESM(require("@emotion/styled"));
2551
3000
 
2552
3001
  // src/components/chat-composer/lib/chat-composer.ts
2553
3002
  var DRAFT_CHAT_SESSION_ID_PREFIX = "draft-session-";
@@ -3112,8 +3561,8 @@ var useChatComposer = () => {
3112
3561
 
3113
3562
  // src/components/chat-composer/components/chat-composer-attachment-list.tsx
3114
3563
  var import_react14 = require("react");
3115
- var import_styled11 = __toESM(require("@emotion/styled"));
3116
- var import_jsx_runtime12 = require("@emotion/react/jsx-runtime");
3564
+ var import_styled10 = __toESM(require("@emotion/styled"));
3565
+ var import_jsx_runtime11 = require("@emotion/react/jsx-runtime");
3117
3566
  var ChatComposerAttachmentList = ({
3118
3567
  attachments,
3119
3568
  onRemoveAttachment
@@ -3122,18 +3571,18 @@ var ChatComposerAttachmentList = ({
3122
3571
  if (!attachments.length) {
3123
3572
  return null;
3124
3573
  }
3125
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3126
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AttachmentList, { "data-testid": "chat-composer-attachment-list", children: attachments.map((attachment) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(AttachmentCard, { children: [
3127
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3574
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
3575
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AttachmentList, { "data-testid": "chat-composer-attachment-list", children: attachments.map((attachment) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(AttachmentCard, { children: [
3576
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3128
3577
  AttachmentPreviewButton,
3129
3578
  {
3130
3579
  type: "button",
3131
3580
  "aria-label": `${attachment.name} preview`,
3132
3581
  onClick: () => setActiveImage(attachment),
3133
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AttachmentThumb, { src: attachment.previewUrl, alt: attachment.name })
3582
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AttachmentThumb, { src: attachment.previewUrl, alt: attachment.name })
3134
3583
  }
3135
3584
  ),
3136
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3585
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3137
3586
  AttachmentRemoveButton,
3138
3587
  {
3139
3588
  type: "button",
@@ -3142,11 +3591,11 @@ var ChatComposerAttachmentList = ({
3142
3591
  event.stopPropagation();
3143
3592
  onRemoveAttachment(attachment.id);
3144
3593
  },
3145
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CloseGlyph, { "aria-hidden": "true" })
3594
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CloseGlyph, { "aria-hidden": "true" })
3146
3595
  }
3147
3596
  )
3148
3597
  ] }, attachment.id)) }),
3149
- activeImage ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3598
+ activeImage ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3150
3599
  ImageViewer,
3151
3600
  {
3152
3601
  src: activeImage.previewUrl,
@@ -3156,7 +3605,7 @@ var ChatComposerAttachmentList = ({
3156
3605
  ) : null
3157
3606
  ] });
3158
3607
  };
3159
- var AttachmentList = import_styled11.default.div`
3608
+ var AttachmentList = import_styled10.default.div`
3160
3609
  display: flex;
3161
3610
  flex-wrap: wrap;
3162
3611
  gap: 10px;
@@ -3178,7 +3627,7 @@ var AttachmentList = import_styled11.default.div`
3178
3627
  background: transparent;
3179
3628
  }
3180
3629
  `;
3181
- var AttachmentCard = import_styled11.default.div`
3630
+ var AttachmentCard = import_styled10.default.div`
3182
3631
  position: relative;
3183
3632
  width: 108px;
3184
3633
  height: 72px;
@@ -3187,7 +3636,7 @@ var AttachmentCard = import_styled11.default.div`
3187
3636
  border: 1px solid rgba(255, 255, 255, 0.12);
3188
3637
  background: rgba(255, 255, 255, 0.04);
3189
3638
  `;
3190
- var AttachmentPreviewButton = import_styled11.default.button`
3639
+ var AttachmentPreviewButton = import_styled10.default.button`
3191
3640
  width: 100%;
3192
3641
  height: 100%;
3193
3642
  padding: 0;
@@ -3195,13 +3644,13 @@ var AttachmentPreviewButton = import_styled11.default.button`
3195
3644
  background: transparent;
3196
3645
  cursor: zoom-in;
3197
3646
  `;
3198
- var AttachmentThumb = import_styled11.default.img`
3647
+ var AttachmentThumb = import_styled10.default.img`
3199
3648
  width: 100%;
3200
3649
  height: 100%;
3201
3650
  object-fit: cover;
3202
3651
  display: block;
3203
3652
  `;
3204
- var AttachmentRemoveButton = import_styled11.default.button`
3653
+ var AttachmentRemoveButton = import_styled10.default.button`
3205
3654
  position: absolute;
3206
3655
  top: 6px;
3207
3656
  right: 6px;
@@ -3239,7 +3688,7 @@ var AttachmentRemoveButton = import_styled11.default.button`
3239
3688
  background: rgba(30, 30, 35, 0.98);
3240
3689
  }
3241
3690
  `;
3242
- var CloseGlyph = import_styled11.default.span`
3691
+ var CloseGlyph = import_styled10.default.span`
3243
3692
  position: relative;
3244
3693
  width: 11px;
3245
3694
  height: 11px;
@@ -3268,9 +3717,9 @@ var CloseGlyph = import_styled11.default.span`
3268
3717
  `;
3269
3718
 
3270
3719
  // src/components/chat-composer/components/chat-model-control.tsx
3271
- var import_styled12 = __toESM(require("@emotion/styled"));
3720
+ var import_styled11 = __toESM(require("@emotion/styled"));
3272
3721
  var import_compass_ui = require("@xinghunm/compass-ui");
3273
- var import_jsx_runtime13 = require("@emotion/react/jsx-runtime");
3722
+ var import_jsx_runtime12 = require("@emotion/react/jsx-runtime");
3274
3723
  var ChatModelControl = ({
3275
3724
  selectedModel,
3276
3725
  availableModels,
@@ -3281,7 +3730,7 @@ var ChatModelControl = ({
3281
3730
  onReloadModels
3282
3731
  }) => {
3283
3732
  if (isModelsError) {
3284
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3733
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3285
3734
  ModelReloadButton,
3286
3735
  {
3287
3736
  type: "button",
@@ -3289,8 +3738,8 @@ var ChatModelControl = ({
3289
3738
  "aria-label": "Reload",
3290
3739
  onClick: onReloadModels,
3291
3740
  children: [
3292
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Failed to load models" }),
3293
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3741
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "Failed to load models" }),
3742
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3294
3743
  ReloadIcon,
3295
3744
  {
3296
3745
  "data-testid": "chat-model-reload-icon",
@@ -3301,8 +3750,8 @@ var ChatModelControl = ({
3301
3750
  fill: "currentColor",
3302
3751
  xmlns: "http://www.w3.org/2000/svg",
3303
3752
  children: [
3304
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M895.469672 511.745197c0-146.498562-82.099856-273.805016-202.788589-338.470805l22.072715-46.630017c-4.50664-12.609179-18.382673-19.176758-30.991852-14.670118l-92.436272 33.040511c-12.609179 4.50664-19.176758 18.382673-14.670118 30.991852l33.040511 92.436272c4.50664 12.609179 18.382673 19.176758 30.991852 14.670118l24.581861-51.92972c99.069343 54.335513 166.240185 159.596881 166.240185 280.561907 0 165.56685-125.817544 301.747415-287.057855 318.14692l0 0.022513c-17.730826 0-32.105209 14.374382-32.105209 32.105209 0 17.730826 14.374382 32.105209 32.105209 32.105209 2.098801 0 4.149507-0.207731 6.135744-0.592494C744.270041 874.039593 895.469672 710.564381 895.469672 511.745197z" }),
3305
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M480.616222 129.23948c-0.041956 0-0.082888 0.00307-0.124843 0.00307l0-0.00307c-0.01535 0.001023-0.031722 0.00307-0.047072 0.004093-1.892093 0.010233-3.744277 0.189312-5.545296 0.5137-194.674794 18.529005-346.957083 182.459588-346.957083 381.987924 0 147.431817 83.146699 275.42798 205.097168 339.700819l-24.814152 52.419883c4.50664 12.609179 18.382673 19.176758 30.991852 14.670118l92.436272-33.040511c12.609179-4.50664 19.176758-18.382673 14.670118-30.991852l-33.040511-92.436272c-4.50664-12.609179-18.382673-19.176758-30.991852-14.670118l-21.853727 46.167482c-100.326986-53.964052-168.535461-159.920246-168.535461-281.81955 0-166.089759 126.616746-302.591643 288.588721-318.284043l0-0.014326c0.041956 0 0.082888 0.00307 0.124843 0.00307 17.730826 0 32.105209-14.374382 32.105209-32.105209C512.721431 143.613862 498.347049 129.23948 480.616222 129.23948z" })
3753
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M895.469672 511.745197c0-146.498562-82.099856-273.805016-202.788589-338.470805l22.072715-46.630017c-4.50664-12.609179-18.382673-19.176758-30.991852-14.670118l-92.436272 33.040511c-12.609179 4.50664-19.176758 18.382673-14.670118 30.991852l33.040511 92.436272c4.50664 12.609179 18.382673 19.176758 30.991852 14.670118l24.581861-51.92972c99.069343 54.335513 166.240185 159.596881 166.240185 280.561907 0 165.56685-125.817544 301.747415-287.057855 318.14692l0 0.022513c-17.730826 0-32.105209 14.374382-32.105209 32.105209 0 17.730826 14.374382 32.105209 32.105209 32.105209 2.098801 0 4.149507-0.207731 6.135744-0.592494C744.270041 874.039593 895.469672 710.564381 895.469672 511.745197z" }),
3754
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M480.616222 129.23948c-0.041956 0-0.082888 0.00307-0.124843 0.00307l0-0.00307c-0.01535 0.001023-0.031722 0.00307-0.047072 0.004093-1.892093 0.010233-3.744277 0.189312-5.545296 0.5137-194.674794 18.529005-346.957083 182.459588-346.957083 381.987924 0 147.431817 83.146699 275.42798 205.097168 339.700819l-24.814152 52.419883c4.50664 12.609179 18.382673 19.176758 30.991852 14.670118l92.436272-33.040511c12.609179-4.50664 19.176758-18.382673 14.670118-30.991852l-33.040511-92.436272c-4.50664-12.609179-18.382673-19.176758-30.991852-14.670118l-21.853727 46.167482c-100.326986-53.964052-168.535461-159.920246-168.535461-281.81955 0-166.089759 126.616746-302.591643 288.588721-318.284043l0-0.014326c0.041956 0 0.082888 0.00307 0.124843 0.00307 17.730826 0 32.105209-14.374382 32.105209-32.105209C512.721431 143.613862 498.347049 129.23948 480.616222 129.23948z" })
3306
3755
  ]
3307
3756
  }
3308
3757
  )
@@ -3311,11 +3760,11 @@ var ChatModelControl = ({
3311
3760
  );
3312
3761
  }
3313
3762
  if (isModelsLoading) {
3314
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ModelBadge, { children: "Loading models..." });
3763
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ModelBadge, { children: "Loading models..." });
3315
3764
  }
3316
3765
  if (hasModels && selectedModel) {
3317
3766
  if (availableModels.length > 1) {
3318
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3767
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3319
3768
  ModelSelect,
3320
3769
  {
3321
3770
  "data-testid": "chat-model-select",
@@ -3329,11 +3778,11 @@ var ChatModelControl = ({
3329
3778
  }
3330
3779
  );
3331
3780
  }
3332
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ModelBadge, { children: selectedModel });
3781
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ModelBadge, { children: selectedModel });
3333
3782
  }
3334
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ModelBadge, { children: "No model available" });
3783
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ModelBadge, { children: "No model available" });
3335
3784
  };
3336
- var ModelBadge = import_styled12.default.span`
3785
+ var ModelBadge = import_styled11.default.span`
3337
3786
  border-radius: 999px;
3338
3787
  border: 1px solid var(--border-hover);
3339
3788
  padding: 5px 12px;
@@ -3342,7 +3791,7 @@ var ModelBadge = import_styled12.default.span`
3342
3791
  color: var(--text-secondary);
3343
3792
  line-height: 12px;
3344
3793
  `;
3345
- var ModelReloadButton = import_styled12.default.button`
3794
+ var ModelReloadButton = import_styled11.default.button`
3346
3795
  display: inline-flex;
3347
3796
  align-items: center;
3348
3797
  gap: 8px;
@@ -3367,10 +3816,10 @@ var ModelReloadButton = import_styled12.default.button`
3367
3816
  color: rgba(255, 255, 255, 0.88);
3368
3817
  }
3369
3818
  `;
3370
- var ReloadIcon = import_styled12.default.svg`
3819
+ var ReloadIcon = import_styled11.default.svg`
3371
3820
  flex-shrink: 0;
3372
3821
  `;
3373
- var ModelSelect = (0, import_styled12.default)(import_compass_ui.Select)`
3822
+ var ModelSelect = (0, import_styled11.default)(import_compass_ui.Select)`
3374
3823
  && {
3375
3824
  width: auto;
3376
3825
  min-width: 0;
@@ -3390,16 +3839,16 @@ var ModelSelect = (0, import_styled12.default)(import_compass_ui.Select)`
3390
3839
  `;
3391
3840
 
3392
3841
  // src/components/chat-composer/components/chat-mode-control.tsx
3393
- var import_styled13 = __toESM(require("@emotion/styled"));
3842
+ var import_styled12 = __toESM(require("@emotion/styled"));
3394
3843
  var import_compass_ui2 = require("@xinghunm/compass-ui");
3395
- var import_jsx_runtime14 = require("@emotion/react/jsx-runtime");
3844
+ var import_jsx_runtime13 = require("@emotion/react/jsx-runtime");
3396
3845
  var ChatModeControl = ({
3397
3846
  value,
3398
3847
  disabled = false,
3399
3848
  labels,
3400
3849
  onChange
3401
3850
  }) => {
3402
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3851
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3403
3852
  ModeSelect,
3404
3853
  {
3405
3854
  "data-testid": "chat-mode-select",
@@ -3414,7 +3863,7 @@ var ChatModeControl = ({
3414
3863
  }
3415
3864
  );
3416
3865
  };
3417
- var ModeSelect = (0, import_styled13.default)(import_compass_ui2.Select)`
3866
+ var ModeSelect = (0, import_styled12.default)(import_compass_ui2.Select)`
3418
3867
  && {
3419
3868
  flex: 0 1 auto;
3420
3869
  width: auto;
@@ -3435,10 +3884,10 @@ var ModeSelect = (0, import_styled13.default)(import_compass_ui2.Select)`
3435
3884
  `;
3436
3885
 
3437
3886
  // src/components/chat-composer/components/chat-send-actions.tsx
3438
- var import_styled14 = __toESM(require("@emotion/styled"));
3887
+ var import_styled13 = __toESM(require("@emotion/styled"));
3439
3888
  var import_compass_ui3 = require("@xinghunm/compass-ui");
3440
- var import_jsx_runtime15 = require("@emotion/react/jsx-runtime");
3441
- var ArrowUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3889
+ var import_jsx_runtime14 = require("@emotion/react/jsx-runtime");
3890
+ var ArrowUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3442
3891
  "svg",
3443
3892
  {
3444
3893
  "aria-hidden": "true",
@@ -3447,7 +3896,7 @@ var ArrowUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3447
3896
  viewBox: "0 0 12 12",
3448
3897
  fill: "none",
3449
3898
  xmlns: "http://www.w3.org/2000/svg",
3450
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3899
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3451
3900
  "path",
3452
3901
  {
3453
3902
  d: "M6 10V2M6 2L2 6M6 2L10 6",
@@ -3465,7 +3914,7 @@ var ChatSendActions = ({
3465
3914
  isStopping,
3466
3915
  onStop,
3467
3916
  onSend
3468
- }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: isStreaming ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3917
+ }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: isStreaming ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3469
3918
  StopButton,
3470
3919
  {
3471
3920
  type: "button",
@@ -3475,14 +3924,14 @@ var ChatSendActions = ({
3475
3924
  disabled: isStopping,
3476
3925
  shape: "circle",
3477
3926
  onClick: () => void onStop(),
3478
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(StopGlyph, { "aria-hidden": "true" })
3927
+ children: isStopping ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StopSpinner, { "aria-hidden": "true", "data-testid": "chat-composer-stop-spinner" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StopGlyph, { "aria-hidden": "true", "data-testid": "chat-composer-stop-glyph" })
3479
3928
  }
3480
- ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3929
+ ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3481
3930
  PrimaryButton,
3482
3931
  {
3483
3932
  $canSend: canSend,
3484
3933
  type: "button",
3485
- icon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ArrowUpIcon, {}),
3934
+ icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ArrowUpIcon, {}),
3486
3935
  "aria-label": "Send",
3487
3936
  "data-testid": "chat-composer-send",
3488
3937
  disabled: !canSend,
@@ -3490,7 +3939,7 @@ var ChatSendActions = ({
3490
3939
  onClick: () => void onSend()
3491
3940
  }
3492
3941
  ) });
3493
- var PrimaryButton = (0, import_styled14.default)(import_compass_ui3.Button)`
3942
+ var PrimaryButton = (0, import_styled13.default)(import_compass_ui3.Button)`
3494
3943
  && {
3495
3944
  min-width: 24px;
3496
3945
  width: 24px;
@@ -3516,7 +3965,7 @@ var PrimaryButton = (0, import_styled14.default)(import_compass_ui3.Button)`
3516
3965
  }
3517
3966
  }
3518
3967
  `;
3519
- var StopButton = (0, import_styled14.default)(import_compass_ui3.Button)`
3968
+ var StopButton = (0, import_styled13.default)(import_compass_ui3.Button)`
3520
3969
  && {
3521
3970
  min-width: 24px;
3522
3971
  width: 24px;
@@ -3544,17 +3993,35 @@ var StopButton = (0, import_styled14.default)(import_compass_ui3.Button)`
3544
3993
  }
3545
3994
  }
3546
3995
  `;
3547
- var StopGlyph = import_styled14.default.span`
3996
+ var StopGlyph = import_styled13.default.span`
3548
3997
  width: 8px;
3549
3998
  height: 8px;
3550
3999
  border-radius: 2px;
3551
4000
  background: #1b1b1b;
3552
4001
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
3553
4002
  `;
4003
+ var StopSpinner = import_styled13.default.span`
4004
+ width: 10px;
4005
+ height: 10px;
4006
+ border-radius: 999px;
4007
+ border: 1.5px solid rgba(27, 27, 27, 0.2);
4008
+ border-top-color: #1b1b1b;
4009
+ animation: chat-composer-stop-spin 0.7s linear infinite;
4010
+
4011
+ @keyframes chat-composer-stop-spin {
4012
+ from {
4013
+ transform: rotate(0deg);
4014
+ }
4015
+
4016
+ to {
4017
+ transform: rotate(360deg);
4018
+ }
4019
+ }
4020
+ `;
3554
4021
 
3555
4022
  // src/components/chat-composer/index.tsx
3556
- var import_jsx_runtime16 = require("@emotion/react/jsx-runtime");
3557
- var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4023
+ var import_jsx_runtime15 = require("@emotion/react/jsx-runtime");
4024
+ var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3558
4025
  "svg",
3559
4026
  {
3560
4027
  "aria-hidden": "true",
@@ -3563,7 +4030,7 @@ var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3563
4030
  viewBox: "0 0 16 16",
3564
4031
  fill: "none",
3565
4032
  xmlns: "http://www.w3.org/2000/svg",
3566
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M8 3v10M3 8h10", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" })
4033
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M8 3v10M3 8h10", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" })
3567
4034
  }
3568
4035
  );
3569
4036
  var ChatComposerView = ({
@@ -3623,8 +4090,8 @@ var ChatComposerView = ({
3623
4090
  event.preventDefault();
3624
4091
  onPasteImages(imageFiles);
3625
4092
  };
3626
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Container2, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Surface, { "data-testid": "chat-composer-surface", children: [
3627
- enableImageAttachments ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4093
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Container2, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Surface, { "data-testid": "chat-composer-surface", children: [
4094
+ enableImageAttachments ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3628
4095
  "input",
3629
4096
  {
3630
4097
  ref: imageInputRef,
@@ -3636,15 +4103,15 @@ var ChatComposerView = ({
3636
4103
  onChange: handlePickImages
3637
4104
  }
3638
4105
  ) : null,
3639
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4106
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3640
4107
  ChatComposerAttachmentList,
3641
4108
  {
3642
4109
  attachments,
3643
4110
  onRemoveAttachment
3644
4111
  }
3645
4112
  ),
3646
- attachmentNotice === "limit_reached" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AttachmentNotice, { "data-testid": "chat-composer-attachment-notice", children: attachmentLimitNotice }) : null,
3647
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4113
+ attachmentNotice === "limit_reached" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AttachmentNotice, { "data-testid": "chat-composer-attachment-notice", children: attachmentLimitNotice }) : null,
4114
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3648
4115
  Input,
3649
4116
  {
3650
4117
  "data-testid": "chat-composer-input",
@@ -3655,18 +4122,18 @@ var ChatComposerView = ({
3655
4122
  placeholder
3656
4123
  }
3657
4124
  ),
3658
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Footer, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Actions2, { "data-testid": "chat-composer-actions", children: [
3659
- enableImageAttachments ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4125
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Footer, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Actions2, { "data-testid": "chat-composer-actions", children: [
4126
+ enableImageAttachments ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3660
4127
  AttachButton,
3661
4128
  {
3662
4129
  type: "button",
3663
4130
  "data-testid": "chat-composer-attach-image",
3664
4131
  "aria-label": "Attach image",
3665
4132
  onClick: () => imageInputRef.current?.click(),
3666
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PlusIcon, {})
4133
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PlusIcon, {})
3667
4134
  }
3668
4135
  ) : null,
3669
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4136
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3670
4137
  ChatModeControl,
3671
4138
  {
3672
4139
  value: selectedMode,
@@ -3675,7 +4142,7 @@ var ChatComposerView = ({
3675
4142
  onChange: onSelectedModeChange
3676
4143
  }
3677
4144
  ),
3678
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4145
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3679
4146
  ChatModelControl,
3680
4147
  {
3681
4148
  selectedModel,
@@ -3687,7 +4154,7 @@ var ChatComposerView = ({
3687
4154
  onReloadModels
3688
4155
  }
3689
4156
  ),
3690
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4157
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3691
4158
  ChatSendActions,
3692
4159
  {
3693
4160
  canSend,
@@ -3715,7 +4182,7 @@ var ChatComposer = () => {
3715
4182
  plan: labels.modeLabelPlan,
3716
4183
  agent: labels.modeLabelAgent
3717
4184
  };
3718
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4185
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3719
4186
  ChatComposerView,
3720
4187
  {
3721
4188
  value: state.value,
@@ -3745,10 +4212,10 @@ var ChatComposer = () => {
3745
4212
  }
3746
4213
  );
3747
4214
  };
3748
- var Container2 = import_styled15.default.div`
4215
+ var Container2 = import_styled14.default.div`
3749
4216
  padding: 0 16px 16px;
3750
4217
  `;
3751
- var Surface = import_styled15.default.div`
4218
+ var Surface = import_styled14.default.div`
3752
4219
  background: var(--border-color);
3753
4220
  border-radius: 20px;
3754
4221
  border: 1px solid var(--border-hover);
@@ -3757,7 +4224,7 @@ var Surface = import_styled15.default.div`
3757
4224
  0 12px 36px rgba(0, 0, 0, 0.3);
3758
4225
  backdrop-filter: blur(10px);
3759
4226
  `;
3760
- var AttachmentNotice = import_styled15.default.div`
4227
+ var AttachmentNotice = import_styled14.default.div`
3761
4228
  margin: 10px 12px 0;
3762
4229
  padding: 8px 10px;
3763
4230
  border-radius: 10px;
@@ -3767,7 +4234,7 @@ var AttachmentNotice = import_styled15.default.div`
3767
4234
  font-size: 12px;
3768
4235
  line-height: 1.4;
3769
4236
  `;
3770
- var Input = import_styled15.default.textarea`
4237
+ var Input = import_styled14.default.textarea`
3771
4238
  width: 100%;
3772
4239
  min-height: 96px;
3773
4240
  resize: none;
@@ -3789,14 +4256,14 @@ var Input = import_styled15.default.textarea`
3789
4256
  display: none;
3790
4257
  }
3791
4258
  `;
3792
- var Footer = import_styled15.default.div`
4259
+ var Footer = import_styled14.default.div`
3793
4260
  display: flex;
3794
4261
  align-items: flex-end;
3795
4262
  justify-content: stretch;
3796
4263
  gap: 16px;
3797
4264
  padding: 0 14px 14px;
3798
4265
  `;
3799
- var Actions2 = import_styled15.default.div`
4266
+ var Actions2 = import_styled14.default.div`
3800
4267
  display: flex;
3801
4268
  align-items: center;
3802
4269
  flex-wrap: wrap;
@@ -3805,7 +4272,7 @@ var Actions2 = import_styled15.default.div`
3805
4272
  justify-content: flex-end;
3806
4273
  gap: 8px;
3807
4274
  `;
3808
- var AttachButton = import_styled15.default.button`
4275
+ var AttachButton = import_styled14.default.button`
3809
4276
  width: 28px;
3810
4277
  height: 28px;
3811
4278
  display: grid;
@@ -3822,42 +4289,42 @@ var AttachButton = import_styled15.default.button`
3822
4289
  `;
3823
4290
 
3824
4291
  // src/components/chat-conversation-list/index.tsx
3825
- var import_styled17 = __toESM(require("@emotion/styled"));
4292
+ var import_styled16 = __toESM(require("@emotion/styled"));
3826
4293
 
3827
4294
  // src/components/chat-conversation-list/components/chat-session-item.tsx
3828
4295
  var import_react16 = require("react");
3829
- var import_styled16 = __toESM(require("@emotion/styled"));
3830
- var import_jsx_runtime17 = require("@emotion/react/jsx-runtime");
4296
+ var import_styled15 = __toESM(require("@emotion/styled"));
4297
+ var import_jsx_runtime16 = require("@emotion/react/jsx-runtime");
3831
4298
  var ChatSessionItem = (0, import_react16.memo)(
3832
4299
  ({ session, isActive, modeLabel, onClick }) => {
3833
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
4300
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3834
4301
  SessionButton,
3835
4302
  {
3836
4303
  type: "button",
3837
4304
  "data-active": isActive,
3838
4305
  onClick: () => onClick(session.sessionId),
3839
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(SessionMeta, { children: [
3840
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SessionTitle, { children: session.title }),
3841
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ModeBadge, { children: modeLabel })
4306
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(SessionMeta, { children: [
4307
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SessionTitle, { children: session.title }),
4308
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ModeBadge, { children: modeLabel })
3842
4309
  ] })
3843
4310
  }
3844
4311
  );
3845
4312
  }
3846
4313
  );
3847
4314
  ChatSessionItem.displayName = "ChatSessionItem";
3848
- var SessionMeta = import_styled16.default.div`
4315
+ var SessionMeta = import_styled15.default.div`
3849
4316
  display: flex;
3850
4317
  align-items: center;
3851
4318
  justify-content: space-between;
3852
4319
  gap: 8px;
3853
4320
  `;
3854
- var SessionTitle = import_styled16.default.span`
4321
+ var SessionTitle = import_styled15.default.span`
3855
4322
  min-width: 0;
3856
4323
  overflow: hidden;
3857
4324
  text-overflow: ellipsis;
3858
4325
  white-space: nowrap;
3859
4326
  `;
3860
- var SessionButton = import_styled16.default.button`
4327
+ var SessionButton = import_styled15.default.button`
3861
4328
  border: 1px solid transparent;
3862
4329
  border-radius: 12px;
3863
4330
  padding: 12px;
@@ -3871,7 +4338,7 @@ var SessionButton = import_styled16.default.button`
3871
4338
  background: rgba(255, 255, 255, 0.08);
3872
4339
  }
3873
4340
  `;
3874
- var ModeBadge = import_styled16.default.span`
4341
+ var ModeBadge = import_styled15.default.span`
3875
4342
  flex-shrink: 0;
3876
4343
  border-radius: 999px;
3877
4344
  border: 1px solid rgba(255, 255, 255, 0.1);
@@ -3883,7 +4350,7 @@ var ModeBadge = import_styled16.default.span`
3883
4350
  `;
3884
4351
 
3885
4352
  // src/components/chat-conversation-list/index.tsx
3886
- var import_jsx_runtime18 = require("@emotion/react/jsx-runtime");
4353
+ var import_jsx_runtime17 = require("@emotion/react/jsx-runtime");
3887
4354
  var ChatConversationList = () => {
3888
4355
  const { labels } = useChatContext();
3889
4356
  const sessions = useChatStore((s) => s.sessions);
@@ -3906,12 +4373,12 @@ var ChatConversationList = () => {
3906
4373
  });
3907
4374
  createSession(session);
3908
4375
  };
3909
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Container3, { children: [
3910
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Toolbar, { children: [
3911
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Title4, { children: "Sessions" }),
3912
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick: handleCreateSession, children: labels.newChat })
4376
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Container3, { children: [
4377
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Toolbar, { children: [
4378
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Title4, { children: "Sessions" }),
4379
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick: handleCreateSession, children: labels.newChat })
3913
4380
  ] }),
3914
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(List2, { "data-testid": "chat-session-list", children: sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4381
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(List2, { "data-testid": "chat-session-list", children: sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3915
4382
  ChatSessionItem,
3916
4383
  {
3917
4384
  session,
@@ -3923,7 +4390,7 @@ var ChatConversationList = () => {
3923
4390
  )) })
3924
4391
  ] });
3925
4392
  };
3926
- var Container3 = import_styled17.default.aside`
4393
+ var Container3 = import_styled16.default.aside`
3927
4394
  width: 280px;
3928
4395
  min-width: 280px;
3929
4396
  border-right: 1px solid var(--border-default, rgba(255, 255, 255, 0.08));
@@ -3931,18 +4398,18 @@ var Container3 = import_styled17.default.aside`
3931
4398
  flex-direction: column;
3932
4399
  background: rgba(255, 255, 255, 0.02);
3933
4400
  `;
3934
- var Toolbar = import_styled17.default.div`
4401
+ var Toolbar = import_styled16.default.div`
3935
4402
  padding: 20px 16px 12px;
3936
4403
  display: flex;
3937
4404
  flex-direction: column;
3938
4405
  gap: 12px;
3939
4406
  `;
3940
- var Title4 = import_styled17.default.h2`
4407
+ var Title4 = import_styled16.default.h2`
3941
4408
  margin: 0;
3942
4409
  font-size: 14px;
3943
4410
  color: var(--text-secondary);
3944
4411
  `;
3945
- var CreateButton = import_styled17.default.button`
4412
+ var CreateButton = import_styled16.default.button`
3946
4413
  border: none;
3947
4414
  border-radius: 12px;
3948
4415
  padding: 12px 14px;
@@ -3951,7 +4418,7 @@ var CreateButton = import_styled17.default.button`
3951
4418
  text-align: left;
3952
4419
  cursor: pointer;
3953
4420
  `;
3954
- var List2 = import_styled17.default.div`
4421
+ var List2 = import_styled16.default.div`
3955
4422
  padding: 0 12px 16px;
3956
4423
  display: flex;
3957
4424
  flex-direction: column;
@@ -3960,8 +4427,8 @@ var List2 = import_styled17.default.div`
3960
4427
  `;
3961
4428
 
3962
4429
  // src/components/ai-chat/index.tsx
3963
- var import_jsx_runtime19 = require("@emotion/react/jsx-runtime");
3964
- var AiChat = ({ showConversationList = false, ...providerProps }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4430
+ var import_jsx_runtime18 = require("@emotion/react/jsx-runtime");
4431
+ var AiChat = ({ showConversationList = false, ...providerProps }) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3965
4432
  import_compass_ui4.ConfigProvider,
3966
4433
  {
3967
4434
  theme: {
@@ -3996,23 +4463,23 @@ var AiChat = ({ showConversationList = false, ...providerProps }) => /* @__PURE_
3996
4463
  }
3997
4464
  }
3998
4465
  },
3999
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AiChatProvider, { ...providerProps, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Root, { "data-testid": "ai-chat", children: [
4000
- showConversationList ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatConversationList, {}) : null,
4001
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Workspace, { children: [
4002
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatThread, {}),
4003
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatComposer, {})
4466
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AiChatProvider, { ...providerProps, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Root, { "data-testid": "ai-chat", children: [
4467
+ showConversationList ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatConversationList, {}) : null,
4468
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Workspace, { children: [
4469
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatThread, {}),
4470
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatComposer, {})
4004
4471
  ] })
4005
4472
  ] }) })
4006
4473
  }
4007
4474
  );
4008
- var Root = import_styled18.default.div`
4475
+ var Root = import_styled17.default.div`
4009
4476
  display: flex;
4010
4477
  width: 100%;
4011
4478
  height: 100%;
4012
4479
  min-height: 0;
4013
4480
  overflow: hidden;
4014
4481
  `;
4015
- var Workspace = import_styled18.default.section`
4482
+ var Workspace = import_styled17.default.section`
4016
4483
  flex: 1;
4017
4484
  display: flex;
4018
4485
  flex-direction: column;
@@ -4024,6 +4491,7 @@ var Workspace = import_styled18.default.section`
4024
4491
  AiChat,
4025
4492
  AiChatProvider,
4026
4493
  CHAT_AGENT_MODES,
4494
+ CHAT_MESSAGE_RENDER_ORDERS,
4027
4495
  ChatComposer,
4028
4496
  ChatConversationList,
4029
4497
  ChatThread,