@xinghunm/ai-chat 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -9
- package/dist/index.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +497 -91
- package/dist/index.mjs +447 -42
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -16,6 +16,7 @@ import { createStore } from "zustand/vanilla";
|
|
|
16
16
|
// src/types/index.ts
|
|
17
17
|
var CHAT_AGENT_MODES = ["ask", "plan", "agent"];
|
|
18
18
|
var DEFAULT_CHAT_AGENT_MODE = "agent";
|
|
19
|
+
var CHAT_MESSAGE_RENDER_ORDERS = ["blocks-first", "timeline"];
|
|
19
20
|
var DEFAULT_AI_CHAT_LABELS = {
|
|
20
21
|
sendButton: "Send",
|
|
21
22
|
stopButton: "Stop",
|
|
@@ -517,7 +518,14 @@ var createDefaultChatTransport = ({
|
|
|
517
518
|
// src/components/ai-chat-provider/index.tsx
|
|
518
519
|
import { jsx } from "@emotion/react/jsx-runtime";
|
|
519
520
|
var AiChatProvider = (props) => {
|
|
520
|
-
const {
|
|
521
|
+
const {
|
|
522
|
+
defaultMode,
|
|
523
|
+
labels,
|
|
524
|
+
renderMessageBlock,
|
|
525
|
+
messageRenderOrder,
|
|
526
|
+
enableImageAttachments = true,
|
|
527
|
+
children
|
|
528
|
+
} = props;
|
|
521
529
|
const [store] = useState(
|
|
522
530
|
() => createChatStore(defaultMode ? { preferredMode: defaultMode } : void 0)
|
|
523
531
|
);
|
|
@@ -569,6 +577,7 @@ var AiChatProvider = (props) => {
|
|
|
569
577
|
sendRef,
|
|
570
578
|
retryRef,
|
|
571
579
|
renderMessageBlock,
|
|
580
|
+
messageRenderOrder,
|
|
572
581
|
transformStreamPacket: defaultTransformStreamPacket,
|
|
573
582
|
enableImageAttachments
|
|
574
583
|
}),
|
|
@@ -579,6 +588,7 @@ var AiChatProvider = (props) => {
|
|
|
579
588
|
defaultTransformStreamPacket,
|
|
580
589
|
enableImageAttachments,
|
|
581
590
|
labels,
|
|
591
|
+
messageRenderOrder,
|
|
582
592
|
renderMessageBlock,
|
|
583
593
|
sendRef,
|
|
584
594
|
retryRef,
|
|
@@ -590,7 +600,7 @@ var AiChatProvider = (props) => {
|
|
|
590
600
|
};
|
|
591
601
|
|
|
592
602
|
// src/components/chat-thread/index.tsx
|
|
593
|
-
import { useCallback as useCallback2, useLayoutEffect, useMemo as
|
|
603
|
+
import { useCallback as useCallback2, useLayoutEffect, useMemo as useMemo4, useRef as useRef4, useState as useState4 } from "react";
|
|
594
604
|
import styled9 from "@emotion/styled";
|
|
595
605
|
|
|
596
606
|
// src/context/use-chat-context.ts
|
|
@@ -851,6 +861,7 @@ var useChatMessageReveal = (message) => {
|
|
|
851
861
|
];
|
|
852
862
|
return {
|
|
853
863
|
isAssistantStreaming,
|
|
864
|
+
isFreshBlockActive,
|
|
854
865
|
displayedContent,
|
|
855
866
|
settledContent,
|
|
856
867
|
freshContent,
|
|
@@ -858,6 +869,325 @@ var useChatMessageReveal = (message) => {
|
|
|
858
869
|
};
|
|
859
870
|
};
|
|
860
871
|
|
|
872
|
+
// src/components/chat-thread/hooks/use-timeline-block-anchors.ts
|
|
873
|
+
import { useEffect as useEffect2, useMemo as useMemo3, useReducer as useReducer2 } from "react";
|
|
874
|
+
|
|
875
|
+
// src/components/chat-thread/lib/chat-message-timeline.ts
|
|
876
|
+
var stringifyTimelineKeyPart = (value) => {
|
|
877
|
+
if (value === null || value === void 0) {
|
|
878
|
+
return String(value);
|
|
879
|
+
}
|
|
880
|
+
if (Array.isArray(value)) {
|
|
881
|
+
return `[${value.map((item) => stringifyTimelineKeyPart(item)).join(",")}]`;
|
|
882
|
+
}
|
|
883
|
+
if (typeof value === "object") {
|
|
884
|
+
return `{${Object.entries(value).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)).map(([key, nestedValue]) => `${key}:${stringifyTimelineKeyPart(nestedValue)}`).join(",")}}`;
|
|
885
|
+
}
|
|
886
|
+
return String(value);
|
|
887
|
+
};
|
|
888
|
+
var getTimelineBlockKey = (block, index) => {
|
|
889
|
+
switch (block.type) {
|
|
890
|
+
case "markdown":
|
|
891
|
+
return null;
|
|
892
|
+
case "notice":
|
|
893
|
+
return `${index}:notice:${block.tone}:${block.text}`;
|
|
894
|
+
case "parameter_summary":
|
|
895
|
+
return `${index}:parameter_summary:${block.items.map((item) => `${item.label}:${item.value}:${item.fieldPath ?? ""}`).join("|")}`;
|
|
896
|
+
case "confirmation_card":
|
|
897
|
+
return `${index}:confirmation_card:${block.proposal.proposalId}`;
|
|
898
|
+
case "result_summary":
|
|
899
|
+
return `${index}:result_summary:${block.summary.taskId}:${block.summary.status}`;
|
|
900
|
+
case "questionnaire":
|
|
901
|
+
return `${index}:questionnaire:${block.questionnaire.questionnaireId}`;
|
|
902
|
+
case "custom":
|
|
903
|
+
return `${index}:custom:${block.kind}:${stringifyTimelineKeyPart(block.data)}`;
|
|
904
|
+
default:
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
var getTimelineConsumedText = (blocks) => blocks.filter(
|
|
909
|
+
(block) => block.type === "markdown"
|
|
910
|
+
).map((block) => block.text).join("\n\n");
|
|
911
|
+
var getTimelineTextStream = (content, blocks) => {
|
|
912
|
+
const consumedText = getTimelineConsumedText(blocks);
|
|
913
|
+
if (consumedText.length > 0 && content.startsWith(consumedText)) {
|
|
914
|
+
return content.slice(consumedText.length);
|
|
915
|
+
}
|
|
916
|
+
return content;
|
|
917
|
+
};
|
|
918
|
+
var buildTimelineTextDisplay = (content, isAssistantStreaming, isFreshBlockActive = isAssistantStreaming) => {
|
|
919
|
+
const contentBlocks = splitMarkdownBlocks(content);
|
|
920
|
+
const settledContent = isAssistantStreaming && isFreshBlockActive && contentBlocks.length > 1 ? contentBlocks.slice(0, -1).join("\n\n") : content;
|
|
921
|
+
const freshContent = isAssistantStreaming && isFreshBlockActive && contentBlocks.length > 1 ? contentBlocks[contentBlocks.length - 1] ?? "" : "";
|
|
922
|
+
const displayedBlocks = contentBlocks.length > 1 ? contentBlocks.map((blockContent, index) => ({
|
|
923
|
+
content: blockContent,
|
|
924
|
+
tone: isAssistantStreaming && isFreshBlockActive && freshContent && index === contentBlocks.length - 1 ? "fresh" : "settled"
|
|
925
|
+
})) : [{ content, tone: "settled" }];
|
|
926
|
+
return {
|
|
927
|
+
settledContent,
|
|
928
|
+
freshContent,
|
|
929
|
+
displayedBlocks
|
|
930
|
+
};
|
|
931
|
+
};
|
|
932
|
+
var getTimelineDisplayUnitCount = (content) => splitMarkdownBlocks(content).reduce((count, block) => count + Array.from(block).length, 0);
|
|
933
|
+
var buildAnchoredTimelineSegments = ({
|
|
934
|
+
blocks,
|
|
935
|
+
timelineBlockAnchors,
|
|
936
|
+
timelineDisplayedBlocks,
|
|
937
|
+
visibleTimelineBlockKeys
|
|
938
|
+
}) => {
|
|
939
|
+
const orderedTimelineSegments = [];
|
|
940
|
+
const totalTimelineUnits = timelineDisplayedBlocks.reduce(
|
|
941
|
+
(count, block) => count + Array.from(block.content).length,
|
|
942
|
+
0
|
|
943
|
+
);
|
|
944
|
+
let textCursor = 0;
|
|
945
|
+
const buildTextSegment = (start, end, options) => {
|
|
946
|
+
if (end <= start) {
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
const displayedBlocks = [];
|
|
950
|
+
let blockCursor = 0;
|
|
951
|
+
for (const block of timelineDisplayedBlocks) {
|
|
952
|
+
const blockUnits = Array.from(block.content);
|
|
953
|
+
const blockStart = blockCursor;
|
|
954
|
+
const blockEnd = blockCursor + blockUnits.length;
|
|
955
|
+
if (blockEnd <= start) {
|
|
956
|
+
blockCursor = blockEnd;
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
959
|
+
if (blockStart >= end) {
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
const sliceStart = Math.max(0, start - blockStart);
|
|
963
|
+
const sliceEnd = Math.min(blockUnits.length, end - blockStart);
|
|
964
|
+
const slicedContent = blockUnits.slice(sliceStart, sliceEnd).join("");
|
|
965
|
+
if (slicedContent) {
|
|
966
|
+
displayedBlocks.push({
|
|
967
|
+
content: slicedContent,
|
|
968
|
+
tone: options?.forceSettled ? "settled" : block.tone
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
blockCursor = blockEnd;
|
|
972
|
+
}
|
|
973
|
+
const content = displayedBlocks.map((block) => block.content).join("\n\n");
|
|
974
|
+
if (!content) {
|
|
975
|
+
return null;
|
|
976
|
+
}
|
|
977
|
+
return {
|
|
978
|
+
type: "text",
|
|
979
|
+
content,
|
|
980
|
+
displayedBlocks,
|
|
981
|
+
useTimelineSegmentation: true
|
|
982
|
+
};
|
|
983
|
+
};
|
|
984
|
+
let trailingCutoff = totalTimelineUnits;
|
|
985
|
+
for (const [index, block] of blocks.entries()) {
|
|
986
|
+
if (block.type === "markdown") {
|
|
987
|
+
orderedTimelineSegments.push({
|
|
988
|
+
type: "markdown",
|
|
989
|
+
content: block.text
|
|
990
|
+
});
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
const blockKey = getTimelineBlockKey(block, index);
|
|
994
|
+
const anchor = blockKey !== null ? timelineBlockAnchors[blockKey] ?? totalTimelineUnits : totalTimelineUnits;
|
|
995
|
+
const isBlockVisible = blockKey !== null && visibleTimelineBlockKeys?.[blockKey] ? true : anchor <= totalTimelineUnits;
|
|
996
|
+
if (anchor > textCursor) {
|
|
997
|
+
const textSegment = buildTextSegment(textCursor, Math.min(anchor, totalTimelineUnits), {
|
|
998
|
+
forceSettled: isBlockVisible
|
|
999
|
+
});
|
|
1000
|
+
if (textSegment) {
|
|
1001
|
+
orderedTimelineSegments.push(textSegment);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
if (!isBlockVisible) {
|
|
1005
|
+
textCursor = Math.min(anchor, totalTimelineUnits);
|
|
1006
|
+
trailingCutoff = Math.min(trailingCutoff, textCursor);
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
trailingCutoff = totalTimelineUnits;
|
|
1010
|
+
orderedTimelineSegments.push({
|
|
1011
|
+
type: "block",
|
|
1012
|
+
block,
|
|
1013
|
+
index
|
|
1014
|
+
});
|
|
1015
|
+
textCursor = Math.max(textCursor, anchor);
|
|
1016
|
+
}
|
|
1017
|
+
const trailingTextSegment = buildTextSegment(textCursor, trailingCutoff);
|
|
1018
|
+
if (trailingTextSegment) {
|
|
1019
|
+
orderedTimelineSegments.push(trailingTextSegment);
|
|
1020
|
+
}
|
|
1021
|
+
return orderedTimelineSegments;
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
// src/components/chat-thread/hooks/use-timeline-block-anchors.ts
|
|
1025
|
+
var createTimelineAnchorState = ({
|
|
1026
|
+
messageId,
|
|
1027
|
+
currentBlockKeys
|
|
1028
|
+
}) => ({
|
|
1029
|
+
messageId,
|
|
1030
|
+
previousBlockKeys: currentBlockKeys,
|
|
1031
|
+
timelineBlockAnchors: {},
|
|
1032
|
+
visibleTimelineBlockKeys: {}
|
|
1033
|
+
});
|
|
1034
|
+
var timelineAnchorReducer = (state, action) => {
|
|
1035
|
+
switch (action.type) {
|
|
1036
|
+
case "reset-message":
|
|
1037
|
+
if (state.messageId === action.messageId) {
|
|
1038
|
+
return state;
|
|
1039
|
+
}
|
|
1040
|
+
return createTimelineAnchorState(action);
|
|
1041
|
+
case "sync-anchors": {
|
|
1042
|
+
const previousBlockKeys = new Set(state.previousBlockKeys);
|
|
1043
|
+
const nextAnchors = action.currentBlockKeys.reduce(
|
|
1044
|
+
(acc, blockKey) => {
|
|
1045
|
+
const existingAnchor = state.timelineBlockAnchors[blockKey];
|
|
1046
|
+
if (existingAnchor !== void 0) {
|
|
1047
|
+
acc[blockKey] = existingAnchor;
|
|
1048
|
+
return acc;
|
|
1049
|
+
}
|
|
1050
|
+
if (!previousBlockKeys.has(blockKey)) {
|
|
1051
|
+
acc[blockKey] = action.timelineTextStreamLength;
|
|
1052
|
+
}
|
|
1053
|
+
return acc;
|
|
1054
|
+
},
|
|
1055
|
+
{}
|
|
1056
|
+
);
|
|
1057
|
+
const hasAnchorChanged = Object.keys(nextAnchors).length !== Object.keys(state.timelineBlockAnchors).length || Object.entries(nextAnchors).some(
|
|
1058
|
+
([blockKey, anchor]) => state.timelineBlockAnchors[blockKey] !== anchor
|
|
1059
|
+
);
|
|
1060
|
+
const hasPreviousKeysChanged = action.currentBlockKeys.length !== state.previousBlockKeys.length || action.currentBlockKeys.some(
|
|
1061
|
+
(blockKey, index) => state.previousBlockKeys[index] !== blockKey
|
|
1062
|
+
);
|
|
1063
|
+
if (!hasAnchorChanged && !hasPreviousKeysChanged) {
|
|
1064
|
+
return state;
|
|
1065
|
+
}
|
|
1066
|
+
return {
|
|
1067
|
+
...state,
|
|
1068
|
+
previousBlockKeys: action.currentBlockKeys,
|
|
1069
|
+
timelineBlockAnchors: hasAnchorChanged ? nextAnchors : state.timelineBlockAnchors
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
case "sync-visible": {
|
|
1073
|
+
const nextVisibleBlockKeys = action.currentBlockKeys.reduce(
|
|
1074
|
+
(acc, blockKey) => {
|
|
1075
|
+
if (state.visibleTimelineBlockKeys[blockKey]) {
|
|
1076
|
+
acc[blockKey] = true;
|
|
1077
|
+
return acc;
|
|
1078
|
+
}
|
|
1079
|
+
const anchor = action.effectiveTimelineBlockAnchors[blockKey];
|
|
1080
|
+
if (anchor !== void 0 && anchor <= action.displayedTimelineTextLength) {
|
|
1081
|
+
acc[blockKey] = true;
|
|
1082
|
+
}
|
|
1083
|
+
return acc;
|
|
1084
|
+
},
|
|
1085
|
+
{}
|
|
1086
|
+
);
|
|
1087
|
+
const hasVisibleBlockChanged = Object.keys(nextVisibleBlockKeys).length !== Object.keys(state.visibleTimelineBlockKeys).length || Object.keys(nextVisibleBlockKeys).some(
|
|
1088
|
+
(blockKey) => !state.visibleTimelineBlockKeys[blockKey]
|
|
1089
|
+
);
|
|
1090
|
+
if (!hasVisibleBlockChanged) {
|
|
1091
|
+
return state;
|
|
1092
|
+
}
|
|
1093
|
+
return {
|
|
1094
|
+
...state,
|
|
1095
|
+
visibleTimelineBlockKeys: nextVisibleBlockKeys
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
default:
|
|
1099
|
+
return state;
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
var useTimelineBlockAnchors = ({
|
|
1103
|
+
blocks,
|
|
1104
|
+
displayedTimelineTextLength,
|
|
1105
|
+
isAssistantStreaming,
|
|
1106
|
+
message,
|
|
1107
|
+
messageRenderOrder
|
|
1108
|
+
}) => {
|
|
1109
|
+
const currentTimelineBlockKeys = useMemo3(
|
|
1110
|
+
() => blocks.map((block, index) => getTimelineBlockKey(block, index)).filter((blockKey) => Boolean(blockKey)),
|
|
1111
|
+
[blocks]
|
|
1112
|
+
);
|
|
1113
|
+
const timelineTextStreamLength = useMemo3(
|
|
1114
|
+
() => getTimelineDisplayUnitCount(getTimelineTextStream(message.content, blocks)),
|
|
1115
|
+
[blocks, message.content]
|
|
1116
|
+
);
|
|
1117
|
+
const [state, dispatch] = useReducer2(
|
|
1118
|
+
timelineAnchorReducer,
|
|
1119
|
+
{
|
|
1120
|
+
messageId: message.id,
|
|
1121
|
+
currentBlockKeys: currentTimelineBlockKeys
|
|
1122
|
+
},
|
|
1123
|
+
createTimelineAnchorState
|
|
1124
|
+
);
|
|
1125
|
+
const effectiveTimelineBlockAnchors = useMemo3(() => {
|
|
1126
|
+
if (messageRenderOrder !== "timeline" || !isAssistantStreaming) {
|
|
1127
|
+
return state.timelineBlockAnchors;
|
|
1128
|
+
}
|
|
1129
|
+
const previousBlockKeys = new Set(state.previousBlockKeys);
|
|
1130
|
+
return currentTimelineBlockKeys.reduce(
|
|
1131
|
+
(acc, blockKey) => {
|
|
1132
|
+
const existingAnchor = state.timelineBlockAnchors[blockKey];
|
|
1133
|
+
if (existingAnchor !== void 0) {
|
|
1134
|
+
acc[blockKey] = existingAnchor;
|
|
1135
|
+
return acc;
|
|
1136
|
+
}
|
|
1137
|
+
if (!previousBlockKeys.has(blockKey)) {
|
|
1138
|
+
acc[blockKey] = timelineTextStreamLength;
|
|
1139
|
+
}
|
|
1140
|
+
return acc;
|
|
1141
|
+
},
|
|
1142
|
+
{ ...state.timelineBlockAnchors }
|
|
1143
|
+
);
|
|
1144
|
+
}, [
|
|
1145
|
+
currentTimelineBlockKeys,
|
|
1146
|
+
isAssistantStreaming,
|
|
1147
|
+
messageRenderOrder,
|
|
1148
|
+
state.previousBlockKeys,
|
|
1149
|
+
state.timelineBlockAnchors,
|
|
1150
|
+
timelineTextStreamLength
|
|
1151
|
+
]);
|
|
1152
|
+
useEffect2(() => {
|
|
1153
|
+
dispatch({
|
|
1154
|
+
type: "reset-message",
|
|
1155
|
+
messageId: message.id,
|
|
1156
|
+
currentBlockKeys: currentTimelineBlockKeys
|
|
1157
|
+
});
|
|
1158
|
+
}, [currentTimelineBlockKeys, message.id]);
|
|
1159
|
+
useEffect2(() => {
|
|
1160
|
+
if (messageRenderOrder !== "timeline" || !isAssistantStreaming) {
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
dispatch({
|
|
1164
|
+
type: "sync-anchors",
|
|
1165
|
+
currentBlockKeys: currentTimelineBlockKeys,
|
|
1166
|
+
timelineTextStreamLength
|
|
1167
|
+
});
|
|
1168
|
+
}, [currentTimelineBlockKeys, isAssistantStreaming, messageRenderOrder, timelineTextStreamLength]);
|
|
1169
|
+
useEffect2(() => {
|
|
1170
|
+
if (messageRenderOrder !== "timeline") {
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
dispatch({
|
|
1174
|
+
type: "sync-visible",
|
|
1175
|
+
currentBlockKeys: currentTimelineBlockKeys,
|
|
1176
|
+
effectiveTimelineBlockAnchors,
|
|
1177
|
+
displayedTimelineTextLength
|
|
1178
|
+
});
|
|
1179
|
+
}, [
|
|
1180
|
+
currentTimelineBlockKeys,
|
|
1181
|
+
displayedTimelineTextLength,
|
|
1182
|
+
effectiveTimelineBlockAnchors,
|
|
1183
|
+
messageRenderOrder
|
|
1184
|
+
]);
|
|
1185
|
+
return {
|
|
1186
|
+
timelineBlockAnchors: messageRenderOrder === "timeline" ? effectiveTimelineBlockAnchors : {},
|
|
1187
|
+
visibleTimelineBlockKeys: messageRenderOrder === "timeline" ? state.visibleTimelineBlockKeys : {}
|
|
1188
|
+
};
|
|
1189
|
+
};
|
|
1190
|
+
|
|
861
1191
|
// src/components/chat-thread/components/pde-ai-execution-confirmation-card.tsx
|
|
862
1192
|
import styled from "@emotion/styled";
|
|
863
1193
|
import { jsx as jsx2, jsxs } from "@emotion/react/jsx-runtime";
|
|
@@ -1609,7 +1939,7 @@ var Detail = styled5.li`
|
|
|
1609
1939
|
|
|
1610
1940
|
// src/components/chat-thread/components/image-viewer.tsx
|
|
1611
1941
|
import styled6 from "@emotion/styled";
|
|
1612
|
-
import { useEffect as
|
|
1942
|
+
import { useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
1613
1943
|
import { jsx as jsx7 } from "@emotion/react/jsx-runtime";
|
|
1614
1944
|
var Overlay = styled6.div`
|
|
1615
1945
|
position: fixed;
|
|
@@ -1629,7 +1959,7 @@ var Img = styled6.img`
|
|
|
1629
1959
|
`;
|
|
1630
1960
|
var ImageViewer = ({ src, alt, onClose }) => {
|
|
1631
1961
|
const overlayRef = useRef3(null);
|
|
1632
|
-
|
|
1962
|
+
useEffect3(() => {
|
|
1633
1963
|
const handleKey = (e) => {
|
|
1634
1964
|
if (e.key === "Escape")
|
|
1635
1965
|
onClose();
|
|
@@ -1637,7 +1967,7 @@ var ImageViewer = ({ src, alt, onClose }) => {
|
|
|
1637
1967
|
document.addEventListener("keydown", handleKey);
|
|
1638
1968
|
return () => document.removeEventListener("keydown", handleKey);
|
|
1639
1969
|
}, [onClose]);
|
|
1640
|
-
|
|
1970
|
+
useEffect3(() => {
|
|
1641
1971
|
overlayRef.current?.focus();
|
|
1642
1972
|
}, []);
|
|
1643
1973
|
const stopPropagation = (e) => e.stopPropagation();
|
|
@@ -1800,9 +2130,16 @@ var ChatMessageItemView = ({
|
|
|
1800
2130
|
onQuestionnaireSubmit,
|
|
1801
2131
|
renderMessageBlock
|
|
1802
2132
|
}) => {
|
|
1803
|
-
const { labels } = useChatContext();
|
|
2133
|
+
const { labels, messageRenderOrder = "blocks-first" } = useChatContext();
|
|
1804
2134
|
const [activeImage, setActiveImage] = useState3(void 0);
|
|
1805
|
-
const {
|
|
2135
|
+
const {
|
|
2136
|
+
displayedBlocks,
|
|
2137
|
+
displayedContent,
|
|
2138
|
+
freshContent,
|
|
2139
|
+
isAssistantStreaming,
|
|
2140
|
+
isFreshBlockActive,
|
|
2141
|
+
settledContent
|
|
2142
|
+
} = useChatMessageReveal(message);
|
|
1806
2143
|
const isStoppedAssistant = message.role === "assistant" && message.status === "stopped";
|
|
1807
2144
|
const attachments = message.attachments ?? [];
|
|
1808
2145
|
const blocks = message.blocks ?? [];
|
|
@@ -1814,6 +2151,22 @@ var ChatMessageItemView = ({
|
|
|
1814
2151
|
const canSubmitConfirmation = isPlanMode && typeof onConfirmationSubmit === "function";
|
|
1815
2152
|
const canSubmitQuestionnaire = isPlanMode && typeof onQuestionnaireSubmit === "function";
|
|
1816
2153
|
const shouldShowStreamingCaret = isAssistantStreaming && (!shouldRenderStructuredBlocks || hasTextContent);
|
|
2154
|
+
const timelineConsumedText = messageRenderOrder === "timeline" ? getTimelineConsumedText(blocks) : "";
|
|
2155
|
+
const hasConsumedTimelineText = timelineConsumedText.length > 0 && displayedContent.startsWith(timelineConsumedText);
|
|
2156
|
+
const timelineDisplayedContent = hasConsumedTimelineText ? displayedContent.slice(timelineConsumedText.length) : displayedContent;
|
|
2157
|
+
const timelineTextDisplay = buildTimelineTextDisplay(
|
|
2158
|
+
timelineDisplayedContent,
|
|
2159
|
+
isAssistantStreaming,
|
|
2160
|
+
isFreshBlockActive
|
|
2161
|
+
);
|
|
2162
|
+
const displayedTimelineTextLength = getTimelineDisplayUnitCount(timelineDisplayedContent);
|
|
2163
|
+
const { timelineBlockAnchors, visibleTimelineBlockKeys } = useTimelineBlockAnchors({
|
|
2164
|
+
blocks,
|
|
2165
|
+
displayedTimelineTextLength,
|
|
2166
|
+
isAssistantStreaming,
|
|
2167
|
+
message,
|
|
2168
|
+
messageRenderOrder
|
|
2169
|
+
});
|
|
1817
2170
|
const renderChatMessageBlock = (block, index) => {
|
|
1818
2171
|
switch (block.type) {
|
|
1819
2172
|
case "markdown":
|
|
@@ -1870,19 +2223,69 @@ var ChatMessageItemView = ({
|
|
|
1870
2223
|
return null;
|
|
1871
2224
|
}
|
|
1872
2225
|
};
|
|
1873
|
-
const renderTextContent = () =>
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
2226
|
+
const renderTextContent = (options) => {
|
|
2227
|
+
const textContent = options?.content ?? displayedContent;
|
|
2228
|
+
const localTimelineTextDisplay = options?.displayedBlocks ? void 0 : options?.useTimelineSegmentation && options.content !== void 0 ? buildTimelineTextDisplay(options.content, isAssistantStreaming, isFreshBlockActive) : void 0;
|
|
2229
|
+
const textBlocks = options?.displayedBlocks ?? localTimelineTextDisplay?.displayedBlocks ?? displayedBlocks;
|
|
2230
|
+
const settledText = localTimelineTextDisplay?.settledContent ?? settledContent;
|
|
2231
|
+
const freshText = localTimelineTextDisplay?.freshContent ?? freshContent;
|
|
2232
|
+
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
2233
|
+
textBlocks.filter((block) => block.content).map((block, index) => /* @__PURE__ */ jsx8(
|
|
2234
|
+
ContentBlock,
|
|
2235
|
+
{
|
|
2236
|
+
"data-testid": block.tone === "fresh" ? "chat-message-fresh-block" : "chat-message-settled-block",
|
|
2237
|
+
"data-block-tone": block.tone,
|
|
2238
|
+
"data-block-index": index,
|
|
2239
|
+
children: renderMarkdownContent(block.content)
|
|
2240
|
+
},
|
|
2241
|
+
`${block.tone}-${index}`
|
|
2242
|
+
)),
|
|
2243
|
+
!textBlocks.some((block) => block.content) && !settledText && !freshText && Boolean(textContent) ? /* @__PURE__ */ jsx8(ContentBlock, { "data-testid": "chat-message-settled-block", "data-block-tone": "settled", children: renderMarkdownContent(textContent) }) : null
|
|
2244
|
+
] });
|
|
2245
|
+
};
|
|
2246
|
+
const renderStaticTextSegment = (content) => /* @__PURE__ */ jsx8(ContentBlock, { "data-testid": "chat-message-settled-block", "data-block-tone": "settled", children: renderMarkdownContent(content) });
|
|
2247
|
+
const bodySegments = (() => {
|
|
2248
|
+
if (!shouldRenderStructuredBlocks && hasTextContent) {
|
|
2249
|
+
return [{ type: "text" }];
|
|
2250
|
+
}
|
|
2251
|
+
if (!shouldRenderStructuredBlocks) {
|
|
2252
|
+
return [];
|
|
2253
|
+
}
|
|
2254
|
+
if (messageRenderOrder === "timeline" && hasTextContent) {
|
|
2255
|
+
const hasAnchoredStructuredBlocks = blocks.some((block, index) => {
|
|
2256
|
+
const blockKey = getTimelineBlockKey(block, index);
|
|
2257
|
+
return blockKey ? timelineBlockAnchors[blockKey] !== void 0 : false;
|
|
2258
|
+
});
|
|
2259
|
+
if (hasAnchoredStructuredBlocks) {
|
|
2260
|
+
return buildAnchoredTimelineSegments({
|
|
2261
|
+
blocks,
|
|
2262
|
+
timelineBlockAnchors,
|
|
2263
|
+
timelineDisplayedBlocks: timelineTextDisplay.displayedBlocks,
|
|
2264
|
+
visibleTimelineBlockKeys
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
const orderedTimelineSegments = blocks.map(
|
|
2268
|
+
(block, index) => block.type === "markdown" ? {
|
|
2269
|
+
type: "markdown",
|
|
2270
|
+
content: block.text
|
|
2271
|
+
} : {
|
|
2272
|
+
type: "block",
|
|
2273
|
+
block,
|
|
2274
|
+
index
|
|
2275
|
+
}
|
|
2276
|
+
);
|
|
2277
|
+
if (!timelineConsumedText) {
|
|
2278
|
+
return displayedContent ? [{ type: "text", content: displayedContent }, ...orderedTimelineSegments] : orderedTimelineSegments;
|
|
2279
|
+
}
|
|
2280
|
+
return timelineDisplayedContent ? [...orderedTimelineSegments, { type: "text", content: timelineDisplayedContent }] : orderedTimelineSegments;
|
|
2281
|
+
}
|
|
2282
|
+
const orderedBlocks = blocks.map((block, index) => ({
|
|
2283
|
+
type: "block",
|
|
2284
|
+
block,
|
|
2285
|
+
index
|
|
2286
|
+
}));
|
|
2287
|
+
return hasTextContent ? [...orderedBlocks, { type: "text" }] : orderedBlocks;
|
|
2288
|
+
})();
|
|
1886
2289
|
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1887
2290
|
/* @__PURE__ */ jsxs5(Bubble, { "data-role": message.role, "data-status": message.status ?? "done", children: [
|
|
1888
2291
|
/* @__PURE__ */ jsxs5(Header2, { children: [
|
|
@@ -1901,17 +2304,18 @@ var ChatMessageItemView = ({
|
|
|
1901
2304
|
isStoppedAssistant ? /* @__PURE__ */ jsx8(StatusTag, { "data-testid": "chat-message-stopped-tag", children: labels.stoppedResponse }) : null
|
|
1902
2305
|
] }),
|
|
1903
2306
|
/* @__PURE__ */ jsxs5(Content, { "data-testid": "chat-message-content", children: [
|
|
1904
|
-
shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
2307
|
+
shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */ jsx8(ContentStack, { "data-testid": "chat-message-body-stack", children: bodySegments.map((segment, index) => /* @__PURE__ */ jsx8(
|
|
2308
|
+
ContentSegment,
|
|
2309
|
+
{
|
|
2310
|
+
"data-testid": "chat-message-content-segment",
|
|
2311
|
+
children: segment.type === "block" ? renderChatMessageBlock(segment.block, segment.index) : segment.type === "text" ? segment.content !== void 0 ? segment.useTimelineSegmentation ? renderTextContent({
|
|
2312
|
+
content: segment.content,
|
|
2313
|
+
displayedBlocks: segment.displayedBlocks,
|
|
2314
|
+
useTimelineSegmentation: true
|
|
2315
|
+
}) : renderStaticTextSegment(segment.content) : renderTextContent() : renderStaticTextSegment(segment.content)
|
|
2316
|
+
},
|
|
2317
|
+
segment.type === "text" ? `text-${index}` : segment.type === "markdown" ? `markdown-${index}` : `${segment.block.type}-${segment.index}`
|
|
2318
|
+
)) }) : null,
|
|
1915
2319
|
attachments.length ? /* @__PURE__ */ jsx8(AttachmentGrid, { "data-testid": "chat-message-attachment-grid", children: attachments.map((attachment) => /* @__PURE__ */ jsx8(
|
|
1916
2320
|
AttachmentButton,
|
|
1917
2321
|
{
|
|
@@ -2304,7 +2708,7 @@ var ChatThreadView = ({
|
|
|
2304
2708
|
renderMessageBlock
|
|
2305
2709
|
}) => {
|
|
2306
2710
|
const containerRef = useRef4(null);
|
|
2307
|
-
const conversationTurns =
|
|
2711
|
+
const conversationTurns = useMemo4(
|
|
2308
2712
|
() => groupConversationTurns(historyMessages, streamingMessage),
|
|
2309
2713
|
[historyMessages, streamingMessage]
|
|
2310
2714
|
);
|
|
@@ -2566,7 +2970,7 @@ var RetryButton = styled9.button`
|
|
|
2566
2970
|
`;
|
|
2567
2971
|
|
|
2568
2972
|
// src/components/chat-composer/index.tsx
|
|
2569
|
-
import { useEffect as
|
|
2973
|
+
import { useEffect as useEffect6, useRef as useRef7 } from "react";
|
|
2570
2974
|
import styled14 from "@emotion/styled";
|
|
2571
2975
|
|
|
2572
2976
|
// src/components/chat-composer/lib/chat-composer.ts
|
|
@@ -2678,10 +3082,10 @@ var resolveSendSession = ({
|
|
|
2678
3082
|
};
|
|
2679
3083
|
|
|
2680
3084
|
// src/components/chat-composer/hooks/use-chat-composer.ts
|
|
2681
|
-
import { useCallback as useCallback3, useEffect as
|
|
3085
|
+
import { useCallback as useCallback3, useEffect as useEffect5, useRef as useRef6, useState as useState6 } from "react";
|
|
2682
3086
|
|
|
2683
3087
|
// src/components/chat-composer/hooks/use-composer-attachments.ts
|
|
2684
|
-
import { useEffect as
|
|
3088
|
+
import { useEffect as useEffect4, useRef as useRef5, useState as useState5 } from "react";
|
|
2685
3089
|
var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp"]);
|
|
2686
3090
|
var MAX_COMPOSER_ATTACHMENTS = 10;
|
|
2687
3091
|
var createObjectUrl = (file) => typeof URL !== "undefined" && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : "";
|
|
@@ -2697,10 +3101,10 @@ var releaseComposerAttachments = (attachments) => {
|
|
|
2697
3101
|
var useComposerAttachments = () => {
|
|
2698
3102
|
const [attachments, setAttachments] = useState5([]);
|
|
2699
3103
|
const attachmentsRef = useRef5([]);
|
|
2700
|
-
|
|
3104
|
+
useEffect4(() => {
|
|
2701
3105
|
attachmentsRef.current = attachments;
|
|
2702
3106
|
}, [attachments]);
|
|
2703
|
-
|
|
3107
|
+
useEffect4(
|
|
2704
3108
|
() => () => {
|
|
2705
3109
|
releaseComposerAttachments(attachmentsRef.current);
|
|
2706
3110
|
},
|
|
@@ -2828,7 +3232,7 @@ var useChatComposer = () => {
|
|
|
2828
3232
|
setIsModelsLoading(false);
|
|
2829
3233
|
}
|
|
2830
3234
|
}, [transport]);
|
|
2831
|
-
|
|
3235
|
+
useEffect5(() => {
|
|
2832
3236
|
void fetchModels();
|
|
2833
3237
|
}, [fetchModels]);
|
|
2834
3238
|
const hasModels = availableModels.length > 0;
|
|
@@ -2840,19 +3244,19 @@ var useChatComposer = () => {
|
|
|
2840
3244
|
const abortControllerRef = useRef6(null);
|
|
2841
3245
|
const stopRequestRef = useRef6(null);
|
|
2842
3246
|
const lastRequestRef = useRef6(null);
|
|
2843
|
-
|
|
3247
|
+
useEffect5(() => {
|
|
2844
3248
|
setSelectedModel(
|
|
2845
3249
|
(current) => resolveSelectedChatModel({ currentModel: current, availableModels, isModelsLoading })
|
|
2846
3250
|
);
|
|
2847
3251
|
}, [availableModels, isModelsLoading]);
|
|
2848
|
-
|
|
3252
|
+
useEffect5(() => {
|
|
2849
3253
|
if (activeSession) {
|
|
2850
3254
|
setSelectedModeLocal(activeSession.mode ?? DEFAULT_CHAT_AGENT_MODE);
|
|
2851
3255
|
return;
|
|
2852
3256
|
}
|
|
2853
3257
|
setSelectedModeLocal(preferredMode ?? DEFAULT_CHAT_AGENT_MODE);
|
|
2854
3258
|
}, [activeSession, preferredMode]);
|
|
2855
|
-
|
|
3259
|
+
useEffect5(() => {
|
|
2856
3260
|
if (!attachmentNotice)
|
|
2857
3261
|
return;
|
|
2858
3262
|
const timeoutId = window.setTimeout(
|
|
@@ -3742,7 +4146,7 @@ var ChatComposer = () => {
|
|
|
3742
4146
|
const { labels, sendRef, retryRef, enableImageAttachments } = useChatContext();
|
|
3743
4147
|
const { state, actions } = useChatComposer();
|
|
3744
4148
|
const { send, retry } = actions;
|
|
3745
|
-
|
|
4149
|
+
useEffect6(() => {
|
|
3746
4150
|
sendRef.current = send;
|
|
3747
4151
|
retryRef.current = async () => {
|
|
3748
4152
|
retry();
|
|
@@ -4061,6 +4465,7 @@ export {
|
|
|
4061
4465
|
AiChat,
|
|
4062
4466
|
AiChatProvider,
|
|
4063
4467
|
CHAT_AGENT_MODES,
|
|
4468
|
+
CHAT_MESSAGE_RENDER_ORDERS,
|
|
4064
4469
|
ChatComposer,
|
|
4065
4470
|
ChatConversationList,
|
|
4066
4471
|
ChatThread,
|