@xinghunm/ai-chat 0.3.0 → 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.
Files changed (3) hide show
  1. package/dist/index.js +114 -92
  2. package/dist/index.mjs +145 -123
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1069,6 +1069,83 @@ var buildAnchoredTimelineSegments = ({
1069
1069
  };
1070
1070
 
1071
1071
  // src/components/chat-thread/hooks/use-timeline-block-anchors.ts
1072
+ var createTimelineAnchorState = ({
1073
+ messageId,
1074
+ currentBlockKeys
1075
+ }) => ({
1076
+ messageId,
1077
+ previousBlockKeys: currentBlockKeys,
1078
+ timelineBlockAnchors: {},
1079
+ visibleTimelineBlockKeys: {}
1080
+ });
1081
+ var timelineAnchorReducer = (state, action) => {
1082
+ switch (action.type) {
1083
+ case "reset-message":
1084
+ if (state.messageId === action.messageId) {
1085
+ return state;
1086
+ }
1087
+ return createTimelineAnchorState(action);
1088
+ case "sync-anchors": {
1089
+ const previousBlockKeys = new Set(state.previousBlockKeys);
1090
+ const nextAnchors = action.currentBlockKeys.reduce(
1091
+ (acc, blockKey) => {
1092
+ const existingAnchor = state.timelineBlockAnchors[blockKey];
1093
+ if (existingAnchor !== void 0) {
1094
+ acc[blockKey] = existingAnchor;
1095
+ return acc;
1096
+ }
1097
+ if (!previousBlockKeys.has(blockKey)) {
1098
+ acc[blockKey] = action.timelineTextStreamLength;
1099
+ }
1100
+ return acc;
1101
+ },
1102
+ {}
1103
+ );
1104
+ const hasAnchorChanged = Object.keys(nextAnchors).length !== Object.keys(state.timelineBlockAnchors).length || Object.entries(nextAnchors).some(
1105
+ ([blockKey, anchor]) => state.timelineBlockAnchors[blockKey] !== anchor
1106
+ );
1107
+ const hasPreviousKeysChanged = action.currentBlockKeys.length !== state.previousBlockKeys.length || action.currentBlockKeys.some(
1108
+ (blockKey, index) => state.previousBlockKeys[index] !== blockKey
1109
+ );
1110
+ if (!hasAnchorChanged && !hasPreviousKeysChanged) {
1111
+ return state;
1112
+ }
1113
+ return {
1114
+ ...state,
1115
+ previousBlockKeys: action.currentBlockKeys,
1116
+ timelineBlockAnchors: hasAnchorChanged ? nextAnchors : state.timelineBlockAnchors
1117
+ };
1118
+ }
1119
+ case "sync-visible": {
1120
+ const nextVisibleBlockKeys = action.currentBlockKeys.reduce(
1121
+ (acc, blockKey) => {
1122
+ if (state.visibleTimelineBlockKeys[blockKey]) {
1123
+ acc[blockKey] = true;
1124
+ return acc;
1125
+ }
1126
+ const anchor = action.effectiveTimelineBlockAnchors[blockKey];
1127
+ if (anchor !== void 0 && anchor <= action.displayedTimelineTextLength) {
1128
+ acc[blockKey] = true;
1129
+ }
1130
+ return acc;
1131
+ },
1132
+ {}
1133
+ );
1134
+ const hasVisibleBlockChanged = Object.keys(nextVisibleBlockKeys).length !== Object.keys(state.visibleTimelineBlockKeys).length || Object.keys(nextVisibleBlockKeys).some(
1135
+ (blockKey) => !state.visibleTimelineBlockKeys[blockKey]
1136
+ );
1137
+ if (!hasVisibleBlockChanged) {
1138
+ return state;
1139
+ }
1140
+ return {
1141
+ ...state,
1142
+ visibleTimelineBlockKeys: nextVisibleBlockKeys
1143
+ };
1144
+ }
1145
+ default:
1146
+ return state;
1147
+ }
1148
+ };
1072
1149
  var useTimelineBlockAnchors = ({
1073
1150
  blocks,
1074
1151
  displayedTimelineTextLength,
@@ -1076,8 +1153,6 @@ var useTimelineBlockAnchors = ({
1076
1153
  message,
1077
1154
  messageRenderOrder
1078
1155
  }) => {
1079
- const [timelineBlockAnchors, setTimelineBlockAnchors] = (0, import_react6.useState)({});
1080
- const [visibleTimelineBlockKeys, setVisibleTimelineBlockKeys] = (0, import_react6.useState)({});
1081
1156
  const currentTimelineBlockKeys = (0, import_react6.useMemo)(
1082
1157
  () => blocks.map((block, index) => getTimelineBlockKey(block, index)).filter((blockKey) => Boolean(blockKey)),
1083
1158
  [blocks]
@@ -1086,20 +1161,22 @@ var useTimelineBlockAnchors = ({
1086
1161
  () => getTimelineDisplayUnitCount(getTimelineTextStream(message.content, blocks)),
1087
1162
  [blocks, message.content]
1088
1163
  );
1089
- const previousTimelineStateRef = (0, import_react6.useRef)({
1090
- messageId: message.id,
1091
- blockKeys: currentTimelineBlockKeys,
1092
- textLength: timelineTextStreamLength
1093
- });
1164
+ const [state, dispatch] = (0, import_react6.useReducer)(
1165
+ timelineAnchorReducer,
1166
+ {
1167
+ messageId: message.id,
1168
+ currentBlockKeys: currentTimelineBlockKeys
1169
+ },
1170
+ createTimelineAnchorState
1171
+ );
1094
1172
  const effectiveTimelineBlockAnchors = (0, import_react6.useMemo)(() => {
1095
1173
  if (messageRenderOrder !== "timeline" || !isAssistantStreaming) {
1096
- return timelineBlockAnchors;
1174
+ return state.timelineBlockAnchors;
1097
1175
  }
1098
- const previousTimelineState = previousTimelineStateRef.current;
1099
- const previousBlockKeys = new Set(previousTimelineState.blockKeys);
1176
+ const previousBlockKeys = new Set(state.previousBlockKeys);
1100
1177
  return currentTimelineBlockKeys.reduce(
1101
1178
  (acc, blockKey) => {
1102
- const existingAnchor = timelineBlockAnchors[blockKey];
1179
+ const existingAnchor = state.timelineBlockAnchors[blockKey];
1103
1180
  if (existingAnchor !== void 0) {
1104
1181
  acc[blockKey] = existingAnchor;
1105
1182
  return acc;
@@ -1109,107 +1186,52 @@ var useTimelineBlockAnchors = ({
1109
1186
  }
1110
1187
  return acc;
1111
1188
  },
1112
- { ...timelineBlockAnchors }
1189
+ { ...state.timelineBlockAnchors }
1113
1190
  );
1114
1191
  }, [
1115
1192
  currentTimelineBlockKeys,
1116
1193
  isAssistantStreaming,
1117
1194
  messageRenderOrder,
1118
- timelineBlockAnchors,
1195
+ state.previousBlockKeys,
1196
+ state.timelineBlockAnchors,
1119
1197
  timelineTextStreamLength
1120
1198
  ]);
1121
1199
  (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
- };
1200
+ dispatch({
1201
+ type: "reset-message",
1202
+ messageId: message.id,
1203
+ currentBlockKeys: currentTimelineBlockKeys
1204
+ });
1205
+ }, [currentTimelineBlockKeys, message.id]);
1206
+ (0, import_react6.useEffect)(() => {
1207
+ if (messageRenderOrder !== "timeline" || !isAssistantStreaming) {
1135
1208
  return;
1136
1209
  }
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
- ]);
1210
+ dispatch({
1211
+ type: "sync-anchors",
1212
+ currentBlockKeys: currentTimelineBlockKeys,
1213
+ timelineTextStreamLength
1214
+ });
1215
+ }, [currentTimelineBlockKeys, isAssistantStreaming, messageRenderOrder, timelineTextStreamLength]);
1177
1216
  (0, import_react6.useEffect)(() => {
1178
1217
  if (messageRenderOrder !== "timeline") {
1179
- if (Object.keys(visibleTimelineBlockKeys).length > 0) {
1180
- setVisibleTimelineBlockKeys({});
1181
- }
1182
1218
  return;
1183
1219
  }
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
- }
1220
+ dispatch({
1221
+ type: "sync-visible",
1222
+ currentBlockKeys: currentTimelineBlockKeys,
1223
+ effectiveTimelineBlockAnchors,
1224
+ displayedTimelineTextLength
1225
+ });
1202
1226
  }, [
1203
1227
  currentTimelineBlockKeys,
1204
1228
  displayedTimelineTextLength,
1205
1229
  effectiveTimelineBlockAnchors,
1206
- messageRenderOrder,
1207
- timelineBlockAnchors,
1208
- visibleTimelineBlockKeys
1230
+ messageRenderOrder
1209
1231
  ]);
1210
1232
  return {
1211
- timelineBlockAnchors: effectiveTimelineBlockAnchors,
1212
- visibleTimelineBlockKeys
1233
+ timelineBlockAnchors: messageRenderOrder === "timeline" ? effectiveTimelineBlockAnchors : {},
1234
+ visibleTimelineBlockKeys: messageRenderOrder === "timeline" ? state.visibleTimelineBlockKeys : {}
1213
1235
  };
1214
1236
  };
1215
1237
 
package/dist/index.mjs CHANGED
@@ -600,7 +600,7 @@ var AiChatProvider = (props) => {
600
600
  };
601
601
 
602
602
  // src/components/chat-thread/index.tsx
603
- import { useCallback as useCallback2, useLayoutEffect, useMemo as useMemo4, useRef as useRef5, useState as useState5 } from "react";
603
+ import { useCallback as useCallback2, useLayoutEffect, useMemo as useMemo4, useRef as useRef4, useState as useState4 } from "react";
604
604
  import styled9 from "@emotion/styled";
605
605
 
606
606
  // src/context/use-chat-context.ts
@@ -621,7 +621,7 @@ var useChatStore = (selector) => {
621
621
  var CHAT_THREAD_SCROLL_TOP_GAP = 16;
622
622
 
623
623
  // src/components/chat-thread/components/chat-message-item.tsx
624
- import { Fragment, memo, useState as useState4 } from "react";
624
+ import { Fragment, memo, useState as useState3 } from "react";
625
625
  import styled7 from "@emotion/styled";
626
626
  import { keyframes } from "@emotion/react";
627
627
  import ReactMarkdown from "react-markdown";
@@ -870,7 +870,7 @@ var useChatMessageReveal = (message) => {
870
870
  };
871
871
 
872
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";
873
+ import { useEffect as useEffect2, useMemo as useMemo3, useReducer as useReducer2 } from "react";
874
874
 
875
875
  // src/components/chat-thread/lib/chat-message-timeline.ts
876
876
  var stringifyTimelineKeyPart = (value) => {
@@ -1022,6 +1022,83 @@ var buildAnchoredTimelineSegments = ({
1022
1022
  };
1023
1023
 
1024
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
+ };
1025
1102
  var useTimelineBlockAnchors = ({
1026
1103
  blocks,
1027
1104
  displayedTimelineTextLength,
@@ -1029,8 +1106,6 @@ var useTimelineBlockAnchors = ({
1029
1106
  message,
1030
1107
  messageRenderOrder
1031
1108
  }) => {
1032
- const [timelineBlockAnchors, setTimelineBlockAnchors] = useState2({});
1033
- const [visibleTimelineBlockKeys, setVisibleTimelineBlockKeys] = useState2({});
1034
1109
  const currentTimelineBlockKeys = useMemo3(
1035
1110
  () => blocks.map((block, index) => getTimelineBlockKey(block, index)).filter((blockKey) => Boolean(blockKey)),
1036
1111
  [blocks]
@@ -1039,20 +1114,22 @@ var useTimelineBlockAnchors = ({
1039
1114
  () => getTimelineDisplayUnitCount(getTimelineTextStream(message.content, blocks)),
1040
1115
  [blocks, message.content]
1041
1116
  );
1042
- const previousTimelineStateRef = useRef3({
1043
- messageId: message.id,
1044
- blockKeys: currentTimelineBlockKeys,
1045
- textLength: timelineTextStreamLength
1046
- });
1117
+ const [state, dispatch] = useReducer2(
1118
+ timelineAnchorReducer,
1119
+ {
1120
+ messageId: message.id,
1121
+ currentBlockKeys: currentTimelineBlockKeys
1122
+ },
1123
+ createTimelineAnchorState
1124
+ );
1047
1125
  const effectiveTimelineBlockAnchors = useMemo3(() => {
1048
1126
  if (messageRenderOrder !== "timeline" || !isAssistantStreaming) {
1049
- return timelineBlockAnchors;
1127
+ return state.timelineBlockAnchors;
1050
1128
  }
1051
- const previousTimelineState = previousTimelineStateRef.current;
1052
- const previousBlockKeys = new Set(previousTimelineState.blockKeys);
1129
+ const previousBlockKeys = new Set(state.previousBlockKeys);
1053
1130
  return currentTimelineBlockKeys.reduce(
1054
1131
  (acc, blockKey) => {
1055
- const existingAnchor = timelineBlockAnchors[blockKey];
1132
+ const existingAnchor = state.timelineBlockAnchors[blockKey];
1056
1133
  if (existingAnchor !== void 0) {
1057
1134
  acc[blockKey] = existingAnchor;
1058
1135
  return acc;
@@ -1062,107 +1139,52 @@ var useTimelineBlockAnchors = ({
1062
1139
  }
1063
1140
  return acc;
1064
1141
  },
1065
- { ...timelineBlockAnchors }
1142
+ { ...state.timelineBlockAnchors }
1066
1143
  );
1067
1144
  }, [
1068
1145
  currentTimelineBlockKeys,
1069
1146
  isAssistantStreaming,
1070
1147
  messageRenderOrder,
1071
- timelineBlockAnchors,
1148
+ state.previousBlockKeys,
1149
+ state.timelineBlockAnchors,
1072
1150
  timelineTextStreamLength
1073
1151
  ]);
1074
1152
  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
- };
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) {
1088
1161
  return;
1089
1162
  }
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
- ]);
1163
+ dispatch({
1164
+ type: "sync-anchors",
1165
+ currentBlockKeys: currentTimelineBlockKeys,
1166
+ timelineTextStreamLength
1167
+ });
1168
+ }, [currentTimelineBlockKeys, isAssistantStreaming, messageRenderOrder, timelineTextStreamLength]);
1130
1169
  useEffect2(() => {
1131
1170
  if (messageRenderOrder !== "timeline") {
1132
- if (Object.keys(visibleTimelineBlockKeys).length > 0) {
1133
- setVisibleTimelineBlockKeys({});
1134
- }
1135
1171
  return;
1136
1172
  }
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
- }
1173
+ dispatch({
1174
+ type: "sync-visible",
1175
+ currentBlockKeys: currentTimelineBlockKeys,
1176
+ effectiveTimelineBlockAnchors,
1177
+ displayedTimelineTextLength
1178
+ });
1155
1179
  }, [
1156
1180
  currentTimelineBlockKeys,
1157
1181
  displayedTimelineTextLength,
1158
1182
  effectiveTimelineBlockAnchors,
1159
- messageRenderOrder,
1160
- timelineBlockAnchors,
1161
- visibleTimelineBlockKeys
1183
+ messageRenderOrder
1162
1184
  ]);
1163
1185
  return {
1164
- timelineBlockAnchors: effectiveTimelineBlockAnchors,
1165
- visibleTimelineBlockKeys
1186
+ timelineBlockAnchors: messageRenderOrder === "timeline" ? effectiveTimelineBlockAnchors : {},
1187
+ visibleTimelineBlockKeys: messageRenderOrder === "timeline" ? state.visibleTimelineBlockKeys : {}
1166
1188
  };
1167
1189
  };
1168
1190
 
@@ -1376,7 +1398,7 @@ var Value = styled3.span`
1376
1398
  `;
1377
1399
 
1378
1400
  // src/components/chat-thread/components/pde-ai-questionnaire-card.tsx
1379
- import { useState as useState3 } from "react";
1401
+ import { useState as useState2 } from "react";
1380
1402
  import styled4 from "@emotion/styled";
1381
1403
  import { jsx as jsx5, jsxs as jsxs3 } from "@emotion/react/jsx-runtime";
1382
1404
  var OTHER_OPTION_VALUE = "__other__";
@@ -1478,10 +1500,10 @@ var PDEAIQuestionnaireCardInner = ({
1478
1500
  interactive = false,
1479
1501
  onSubmit
1480
1502
  }) => {
1481
- const [answers, setAnswers] = useState3(
1503
+ const [answers, setAnswers] = useState2(
1482
1504
  () => createInitialAnswers(questionnaire)
1483
1505
  );
1484
- const [errorMessage, setErrorMessage] = useState3(null);
1506
+ const [errorMessage, setErrorMessage] = useState2(null);
1485
1507
  const handleSubmit = () => {
1486
1508
  const missingQuestions = questionnaire.questions.filter(
1487
1509
  (question) => question.required && isMissingRequiredAnswer(question, answers)
@@ -1917,7 +1939,7 @@ var Detail = styled5.li`
1917
1939
 
1918
1940
  // src/components/chat-thread/components/image-viewer.tsx
1919
1941
  import styled6 from "@emotion/styled";
1920
- import { useEffect as useEffect3, useRef as useRef4 } from "react";
1942
+ import { useEffect as useEffect3, useRef as useRef3 } from "react";
1921
1943
  import { jsx as jsx7 } from "@emotion/react/jsx-runtime";
1922
1944
  var Overlay = styled6.div`
1923
1945
  position: fixed;
@@ -1936,7 +1958,7 @@ var Img = styled6.img`
1936
1958
  border-radius: 4px;
1937
1959
  `;
1938
1960
  var ImageViewer = ({ src, alt, onClose }) => {
1939
- const overlayRef = useRef4(null);
1961
+ const overlayRef = useRef3(null);
1940
1962
  useEffect3(() => {
1941
1963
  const handleKey = (e) => {
1942
1964
  if (e.key === "Escape")
@@ -2109,7 +2131,7 @@ var ChatMessageItemView = ({
2109
2131
  renderMessageBlock
2110
2132
  }) => {
2111
2133
  const { labels, messageRenderOrder = "blocks-first" } = useChatContext();
2112
- const [activeImage, setActiveImage] = useState4(void 0);
2134
+ const [activeImage, setActiveImage] = useState3(void 0);
2113
2135
  const {
2114
2136
  displayedBlocks,
2115
2137
  displayedContent,
@@ -2685,7 +2707,7 @@ var ChatThreadView = ({
2685
2707
  onQuestionnaireSubmit,
2686
2708
  renderMessageBlock
2687
2709
  }) => {
2688
- const containerRef = useRef5(null);
2710
+ const containerRef = useRef4(null);
2689
2711
  const conversationTurns = useMemo4(
2690
2712
  () => groupConversationTurns(historyMessages, streamingMessage),
2691
2713
  [historyMessages, streamingMessage]
@@ -2693,9 +2715,9 @@ var ChatThreadView = ({
2693
2715
  const latestTurn = conversationTurns[conversationTurns.length - 1];
2694
2716
  const previousTurns = conversationTurns.slice(0, -1);
2695
2717
  const latestUserMessageId = latestTurn?.userMessage?.id;
2696
- const latestUserMessageRef = useRef5(null);
2697
- const reservedSpaceFrameRef = useRef5(null);
2698
- const [latestTurnMinHeight, setLatestTurnMinHeight] = useState5(0);
2718
+ const latestUserMessageRef = useRef4(null);
2719
+ const reservedSpaceFrameRef = useRef4(null);
2720
+ const [latestTurnMinHeight, setLatestTurnMinHeight] = useState4(0);
2699
2721
  const measureLatestTurnMinHeight = useCallback2(() => {
2700
2722
  const container = containerRef.current;
2701
2723
  if (!container)
@@ -2948,7 +2970,7 @@ var RetryButton = styled9.button`
2948
2970
  `;
2949
2971
 
2950
2972
  // src/components/chat-composer/index.tsx
2951
- import { useEffect as useEffect6, useRef as useRef8 } from "react";
2973
+ import { useEffect as useEffect6, useRef as useRef7 } from "react";
2952
2974
  import styled14 from "@emotion/styled";
2953
2975
 
2954
2976
  // src/components/chat-composer/lib/chat-composer.ts
@@ -3060,10 +3082,10 @@ var resolveSendSession = ({
3060
3082
  };
3061
3083
 
3062
3084
  // src/components/chat-composer/hooks/use-chat-composer.ts
3063
- import { useCallback as useCallback3, useEffect as useEffect5, useRef as useRef7, useState as useState7 } from "react";
3085
+ import { useCallback as useCallback3, useEffect as useEffect5, useRef as useRef6, useState as useState6 } from "react";
3064
3086
 
3065
3087
  // src/components/chat-composer/hooks/use-composer-attachments.ts
3066
- import { useEffect as useEffect4, useRef as useRef6, useState as useState6 } from "react";
3088
+ import { useEffect as useEffect4, useRef as useRef5, useState as useState5 } from "react";
3067
3089
  var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp"]);
3068
3090
  var MAX_COMPOSER_ATTACHMENTS = 10;
3069
3091
  var createObjectUrl = (file) => typeof URL !== "undefined" && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : "";
@@ -3077,8 +3099,8 @@ var releaseComposerAttachments = (attachments) => {
3077
3099
  attachments.forEach((attachment) => revokeObjectUrl(attachment.previewUrl));
3078
3100
  };
3079
3101
  var useComposerAttachments = () => {
3080
- const [attachments, setAttachments] = useState6([]);
3081
- const attachmentsRef = useRef6([]);
3102
+ const [attachments, setAttachments] = useState5([]);
3103
+ const attachmentsRef = useRef5([]);
3082
3104
  useEffect4(() => {
3083
3105
  attachmentsRef.current = attachments;
3084
3106
  }, [attachments]);
@@ -3195,9 +3217,9 @@ var useChatComposer = () => {
3195
3217
  const clearSessionError = useChatStore((s) => s.clearSessionError);
3196
3218
  const setPreferredMode = useChatStore((s) => s.setPreferredMode);
3197
3219
  const setSessionMode = useChatStore((s) => s.setSessionMode);
3198
- const [availableModels, setAvailableModels] = useState7([]);
3199
- const [isModelsLoading, setIsModelsLoading] = useState7(true);
3200
- const [isModelsError, setIsModelsError] = useState7(false);
3220
+ const [availableModels, setAvailableModels] = useState6([]);
3221
+ const [isModelsLoading, setIsModelsLoading] = useState6(true);
3222
+ const [isModelsError, setIsModelsError] = useState6(false);
3201
3223
  const fetchModels = useCallback3(async () => {
3202
3224
  setIsModelsLoading(true);
3203
3225
  setIsModelsError(false);
@@ -3214,14 +3236,14 @@ var useChatComposer = () => {
3214
3236
  void fetchModels();
3215
3237
  }, [fetchModels]);
3216
3238
  const hasModels = availableModels.length > 0;
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);
3239
+ const [value, setValue] = useState6("");
3240
+ const [selectedModel, setSelectedModel] = useState6("");
3241
+ const [selectedMode, setSelectedModeLocal] = useState6(DEFAULT_CHAT_AGENT_MODE);
3242
+ const [attachmentNotice, setAttachmentNotice] = useState6(null);
3221
3243
  const { attachments, appendFiles, removeAttachment, takeMessageAttachments } = useComposerAttachments();
3222
- const abortControllerRef = useRef7(null);
3223
- const stopRequestRef = useRef7(null);
3224
- const lastRequestRef = useRef7(null);
3244
+ const abortControllerRef = useRef6(null);
3245
+ const stopRequestRef = useRef6(null);
3246
+ const lastRequestRef = useRef6(null);
3225
3247
  useEffect5(() => {
3226
3248
  setSelectedModel(
3227
3249
  (current) => resolveSelectedChatModel({ currentModel: current, availableModels, isModelsLoading })
@@ -3513,14 +3535,14 @@ var useChatComposer = () => {
3513
3535
  };
3514
3536
 
3515
3537
  // src/components/chat-composer/components/chat-composer-attachment-list.tsx
3516
- import { useState as useState8 } from "react";
3538
+ import { useState as useState7 } from "react";
3517
3539
  import styled10 from "@emotion/styled";
3518
3540
  import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs8 } from "@emotion/react/jsx-runtime";
3519
3541
  var ChatComposerAttachmentList = ({
3520
3542
  attachments,
3521
3543
  onRemoveAttachment
3522
3544
  }) => {
3523
- const [activeImage, setActiveImage] = useState8(null);
3545
+ const [activeImage, setActiveImage] = useState7(null);
3524
3546
  if (!attachments.length) {
3525
3547
  return null;
3526
3548
  }
@@ -4012,7 +4034,7 @@ var ChatComposerView = ({
4012
4034
  onStop,
4013
4035
  onSend
4014
4036
  }) => {
4015
- const imageInputRef = useRef8(null);
4037
+ const imageInputRef = useRef7(null);
4016
4038
  const canSend = canSendChatMessage({
4017
4039
  value,
4018
4040
  attachmentCount: attachments.length,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xinghunm/ai-chat",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "AI chat React component library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",