@xinghunm/ai-chat 0.2.2 → 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/README.md +58 -9
- package/dist/index.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +475 -91
- package/dist/index.mjs +450 -67
- 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 useRef5, useState as useState5 } from "react";
|
|
594
604
|
import styled9 from "@emotion/styled";
|
|
595
605
|
|
|
596
606
|
// src/context/use-chat-context.ts
|
|
@@ -611,7 +621,7 @@ var useChatStore = (selector) => {
|
|
|
611
621
|
var CHAT_THREAD_SCROLL_TOP_GAP = 16;
|
|
612
622
|
|
|
613
623
|
// src/components/chat-thread/components/chat-message-item.tsx
|
|
614
|
-
import { Fragment, memo, useState as
|
|
624
|
+
import { Fragment, memo, useState as useState4 } from "react";
|
|
615
625
|
import styled7 from "@emotion/styled";
|
|
616
626
|
import { keyframes } from "@emotion/react";
|
|
617
627
|
import ReactMarkdown from "react-markdown";
|
|
@@ -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,303 @@ 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, useRef as useRef3, useState as useState2 } 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 useTimelineBlockAnchors = ({
|
|
1026
|
+
blocks,
|
|
1027
|
+
displayedTimelineTextLength,
|
|
1028
|
+
isAssistantStreaming,
|
|
1029
|
+
message,
|
|
1030
|
+
messageRenderOrder
|
|
1031
|
+
}) => {
|
|
1032
|
+
const [timelineBlockAnchors, setTimelineBlockAnchors] = useState2({});
|
|
1033
|
+
const [visibleTimelineBlockKeys, setVisibleTimelineBlockKeys] = useState2({});
|
|
1034
|
+
const currentTimelineBlockKeys = useMemo3(
|
|
1035
|
+
() => blocks.map((block, index) => getTimelineBlockKey(block, index)).filter((blockKey) => Boolean(blockKey)),
|
|
1036
|
+
[blocks]
|
|
1037
|
+
);
|
|
1038
|
+
const timelineTextStreamLength = useMemo3(
|
|
1039
|
+
() => getTimelineDisplayUnitCount(getTimelineTextStream(message.content, blocks)),
|
|
1040
|
+
[blocks, message.content]
|
|
1041
|
+
);
|
|
1042
|
+
const previousTimelineStateRef = useRef3({
|
|
1043
|
+
messageId: message.id,
|
|
1044
|
+
blockKeys: currentTimelineBlockKeys,
|
|
1045
|
+
textLength: timelineTextStreamLength
|
|
1046
|
+
});
|
|
1047
|
+
const effectiveTimelineBlockAnchors = useMemo3(() => {
|
|
1048
|
+
if (messageRenderOrder !== "timeline" || !isAssistantStreaming) {
|
|
1049
|
+
return timelineBlockAnchors;
|
|
1050
|
+
}
|
|
1051
|
+
const previousTimelineState = previousTimelineStateRef.current;
|
|
1052
|
+
const previousBlockKeys = new Set(previousTimelineState.blockKeys);
|
|
1053
|
+
return currentTimelineBlockKeys.reduce(
|
|
1054
|
+
(acc, blockKey) => {
|
|
1055
|
+
const existingAnchor = timelineBlockAnchors[blockKey];
|
|
1056
|
+
if (existingAnchor !== void 0) {
|
|
1057
|
+
acc[blockKey] = existingAnchor;
|
|
1058
|
+
return acc;
|
|
1059
|
+
}
|
|
1060
|
+
if (!previousBlockKeys.has(blockKey)) {
|
|
1061
|
+
acc[blockKey] = timelineTextStreamLength;
|
|
1062
|
+
}
|
|
1063
|
+
return acc;
|
|
1064
|
+
},
|
|
1065
|
+
{ ...timelineBlockAnchors }
|
|
1066
|
+
);
|
|
1067
|
+
}, [
|
|
1068
|
+
currentTimelineBlockKeys,
|
|
1069
|
+
isAssistantStreaming,
|
|
1070
|
+
messageRenderOrder,
|
|
1071
|
+
timelineBlockAnchors,
|
|
1072
|
+
timelineTextStreamLength
|
|
1073
|
+
]);
|
|
1074
|
+
useEffect2(() => {
|
|
1075
|
+
const previousTimelineState = previousTimelineStateRef.current;
|
|
1076
|
+
if (previousTimelineState.messageId !== message.id) {
|
|
1077
|
+
if (Object.keys(timelineBlockAnchors).length > 0) {
|
|
1078
|
+
setTimelineBlockAnchors({});
|
|
1079
|
+
}
|
|
1080
|
+
if (Object.keys(visibleTimelineBlockKeys).length > 0) {
|
|
1081
|
+
setVisibleTimelineBlockKeys({});
|
|
1082
|
+
}
|
|
1083
|
+
previousTimelineStateRef.current = {
|
|
1084
|
+
messageId: message.id,
|
|
1085
|
+
blockKeys: currentTimelineBlockKeys,
|
|
1086
|
+
textLength: timelineTextStreamLength
|
|
1087
|
+
};
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
if (messageRenderOrder === "timeline" && isAssistantStreaming) {
|
|
1091
|
+
const previousBlockKeys = new Set(previousTimelineState.blockKeys);
|
|
1092
|
+
const nextAnchors = currentTimelineBlockKeys.reduce(
|
|
1093
|
+
(acc, blockKey) => {
|
|
1094
|
+
const existingAnchor = timelineBlockAnchors[blockKey];
|
|
1095
|
+
if (existingAnchor !== void 0) {
|
|
1096
|
+
acc[blockKey] = existingAnchor;
|
|
1097
|
+
return acc;
|
|
1098
|
+
}
|
|
1099
|
+
if (!previousBlockKeys.has(blockKey)) {
|
|
1100
|
+
acc[blockKey] = timelineTextStreamLength;
|
|
1101
|
+
}
|
|
1102
|
+
return acc;
|
|
1103
|
+
},
|
|
1104
|
+
{}
|
|
1105
|
+
);
|
|
1106
|
+
const hasAnchorChanged = Object.keys(nextAnchors).length !== Object.keys(timelineBlockAnchors).length || Object.entries(nextAnchors).some(
|
|
1107
|
+
([blockKey, anchor]) => timelineBlockAnchors[blockKey] !== anchor
|
|
1108
|
+
);
|
|
1109
|
+
if (hasAnchorChanged) {
|
|
1110
|
+
setTimelineBlockAnchors(nextAnchors);
|
|
1111
|
+
}
|
|
1112
|
+
} else if (messageRenderOrder !== "timeline" && Object.keys(timelineBlockAnchors).length > 0) {
|
|
1113
|
+
setTimelineBlockAnchors({});
|
|
1114
|
+
}
|
|
1115
|
+
previousTimelineStateRef.current = {
|
|
1116
|
+
messageId: message.id,
|
|
1117
|
+
blockKeys: currentTimelineBlockKeys,
|
|
1118
|
+
textLength: timelineTextStreamLength
|
|
1119
|
+
};
|
|
1120
|
+
}, [
|
|
1121
|
+
currentTimelineBlockKeys,
|
|
1122
|
+
isAssistantStreaming,
|
|
1123
|
+
message.id,
|
|
1124
|
+
message.content,
|
|
1125
|
+
messageRenderOrder,
|
|
1126
|
+
timelineBlockAnchors,
|
|
1127
|
+
timelineTextStreamLength,
|
|
1128
|
+
visibleTimelineBlockKeys
|
|
1129
|
+
]);
|
|
1130
|
+
useEffect2(() => {
|
|
1131
|
+
if (messageRenderOrder !== "timeline") {
|
|
1132
|
+
if (Object.keys(visibleTimelineBlockKeys).length > 0) {
|
|
1133
|
+
setVisibleTimelineBlockKeys({});
|
|
1134
|
+
}
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
const nextVisibleBlockKeys = currentTimelineBlockKeys.reduce(
|
|
1138
|
+
(acc, blockKey) => {
|
|
1139
|
+
if (visibleTimelineBlockKeys[blockKey]) {
|
|
1140
|
+
acc[blockKey] = true;
|
|
1141
|
+
return acc;
|
|
1142
|
+
}
|
|
1143
|
+
const anchor = effectiveTimelineBlockAnchors[blockKey];
|
|
1144
|
+
if (anchor !== void 0 && anchor <= displayedTimelineTextLength) {
|
|
1145
|
+
acc[blockKey] = true;
|
|
1146
|
+
}
|
|
1147
|
+
return acc;
|
|
1148
|
+
},
|
|
1149
|
+
{}
|
|
1150
|
+
);
|
|
1151
|
+
const hasVisibleBlockChanged = Object.keys(nextVisibleBlockKeys).length !== Object.keys(visibleTimelineBlockKeys).length || Object.keys(nextVisibleBlockKeys).some((blockKey) => !visibleTimelineBlockKeys[blockKey]);
|
|
1152
|
+
if (hasVisibleBlockChanged) {
|
|
1153
|
+
setVisibleTimelineBlockKeys(nextVisibleBlockKeys);
|
|
1154
|
+
}
|
|
1155
|
+
}, [
|
|
1156
|
+
currentTimelineBlockKeys,
|
|
1157
|
+
displayedTimelineTextLength,
|
|
1158
|
+
effectiveTimelineBlockAnchors,
|
|
1159
|
+
messageRenderOrder,
|
|
1160
|
+
timelineBlockAnchors,
|
|
1161
|
+
visibleTimelineBlockKeys
|
|
1162
|
+
]);
|
|
1163
|
+
return {
|
|
1164
|
+
timelineBlockAnchors: effectiveTimelineBlockAnchors,
|
|
1165
|
+
visibleTimelineBlockKeys
|
|
1166
|
+
};
|
|
1167
|
+
};
|
|
1168
|
+
|
|
861
1169
|
// src/components/chat-thread/components/pde-ai-execution-confirmation-card.tsx
|
|
862
1170
|
import styled from "@emotion/styled";
|
|
863
1171
|
import { jsx as jsx2, jsxs } from "@emotion/react/jsx-runtime";
|
|
@@ -1068,7 +1376,7 @@ var Value = styled3.span`
|
|
|
1068
1376
|
`;
|
|
1069
1377
|
|
|
1070
1378
|
// src/components/chat-thread/components/pde-ai-questionnaire-card.tsx
|
|
1071
|
-
import { useState as
|
|
1379
|
+
import { useState as useState3 } from "react";
|
|
1072
1380
|
import styled4 from "@emotion/styled";
|
|
1073
1381
|
import { jsx as jsx5, jsxs as jsxs3 } from "@emotion/react/jsx-runtime";
|
|
1074
1382
|
var OTHER_OPTION_VALUE = "__other__";
|
|
@@ -1170,10 +1478,10 @@ var PDEAIQuestionnaireCardInner = ({
|
|
|
1170
1478
|
interactive = false,
|
|
1171
1479
|
onSubmit
|
|
1172
1480
|
}) => {
|
|
1173
|
-
const [answers, setAnswers] =
|
|
1481
|
+
const [answers, setAnswers] = useState3(
|
|
1174
1482
|
() => createInitialAnswers(questionnaire)
|
|
1175
1483
|
);
|
|
1176
|
-
const [errorMessage, setErrorMessage] =
|
|
1484
|
+
const [errorMessage, setErrorMessage] = useState3(null);
|
|
1177
1485
|
const handleSubmit = () => {
|
|
1178
1486
|
const missingQuestions = questionnaire.questions.filter(
|
|
1179
1487
|
(question) => question.required && isMissingRequiredAnswer(question, answers)
|
|
@@ -1609,7 +1917,7 @@ var Detail = styled5.li`
|
|
|
1609
1917
|
|
|
1610
1918
|
// src/components/chat-thread/components/image-viewer.tsx
|
|
1611
1919
|
import styled6 from "@emotion/styled";
|
|
1612
|
-
import { useEffect as
|
|
1920
|
+
import { useEffect as useEffect3, useRef as useRef4 } from "react";
|
|
1613
1921
|
import { jsx as jsx7 } from "@emotion/react/jsx-runtime";
|
|
1614
1922
|
var Overlay = styled6.div`
|
|
1615
1923
|
position: fixed;
|
|
@@ -1628,8 +1936,8 @@ var Img = styled6.img`
|
|
|
1628
1936
|
border-radius: 4px;
|
|
1629
1937
|
`;
|
|
1630
1938
|
var ImageViewer = ({ src, alt, onClose }) => {
|
|
1631
|
-
const overlayRef =
|
|
1632
|
-
|
|
1939
|
+
const overlayRef = useRef4(null);
|
|
1940
|
+
useEffect3(() => {
|
|
1633
1941
|
const handleKey = (e) => {
|
|
1634
1942
|
if (e.key === "Escape")
|
|
1635
1943
|
onClose();
|
|
@@ -1637,7 +1945,7 @@ var ImageViewer = ({ src, alt, onClose }) => {
|
|
|
1637
1945
|
document.addEventListener("keydown", handleKey);
|
|
1638
1946
|
return () => document.removeEventListener("keydown", handleKey);
|
|
1639
1947
|
}, [onClose]);
|
|
1640
|
-
|
|
1948
|
+
useEffect3(() => {
|
|
1641
1949
|
overlayRef.current?.focus();
|
|
1642
1950
|
}, []);
|
|
1643
1951
|
const stopPropagation = (e) => e.stopPropagation();
|
|
@@ -1800,9 +2108,16 @@ var ChatMessageItemView = ({
|
|
|
1800
2108
|
onQuestionnaireSubmit,
|
|
1801
2109
|
renderMessageBlock
|
|
1802
2110
|
}) => {
|
|
1803
|
-
const { labels } = useChatContext();
|
|
1804
|
-
const [activeImage, setActiveImage] =
|
|
1805
|
-
const {
|
|
2111
|
+
const { labels, messageRenderOrder = "blocks-first" } = useChatContext();
|
|
2112
|
+
const [activeImage, setActiveImage] = useState4(void 0);
|
|
2113
|
+
const {
|
|
2114
|
+
displayedBlocks,
|
|
2115
|
+
displayedContent,
|
|
2116
|
+
freshContent,
|
|
2117
|
+
isAssistantStreaming,
|
|
2118
|
+
isFreshBlockActive,
|
|
2119
|
+
settledContent
|
|
2120
|
+
} = useChatMessageReveal(message);
|
|
1806
2121
|
const isStoppedAssistant = message.role === "assistant" && message.status === "stopped";
|
|
1807
2122
|
const attachments = message.attachments ?? [];
|
|
1808
2123
|
const blocks = message.blocks ?? [];
|
|
@@ -1814,6 +2129,22 @@ var ChatMessageItemView = ({
|
|
|
1814
2129
|
const canSubmitConfirmation = isPlanMode && typeof onConfirmationSubmit === "function";
|
|
1815
2130
|
const canSubmitQuestionnaire = isPlanMode && typeof onQuestionnaireSubmit === "function";
|
|
1816
2131
|
const shouldShowStreamingCaret = isAssistantStreaming && (!shouldRenderStructuredBlocks || hasTextContent);
|
|
2132
|
+
const timelineConsumedText = messageRenderOrder === "timeline" ? getTimelineConsumedText(blocks) : "";
|
|
2133
|
+
const hasConsumedTimelineText = timelineConsumedText.length > 0 && displayedContent.startsWith(timelineConsumedText);
|
|
2134
|
+
const timelineDisplayedContent = hasConsumedTimelineText ? displayedContent.slice(timelineConsumedText.length) : displayedContent;
|
|
2135
|
+
const timelineTextDisplay = buildTimelineTextDisplay(
|
|
2136
|
+
timelineDisplayedContent,
|
|
2137
|
+
isAssistantStreaming,
|
|
2138
|
+
isFreshBlockActive
|
|
2139
|
+
);
|
|
2140
|
+
const displayedTimelineTextLength = getTimelineDisplayUnitCount(timelineDisplayedContent);
|
|
2141
|
+
const { timelineBlockAnchors, visibleTimelineBlockKeys } = useTimelineBlockAnchors({
|
|
2142
|
+
blocks,
|
|
2143
|
+
displayedTimelineTextLength,
|
|
2144
|
+
isAssistantStreaming,
|
|
2145
|
+
message,
|
|
2146
|
+
messageRenderOrder
|
|
2147
|
+
});
|
|
1817
2148
|
const renderChatMessageBlock = (block, index) => {
|
|
1818
2149
|
switch (block.type) {
|
|
1819
2150
|
case "markdown":
|
|
@@ -1870,19 +2201,69 @@ var ChatMessageItemView = ({
|
|
|
1870
2201
|
return null;
|
|
1871
2202
|
}
|
|
1872
2203
|
};
|
|
1873
|
-
const renderTextContent = () =>
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
2204
|
+
const renderTextContent = (options) => {
|
|
2205
|
+
const textContent = options?.content ?? displayedContent;
|
|
2206
|
+
const localTimelineTextDisplay = options?.displayedBlocks ? void 0 : options?.useTimelineSegmentation && options.content !== void 0 ? buildTimelineTextDisplay(options.content, isAssistantStreaming, isFreshBlockActive) : void 0;
|
|
2207
|
+
const textBlocks = options?.displayedBlocks ?? localTimelineTextDisplay?.displayedBlocks ?? displayedBlocks;
|
|
2208
|
+
const settledText = localTimelineTextDisplay?.settledContent ?? settledContent;
|
|
2209
|
+
const freshText = localTimelineTextDisplay?.freshContent ?? freshContent;
|
|
2210
|
+
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
2211
|
+
textBlocks.filter((block) => block.content).map((block, index) => /* @__PURE__ */ jsx8(
|
|
2212
|
+
ContentBlock,
|
|
2213
|
+
{
|
|
2214
|
+
"data-testid": block.tone === "fresh" ? "chat-message-fresh-block" : "chat-message-settled-block",
|
|
2215
|
+
"data-block-tone": block.tone,
|
|
2216
|
+
"data-block-index": index,
|
|
2217
|
+
children: renderMarkdownContent(block.content)
|
|
2218
|
+
},
|
|
2219
|
+
`${block.tone}-${index}`
|
|
2220
|
+
)),
|
|
2221
|
+
!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
|
|
2222
|
+
] });
|
|
2223
|
+
};
|
|
2224
|
+
const renderStaticTextSegment = (content) => /* @__PURE__ */ jsx8(ContentBlock, { "data-testid": "chat-message-settled-block", "data-block-tone": "settled", children: renderMarkdownContent(content) });
|
|
2225
|
+
const bodySegments = (() => {
|
|
2226
|
+
if (!shouldRenderStructuredBlocks && hasTextContent) {
|
|
2227
|
+
return [{ type: "text" }];
|
|
2228
|
+
}
|
|
2229
|
+
if (!shouldRenderStructuredBlocks) {
|
|
2230
|
+
return [];
|
|
2231
|
+
}
|
|
2232
|
+
if (messageRenderOrder === "timeline" && hasTextContent) {
|
|
2233
|
+
const hasAnchoredStructuredBlocks = blocks.some((block, index) => {
|
|
2234
|
+
const blockKey = getTimelineBlockKey(block, index);
|
|
2235
|
+
return blockKey ? timelineBlockAnchors[blockKey] !== void 0 : false;
|
|
2236
|
+
});
|
|
2237
|
+
if (hasAnchoredStructuredBlocks) {
|
|
2238
|
+
return buildAnchoredTimelineSegments({
|
|
2239
|
+
blocks,
|
|
2240
|
+
timelineBlockAnchors,
|
|
2241
|
+
timelineDisplayedBlocks: timelineTextDisplay.displayedBlocks,
|
|
2242
|
+
visibleTimelineBlockKeys
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
2245
|
+
const orderedTimelineSegments = blocks.map(
|
|
2246
|
+
(block, index) => block.type === "markdown" ? {
|
|
2247
|
+
type: "markdown",
|
|
2248
|
+
content: block.text
|
|
2249
|
+
} : {
|
|
2250
|
+
type: "block",
|
|
2251
|
+
block,
|
|
2252
|
+
index
|
|
2253
|
+
}
|
|
2254
|
+
);
|
|
2255
|
+
if (!timelineConsumedText) {
|
|
2256
|
+
return displayedContent ? [{ type: "text", content: displayedContent }, ...orderedTimelineSegments] : orderedTimelineSegments;
|
|
2257
|
+
}
|
|
2258
|
+
return timelineDisplayedContent ? [...orderedTimelineSegments, { type: "text", content: timelineDisplayedContent }] : orderedTimelineSegments;
|
|
2259
|
+
}
|
|
2260
|
+
const orderedBlocks = blocks.map((block, index) => ({
|
|
2261
|
+
type: "block",
|
|
2262
|
+
block,
|
|
2263
|
+
index
|
|
2264
|
+
}));
|
|
2265
|
+
return hasTextContent ? [...orderedBlocks, { type: "text" }] : orderedBlocks;
|
|
2266
|
+
})();
|
|
1886
2267
|
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1887
2268
|
/* @__PURE__ */ jsxs5(Bubble, { "data-role": message.role, "data-status": message.status ?? "done", children: [
|
|
1888
2269
|
/* @__PURE__ */ jsxs5(Header2, { children: [
|
|
@@ -1901,17 +2282,18 @@ var ChatMessageItemView = ({
|
|
|
1901
2282
|
isStoppedAssistant ? /* @__PURE__ */ jsx8(StatusTag, { "data-testid": "chat-message-stopped-tag", children: labels.stoppedResponse }) : null
|
|
1902
2283
|
] }),
|
|
1903
2284
|
/* @__PURE__ */ jsxs5(Content, { "data-testid": "chat-message-content", children: [
|
|
1904
|
-
shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
2285
|
+
shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */ jsx8(ContentStack, { "data-testid": "chat-message-body-stack", children: bodySegments.map((segment, index) => /* @__PURE__ */ jsx8(
|
|
2286
|
+
ContentSegment,
|
|
2287
|
+
{
|
|
2288
|
+
"data-testid": "chat-message-content-segment",
|
|
2289
|
+
children: segment.type === "block" ? renderChatMessageBlock(segment.block, segment.index) : segment.type === "text" ? segment.content !== void 0 ? segment.useTimelineSegmentation ? renderTextContent({
|
|
2290
|
+
content: segment.content,
|
|
2291
|
+
displayedBlocks: segment.displayedBlocks,
|
|
2292
|
+
useTimelineSegmentation: true
|
|
2293
|
+
}) : renderStaticTextSegment(segment.content) : renderTextContent() : renderStaticTextSegment(segment.content)
|
|
2294
|
+
},
|
|
2295
|
+
segment.type === "text" ? `text-${index}` : segment.type === "markdown" ? `markdown-${index}` : `${segment.block.type}-${segment.index}`
|
|
2296
|
+
)) }) : null,
|
|
1915
2297
|
attachments.length ? /* @__PURE__ */ jsx8(AttachmentGrid, { "data-testid": "chat-message-attachment-grid", children: attachments.map((attachment) => /* @__PURE__ */ jsx8(
|
|
1916
2298
|
AttachmentButton,
|
|
1917
2299
|
{
|
|
@@ -2303,17 +2685,17 @@ var ChatThreadView = ({
|
|
|
2303
2685
|
onQuestionnaireSubmit,
|
|
2304
2686
|
renderMessageBlock
|
|
2305
2687
|
}) => {
|
|
2306
|
-
const containerRef =
|
|
2307
|
-
const conversationTurns =
|
|
2688
|
+
const containerRef = useRef5(null);
|
|
2689
|
+
const conversationTurns = useMemo4(
|
|
2308
2690
|
() => groupConversationTurns(historyMessages, streamingMessage),
|
|
2309
2691
|
[historyMessages, streamingMessage]
|
|
2310
2692
|
);
|
|
2311
2693
|
const latestTurn = conversationTurns[conversationTurns.length - 1];
|
|
2312
2694
|
const previousTurns = conversationTurns.slice(0, -1);
|
|
2313
2695
|
const latestUserMessageId = latestTurn?.userMessage?.id;
|
|
2314
|
-
const latestUserMessageRef =
|
|
2315
|
-
const reservedSpaceFrameRef =
|
|
2316
|
-
const [latestTurnMinHeight, setLatestTurnMinHeight] =
|
|
2696
|
+
const latestUserMessageRef = useRef5(null);
|
|
2697
|
+
const reservedSpaceFrameRef = useRef5(null);
|
|
2698
|
+
const [latestTurnMinHeight, setLatestTurnMinHeight] = useState5(0);
|
|
2317
2699
|
const measureLatestTurnMinHeight = useCallback2(() => {
|
|
2318
2700
|
const container = containerRef.current;
|
|
2319
2701
|
if (!container)
|
|
@@ -2566,7 +2948,7 @@ var RetryButton = styled9.button`
|
|
|
2566
2948
|
`;
|
|
2567
2949
|
|
|
2568
2950
|
// src/components/chat-composer/index.tsx
|
|
2569
|
-
import { useEffect as
|
|
2951
|
+
import { useEffect as useEffect6, useRef as useRef8 } from "react";
|
|
2570
2952
|
import styled14 from "@emotion/styled";
|
|
2571
2953
|
|
|
2572
2954
|
// src/components/chat-composer/lib/chat-composer.ts
|
|
@@ -2678,10 +3060,10 @@ var resolveSendSession = ({
|
|
|
2678
3060
|
};
|
|
2679
3061
|
|
|
2680
3062
|
// src/components/chat-composer/hooks/use-chat-composer.ts
|
|
2681
|
-
import { useCallback as useCallback3, useEffect as
|
|
3063
|
+
import { useCallback as useCallback3, useEffect as useEffect5, useRef as useRef7, useState as useState7 } from "react";
|
|
2682
3064
|
|
|
2683
3065
|
// src/components/chat-composer/hooks/use-composer-attachments.ts
|
|
2684
|
-
import { useEffect as
|
|
3066
|
+
import { useEffect as useEffect4, useRef as useRef6, useState as useState6 } from "react";
|
|
2685
3067
|
var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp"]);
|
|
2686
3068
|
var MAX_COMPOSER_ATTACHMENTS = 10;
|
|
2687
3069
|
var createObjectUrl = (file) => typeof URL !== "undefined" && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : "";
|
|
@@ -2695,12 +3077,12 @@ var releaseComposerAttachments = (attachments) => {
|
|
|
2695
3077
|
attachments.forEach((attachment) => revokeObjectUrl(attachment.previewUrl));
|
|
2696
3078
|
};
|
|
2697
3079
|
var useComposerAttachments = () => {
|
|
2698
|
-
const [attachments, setAttachments] =
|
|
2699
|
-
const attachmentsRef =
|
|
2700
|
-
|
|
3080
|
+
const [attachments, setAttachments] = useState6([]);
|
|
3081
|
+
const attachmentsRef = useRef6([]);
|
|
3082
|
+
useEffect4(() => {
|
|
2701
3083
|
attachmentsRef.current = attachments;
|
|
2702
3084
|
}, [attachments]);
|
|
2703
|
-
|
|
3085
|
+
useEffect4(
|
|
2704
3086
|
() => () => {
|
|
2705
3087
|
releaseComposerAttachments(attachmentsRef.current);
|
|
2706
3088
|
},
|
|
@@ -2813,9 +3195,9 @@ var useChatComposer = () => {
|
|
|
2813
3195
|
const clearSessionError = useChatStore((s) => s.clearSessionError);
|
|
2814
3196
|
const setPreferredMode = useChatStore((s) => s.setPreferredMode);
|
|
2815
3197
|
const setSessionMode = useChatStore((s) => s.setSessionMode);
|
|
2816
|
-
const [availableModels, setAvailableModels] =
|
|
2817
|
-
const [isModelsLoading, setIsModelsLoading] =
|
|
2818
|
-
const [isModelsError, setIsModelsError] =
|
|
3198
|
+
const [availableModels, setAvailableModels] = useState7([]);
|
|
3199
|
+
const [isModelsLoading, setIsModelsLoading] = useState7(true);
|
|
3200
|
+
const [isModelsError, setIsModelsError] = useState7(false);
|
|
2819
3201
|
const fetchModels = useCallback3(async () => {
|
|
2820
3202
|
setIsModelsLoading(true);
|
|
2821
3203
|
setIsModelsError(false);
|
|
@@ -2828,31 +3210,31 @@ var useChatComposer = () => {
|
|
|
2828
3210
|
setIsModelsLoading(false);
|
|
2829
3211
|
}
|
|
2830
3212
|
}, [transport]);
|
|
2831
|
-
|
|
3213
|
+
useEffect5(() => {
|
|
2832
3214
|
void fetchModels();
|
|
2833
3215
|
}, [fetchModels]);
|
|
2834
3216
|
const hasModels = availableModels.length > 0;
|
|
2835
|
-
const [value, setValue] =
|
|
2836
|
-
const [selectedModel, setSelectedModel] =
|
|
2837
|
-
const [selectedMode, setSelectedModeLocal] =
|
|
2838
|
-
const [attachmentNotice, setAttachmentNotice] =
|
|
3217
|
+
const [value, setValue] = useState7("");
|
|
3218
|
+
const [selectedModel, setSelectedModel] = useState7("");
|
|
3219
|
+
const [selectedMode, setSelectedModeLocal] = useState7(DEFAULT_CHAT_AGENT_MODE);
|
|
3220
|
+
const [attachmentNotice, setAttachmentNotice] = useState7(null);
|
|
2839
3221
|
const { attachments, appendFiles, removeAttachment, takeMessageAttachments } = useComposerAttachments();
|
|
2840
|
-
const abortControllerRef =
|
|
2841
|
-
const stopRequestRef =
|
|
2842
|
-
const lastRequestRef =
|
|
2843
|
-
|
|
3222
|
+
const abortControllerRef = useRef7(null);
|
|
3223
|
+
const stopRequestRef = useRef7(null);
|
|
3224
|
+
const lastRequestRef = useRef7(null);
|
|
3225
|
+
useEffect5(() => {
|
|
2844
3226
|
setSelectedModel(
|
|
2845
3227
|
(current) => resolveSelectedChatModel({ currentModel: current, availableModels, isModelsLoading })
|
|
2846
3228
|
);
|
|
2847
3229
|
}, [availableModels, isModelsLoading]);
|
|
2848
|
-
|
|
3230
|
+
useEffect5(() => {
|
|
2849
3231
|
if (activeSession) {
|
|
2850
3232
|
setSelectedModeLocal(activeSession.mode ?? DEFAULT_CHAT_AGENT_MODE);
|
|
2851
3233
|
return;
|
|
2852
3234
|
}
|
|
2853
3235
|
setSelectedModeLocal(preferredMode ?? DEFAULT_CHAT_AGENT_MODE);
|
|
2854
3236
|
}, [activeSession, preferredMode]);
|
|
2855
|
-
|
|
3237
|
+
useEffect5(() => {
|
|
2856
3238
|
if (!attachmentNotice)
|
|
2857
3239
|
return;
|
|
2858
3240
|
const timeoutId = window.setTimeout(
|
|
@@ -3131,14 +3513,14 @@ var useChatComposer = () => {
|
|
|
3131
3513
|
};
|
|
3132
3514
|
|
|
3133
3515
|
// src/components/chat-composer/components/chat-composer-attachment-list.tsx
|
|
3134
|
-
import { useState as
|
|
3516
|
+
import { useState as useState8 } from "react";
|
|
3135
3517
|
import styled10 from "@emotion/styled";
|
|
3136
3518
|
import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs8 } from "@emotion/react/jsx-runtime";
|
|
3137
3519
|
var ChatComposerAttachmentList = ({
|
|
3138
3520
|
attachments,
|
|
3139
3521
|
onRemoveAttachment
|
|
3140
3522
|
}) => {
|
|
3141
|
-
const [activeImage, setActiveImage] =
|
|
3523
|
+
const [activeImage, setActiveImage] = useState8(null);
|
|
3142
3524
|
if (!attachments.length) {
|
|
3143
3525
|
return null;
|
|
3144
3526
|
}
|
|
@@ -3630,7 +4012,7 @@ var ChatComposerView = ({
|
|
|
3630
4012
|
onStop,
|
|
3631
4013
|
onSend
|
|
3632
4014
|
}) => {
|
|
3633
|
-
const imageInputRef =
|
|
4015
|
+
const imageInputRef = useRef8(null);
|
|
3634
4016
|
const canSend = canSendChatMessage({
|
|
3635
4017
|
value,
|
|
3636
4018
|
attachmentCount: attachments.length,
|
|
@@ -3742,7 +4124,7 @@ var ChatComposer = () => {
|
|
|
3742
4124
|
const { labels, sendRef, retryRef, enableImageAttachments } = useChatContext();
|
|
3743
4125
|
const { state, actions } = useChatComposer();
|
|
3744
4126
|
const { send, retry } = actions;
|
|
3745
|
-
|
|
4127
|
+
useEffect6(() => {
|
|
3746
4128
|
sendRef.current = send;
|
|
3747
4129
|
retryRef.current = async () => {
|
|
3748
4130
|
retry();
|
|
@@ -4061,6 +4443,7 @@ export {
|
|
|
4061
4443
|
AiChat,
|
|
4062
4444
|
AiChatProvider,
|
|
4063
4445
|
CHAT_AGENT_MODES,
|
|
4446
|
+
CHAT_MESSAGE_RENDER_ORDERS,
|
|
4064
4447
|
ChatComposer,
|
|
4065
4448
|
ChatConversationList,
|
|
4066
4449
|
ChatThread,
|