@xinghunm/ai-chat 1.4.2 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -738,6 +738,7 @@ interface ChatState {
738
738
  sessionMessageLoadStatusBySession: Record<string, ChatSessionMessageLoadStatus>;
739
739
  sessionMessageLoadErrorBySession: Record<string, string | null>;
740
740
  historyMessagePaginationBySession: Record<string, ChatHistoryMessagePaginationState>;
741
+ timelineAnchorStateBySession: Record<string, Record<string, ChatTimelineAnchorStateSnapshot>>;
741
742
  }
742
743
  interface ChatHistoryMessagePaginationState {
743
744
  previousCursor: string | null;
@@ -745,6 +746,11 @@ interface ChatHistoryMessagePaginationState {
745
746
  isLoadingPrevious: boolean;
746
747
  error: string | null;
747
748
  }
749
+ interface ChatTimelineAnchorStateSnapshot {
750
+ previousBlockKeys: string[];
751
+ timelineBlockAnchors: Record<string, number>;
752
+ visibleTimelineBlockKeys: Record<string, true>;
753
+ }
748
754
  interface ChatActions {
749
755
  createSession: (session: ChatSession) => void;
750
756
  startNewChat: () => void;
@@ -758,6 +764,7 @@ interface ChatActions {
758
764
  prependHistorySessionMessagesPage: (sessionId: string, page: ChatHistorySessionMessagesPage) => void;
759
765
  setHistorySessionPreviousMessagesLoadStatus: (sessionId: string, isLoading: boolean, error?: string | null) => void;
760
766
  setHistorySessionMessageLoadStatus: (sessionId: string, status: ChatSessionMessageLoadStatus, error?: string | null) => void;
767
+ setTimelineAnchorState: (sessionId: string, messageId: string, snapshot: ChatTimelineAnchorStateSnapshot) => void;
761
768
  startStreamingMessage: (sessionId: string, message: ChatMessage) => void;
762
769
  updateStreamingMessage: (sessionId: string, content: string) => void;
763
770
  patchStreamingMessage: (sessionId: string, patch: ChatStreamMessagePatch) => void;
package/dist/index.d.ts CHANGED
@@ -738,6 +738,7 @@ interface ChatState {
738
738
  sessionMessageLoadStatusBySession: Record<string, ChatSessionMessageLoadStatus>;
739
739
  sessionMessageLoadErrorBySession: Record<string, string | null>;
740
740
  historyMessagePaginationBySession: Record<string, ChatHistoryMessagePaginationState>;
741
+ timelineAnchorStateBySession: Record<string, Record<string, ChatTimelineAnchorStateSnapshot>>;
741
742
  }
742
743
  interface ChatHistoryMessagePaginationState {
743
744
  previousCursor: string | null;
@@ -745,6 +746,11 @@ interface ChatHistoryMessagePaginationState {
745
746
  isLoadingPrevious: boolean;
746
747
  error: string | null;
747
748
  }
749
+ interface ChatTimelineAnchorStateSnapshot {
750
+ previousBlockKeys: string[];
751
+ timelineBlockAnchors: Record<string, number>;
752
+ visibleTimelineBlockKeys: Record<string, true>;
753
+ }
748
754
  interface ChatActions {
749
755
  createSession: (session: ChatSession) => void;
750
756
  startNewChat: () => void;
@@ -758,6 +764,7 @@ interface ChatActions {
758
764
  prependHistorySessionMessagesPage: (sessionId: string, page: ChatHistorySessionMessagesPage) => void;
759
765
  setHistorySessionPreviousMessagesLoadStatus: (sessionId: string, isLoading: boolean, error?: string | null) => void;
760
766
  setHistorySessionMessageLoadStatus: (sessionId: string, status: ChatSessionMessageLoadStatus, error?: string | null) => void;
767
+ setTimelineAnchorState: (sessionId: string, messageId: string, snapshot: ChatTimelineAnchorStateSnapshot) => void;
761
768
  startStreamingMessage: (sessionId: string, message: ChatMessage) => void;
762
769
  updateStreamingMessage: (sessionId: string, content: string) => void;
763
770
  patchStreamingMessage: (sessionId: string, patch: ChatStreamMessagePatch) => void;
package/dist/index.js CHANGED
@@ -157,6 +157,18 @@ var resolveSessionTitleFromMessage = (message) => {
157
157
  }
158
158
  return DEFAULT_CHAT_SESSION_TITLE;
159
159
  };
160
+ var areStringArraysEqual = (left, right) => left.length === right.length && left.every((value, index3) => value === right[index3]);
161
+ var areNumberRecordsEqual = (left, right) => {
162
+ const leftKeys = Object.keys(left);
163
+ const rightKeys = Object.keys(right);
164
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => right[key] === left[key]);
165
+ };
166
+ var areTrueRecordsEqual = (left, right) => {
167
+ const leftKeys = Object.keys(left);
168
+ const rightKeys = Object.keys(right);
169
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => right[key] === true);
170
+ };
171
+ var isSameTimelineAnchorSnapshot = (current, next) => current !== void 0 && areStringArraysEqual(current.previousBlockKeys, next.previousBlockKeys) && areNumberRecordsEqual(current.timelineBlockAnchors, next.timelineBlockAnchors) && areTrueRecordsEqual(current.visibleTimelineBlockKeys, next.visibleTimelineBlockKeys);
160
172
  var mergeStreamingBlocks = (existingBlocks, incomingBlocks) => {
161
173
  const nextBlocks = [...existingBlocks ?? []];
162
174
  incomingBlocks.forEach((incomingBlock) => {
@@ -249,6 +261,7 @@ var createChatStore = (initialState) => (0, import_vanilla.createStore)((set, ge
249
261
  sessionMessageLoadStatusBySession: {},
250
262
  sessionMessageLoadErrorBySession: {},
251
263
  historyMessagePaginationBySession: {},
264
+ timelineAnchorStateBySession: {},
252
265
  // ---- Session management ------------------------------------------------
253
266
  createSession: (session) => {
254
267
  const state = get();
@@ -267,6 +280,9 @@ var createChatStore = (initialState) => (0, import_vanilla.createStore)((set, ge
267
280
  const nextHistoryMessagePaginationBySession = {
268
281
  ...state.historyMessagePaginationBySession
269
282
  };
283
+ const nextTimelineAnchorStateBySession = {
284
+ ...state.timelineAnchorStateBySession
285
+ };
270
286
  const sid = session.sessionId;
271
287
  if (nextMessagesBySession[sid] === void 0)
272
288
  nextMessagesBySession[sid] = [];
@@ -294,7 +310,8 @@ var createChatStore = (initialState) => (0, import_vanilla.createStore)((set, ge
294
310
  isStoppingBySession: nextIsStoppingBySession,
295
311
  sessionMessageLoadStatusBySession: nextSessionMessageLoadStatusBySession,
296
312
  sessionMessageLoadErrorBySession: nextSessionMessageLoadErrorBySession,
297
- historyMessagePaginationBySession: nextHistoryMessagePaginationBySession
313
+ historyMessagePaginationBySession: nextHistoryMessagePaginationBySession,
314
+ timelineAnchorStateBySession: nextTimelineAnchorStateBySession
298
315
  });
299
316
  },
300
317
  startNewChat: () => {
@@ -364,10 +381,17 @@ var createChatStore = (initialState) => (0, import_vanilla.createStore)((set, ge
364
381
  const nextHistoryMessagePaginationBySession = {
365
382
  ...state.historyMessagePaginationBySession
366
383
  };
384
+ const nextTimelineAnchorStateBySession = {
385
+ ...state.timelineAnchorStateBySession
386
+ };
367
387
  if (previousSessionId in nextHistoryMessagePaginationBySession) {
368
388
  nextHistoryMessagePaginationBySession[nextSessionId] = nextHistoryMessagePaginationBySession[previousSessionId] ?? createHistoryMessagePaginationState();
369
389
  delete nextHistoryMessagePaginationBySession[previousSessionId];
370
390
  }
391
+ if (previousSessionId in nextTimelineAnchorStateBySession) {
392
+ nextTimelineAnchorStateBySession[nextSessionId] = nextTimelineAnchorStateBySession[previousSessionId] ?? {};
393
+ delete nextTimelineAnchorStateBySession[previousSessionId];
394
+ }
371
395
  const nextActiveSessionId = state.activeSessionId === previousSessionId ? nextSessionId : state.activeSessionId;
372
396
  set({
373
397
  sessions: nextSessions,
@@ -379,6 +403,7 @@ var createChatStore = (initialState) => (0, import_vanilla.createStore)((set, ge
379
403
  sessionMessageLoadStatusBySession: nextSessionMessageLoadStatusBySession,
380
404
  sessionMessageLoadErrorBySession: nextSessionMessageLoadErrorBySession,
381
405
  historyMessagePaginationBySession: nextHistoryMessagePaginationBySession,
406
+ timelineAnchorStateBySession: nextTimelineAnchorStateBySession,
382
407
  activeSessionId: nextActiveSessionId
383
408
  });
384
409
  },
@@ -419,6 +444,9 @@ var createChatStore = (initialState) => (0, import_vanilla.createStore)((set, ge
419
444
  const nextHistoryMessagePaginationBySession = {
420
445
  ...state.historyMessagePaginationBySession
421
446
  };
447
+ const nextTimelineAnchorStateBySession = {
448
+ ...state.timelineAnchorStateBySession
449
+ };
422
450
  nextSessions.forEach((session) => {
423
451
  const sid = session.sessionId;
424
452
  if (nextErrorBySession[sid] === void 0)
@@ -445,7 +473,8 @@ var createChatStore = (initialState) => (0, import_vanilla.createStore)((set, ge
445
473
  isStoppingBySession: nextIsStoppingBySession,
446
474
  sessionMessageLoadStatusBySession: nextSessionMessageLoadStatusBySession,
447
475
  sessionMessageLoadErrorBySession: nextSessionMessageLoadErrorBySession,
448
- historyMessagePaginationBySession: nextHistoryMessagePaginationBySession
476
+ historyMessagePaginationBySession: nextHistoryMessagePaginationBySession,
477
+ timelineAnchorStateBySession: nextTimelineAnchorStateBySession
449
478
  });
450
479
  },
451
480
  // ---- Message operations ------------------------------------------------
@@ -560,6 +589,23 @@ var createChatStore = (initialState) => (0, import_vanilla.createStore)((set, ge
560
589
  }
561
590
  });
562
591
  },
592
+ setTimelineAnchorState: (sessionId, messageId, snapshot) => {
593
+ const state = get();
594
+ const currentSessionSnapshots = state.timelineAnchorStateBySession[sessionId] ?? {};
595
+ const currentSnapshot = currentSessionSnapshots[messageId];
596
+ if (isSameTimelineAnchorSnapshot(currentSnapshot, snapshot)) {
597
+ return;
598
+ }
599
+ set({
600
+ timelineAnchorStateBySession: {
601
+ ...state.timelineAnchorStateBySession,
602
+ [sessionId]: {
603
+ ...currentSessionSnapshots,
604
+ [messageId]: snapshot
605
+ }
606
+ }
607
+ });
608
+ },
563
609
  startStreamingMessage: (sessionId, message) => {
564
610
  const state = get();
565
611
  set({
@@ -1550,13 +1596,39 @@ var createTimelineAnchorState = ({
1550
1596
  timelineBlockAnchors: {},
1551
1597
  visibleTimelineBlockKeys: {}
1552
1598
  });
1599
+ var restoreTimelineAnchorState = ({
1600
+ messageId,
1601
+ currentBlockKeys,
1602
+ persistedState
1603
+ }) => {
1604
+ if (!persistedState) {
1605
+ return createTimelineAnchorState({ messageId, currentBlockKeys });
1606
+ }
1607
+ const currentBlockKeySet = new Set(currentBlockKeys);
1608
+ return {
1609
+ messageId,
1610
+ previousBlockKeys: persistedState.previousBlockKeys.filter(
1611
+ (blockKey) => currentBlockKeySet.has(blockKey)
1612
+ ),
1613
+ timelineBlockAnchors: Object.fromEntries(
1614
+ Object.entries(persistedState.timelineBlockAnchors).filter(
1615
+ ([blockKey]) => currentBlockKeySet.has(blockKey)
1616
+ )
1617
+ ),
1618
+ visibleTimelineBlockKeys: Object.fromEntries(
1619
+ Object.entries(persistedState.visibleTimelineBlockKeys).filter(
1620
+ ([blockKey]) => currentBlockKeySet.has(blockKey)
1621
+ )
1622
+ )
1623
+ };
1624
+ };
1553
1625
  var createInitialTimelineAnchorState = ({
1554
- messageId
1555
- }) => ({
1556
1626
  messageId,
1557
- previousBlockKeys: [],
1558
- timelineBlockAnchors: {},
1559
- visibleTimelineBlockKeys: {}
1627
+ currentBlockKeys,
1628
+ persistedState
1629
+ }) => ({
1630
+ ...restoreTimelineAnchorState({ messageId, currentBlockKeys, persistedState }),
1631
+ previousBlockKeys: persistedState?.previousBlockKeys ?? []
1560
1632
  });
1561
1633
  var timelineAnchorReducer = (state, action) => {
1562
1634
  switch (action.type) {
@@ -1564,7 +1636,7 @@ var timelineAnchorReducer = (state, action) => {
1564
1636
  if (state.messageId === action.messageId) {
1565
1637
  return state;
1566
1638
  }
1567
- return createTimelineAnchorState(action);
1639
+ return restoreTimelineAnchorState(action);
1568
1640
  case "sync-anchors": {
1569
1641
  const previousBlockKeys = new Set(state.previousBlockKeys);
1570
1642
  const nextAnchors = action.currentBlockKeys.reduce(
@@ -1633,10 +1705,15 @@ var useTimelineBlockAnchors = ({
1633
1705
  message,
1634
1706
  messageRenderOrder
1635
1707
  }) => {
1708
+ const { store } = useChatContext();
1636
1709
  const currentTimelineBlockKeys = (0, import_react6.useMemo)(
1637
1710
  () => blocks.map((block, index3) => getTimelineBlockKey(block, index3)).filter((blockKey) => Boolean(blockKey)),
1638
1711
  [blocks]
1639
1712
  );
1713
+ const persistedState = (0, import_react6.useMemo)(
1714
+ () => store.getState().timelineAnchorStateBySession[message.sessionId]?.[message.id],
1715
+ [message.id, message.sessionId, store]
1716
+ );
1640
1717
  const timelineTextStreamLength = (0, import_react6.useMemo)(
1641
1718
  () => getTimelineDisplayUnitCount(getTimelineTextStream(message.content, blocks)),
1642
1719
  [blocks, message.content]
@@ -1645,7 +1722,8 @@ var useTimelineBlockAnchors = ({
1645
1722
  timelineAnchorReducer,
1646
1723
  {
1647
1724
  messageId: message.id,
1648
- currentBlockKeys: currentTimelineBlockKeys
1725
+ currentBlockKeys: currentTimelineBlockKeys,
1726
+ persistedState
1649
1727
  },
1650
1728
  createInitialTimelineAnchorState
1651
1729
  );
@@ -1680,9 +1758,10 @@ var useTimelineBlockAnchors = ({
1680
1758
  dispatch({
1681
1759
  type: "reset-message",
1682
1760
  messageId: message.id,
1683
- currentBlockKeys: currentTimelineBlockKeys
1761
+ currentBlockKeys: currentTimelineBlockKeys,
1762
+ persistedState
1684
1763
  });
1685
- }, [currentTimelineBlockKeys, message.id]);
1764
+ }, [currentTimelineBlockKeys, message.id, persistedState]);
1686
1765
  (0, import_react6.useEffect)(() => {
1687
1766
  if (messageRenderOrder !== "timeline" || !isAssistantStreaming) {
1688
1767
  return;
@@ -1709,6 +1788,24 @@ var useTimelineBlockAnchors = ({
1709
1788
  effectiveTimelineBlockAnchors,
1710
1789
  messageRenderOrder
1711
1790
  ]);
1791
+ (0, import_react6.useEffect)(() => {
1792
+ if (messageRenderOrder !== "timeline") {
1793
+ return;
1794
+ }
1795
+ store.getState().setTimelineAnchorState(message.sessionId, message.id, {
1796
+ previousBlockKeys: state.previousBlockKeys,
1797
+ timelineBlockAnchors: state.timelineBlockAnchors,
1798
+ visibleTimelineBlockKeys: state.visibleTimelineBlockKeys
1799
+ });
1800
+ }, [
1801
+ message.id,
1802
+ message.sessionId,
1803
+ messageRenderOrder,
1804
+ state.previousBlockKeys,
1805
+ state.timelineBlockAnchors,
1806
+ state.visibleTimelineBlockKeys,
1807
+ store
1808
+ ]);
1712
1809
  return {
1713
1810
  timelineBlockAnchors: messageRenderOrder === "timeline" ? effectiveTimelineBlockAnchors : {},
1714
1811
  visibleTimelineBlockKeys: messageRenderOrder === "timeline" ? state.visibleTimelineBlockKeys : {}
@@ -3018,10 +3115,10 @@ var areExecutionProposalsEqual = (previousProposal, nextProposal) => previousPro
3018
3115
  (warning, index3) => warning === nextProposal.warnings?.[index3]
3019
3116
  );
3020
3117
  var areResultSummariesEqual = (previousSummary, nextSummary) => previousSummary.summaryId === nextSummary.summaryId && previousSummary.status === nextSummary.status && previousSummary.headline === nextSummary.headline && previousSummary.details.length === nextSummary.details.length && previousSummary.details.every((detail, index3) => detail === nextSummary.details[index3]);
3021
- var areStringArraysEqual = (previousValues, nextValues) => previousValues.length === nextValues.length && previousValues.every((value, index3) => value === nextValues[index3]);
3118
+ var areStringArraysEqual2 = (previousValues, nextValues) => previousValues.length === nextValues.length && previousValues.every((value, index3) => value === nextValues[index3]);
3022
3119
  var areQuestionAnswersEqual = (previousAnswer, nextAnswer) => {
3023
3120
  if (Array.isArray(previousAnswer) || Array.isArray(nextAnswer)) {
3024
- return Array.isArray(previousAnswer) && Array.isArray(nextAnswer) && areStringArraysEqual(previousAnswer, nextAnswer);
3121
+ return Array.isArray(previousAnswer) && Array.isArray(nextAnswer) && areStringArraysEqual2(previousAnswer, nextAnswer);
3025
3122
  }
3026
3123
  return previousAnswer === nextAnswer;
3027
3124
  };
@@ -3108,7 +3205,7 @@ var areMessageBlocksEqual = (previousBlocks, nextBlocks) => {
3108
3205
  }
3109
3206
  });
3110
3207
  };
3111
- var isSameMessage = (previousMessage, nextMessage, previousMode, nextMode, previousConfirmationSubmit, nextConfirmationSubmit, previousQuestionnaireSubmit, nextQuestionnaireSubmit, previousRenderMessageBlock, nextRenderMessageBlock) => previousMessage.id === nextMessage.id && previousMessage.sessionId === nextMessage.sessionId && previousMessage.role === nextMessage.role && previousMessage.content === nextMessage.content && areStringArraysEqual(previousMessage.skills ?? [], nextMessage.skills ?? []) && areMessageBlocksEqual(previousMessage.blocks, nextMessage.blocks) && previousMessage.localOnly === nextMessage.localOnly && areChatAttachmentsEqual(previousMessage.attachments, nextMessage.attachments) && previousMessage.status === nextMessage.status && previousMessage.createdAt === nextMessage.createdAt && previousMode === nextMode && previousConfirmationSubmit === nextConfirmationSubmit && previousQuestionnaireSubmit === nextQuestionnaireSubmit && previousRenderMessageBlock === nextRenderMessageBlock;
3208
+ var isSameMessage = (previousMessage, nextMessage, previousMode, nextMode, previousConfirmationSubmit, nextConfirmationSubmit, previousQuestionnaireSubmit, nextQuestionnaireSubmit, previousRenderMessageBlock, nextRenderMessageBlock) => previousMessage.id === nextMessage.id && previousMessage.sessionId === nextMessage.sessionId && previousMessage.role === nextMessage.role && previousMessage.content === nextMessage.content && areStringArraysEqual2(previousMessage.skills ?? [], nextMessage.skills ?? []) && areMessageBlocksEqual(previousMessage.blocks, nextMessage.blocks) && previousMessage.localOnly === nextMessage.localOnly && areChatAttachmentsEqual(previousMessage.attachments, nextMessage.attachments) && previousMessage.status === nextMessage.status && previousMessage.createdAt === nextMessage.createdAt && previousMode === nextMode && previousConfirmationSubmit === nextConfirmationSubmit && previousQuestionnaireSubmit === nextQuestionnaireSubmit && previousRenderMessageBlock === nextRenderMessageBlock;
3112
3209
  var ChatMessageItemView = ({
3113
3210
  message,
3114
3211
  mode = "agent",
package/dist/index.mjs CHANGED
@@ -110,6 +110,18 @@ var resolveSessionTitleFromMessage = (message) => {
110
110
  }
111
111
  return DEFAULT_CHAT_SESSION_TITLE;
112
112
  };
113
+ var areStringArraysEqual = (left, right) => left.length === right.length && left.every((value, index3) => value === right[index3]);
114
+ var areNumberRecordsEqual = (left, right) => {
115
+ const leftKeys = Object.keys(left);
116
+ const rightKeys = Object.keys(right);
117
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => right[key] === left[key]);
118
+ };
119
+ var areTrueRecordsEqual = (left, right) => {
120
+ const leftKeys = Object.keys(left);
121
+ const rightKeys = Object.keys(right);
122
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => right[key] === true);
123
+ };
124
+ var isSameTimelineAnchorSnapshot = (current, next) => current !== void 0 && areStringArraysEqual(current.previousBlockKeys, next.previousBlockKeys) && areNumberRecordsEqual(current.timelineBlockAnchors, next.timelineBlockAnchors) && areTrueRecordsEqual(current.visibleTimelineBlockKeys, next.visibleTimelineBlockKeys);
113
125
  var mergeStreamingBlocks = (existingBlocks, incomingBlocks) => {
114
126
  const nextBlocks = [...existingBlocks ?? []];
115
127
  incomingBlocks.forEach((incomingBlock) => {
@@ -202,6 +214,7 @@ var createChatStore = (initialState) => createStore((set, get) => ({
202
214
  sessionMessageLoadStatusBySession: {},
203
215
  sessionMessageLoadErrorBySession: {},
204
216
  historyMessagePaginationBySession: {},
217
+ timelineAnchorStateBySession: {},
205
218
  // ---- Session management ------------------------------------------------
206
219
  createSession: (session) => {
207
220
  const state = get();
@@ -220,6 +233,9 @@ var createChatStore = (initialState) => createStore((set, get) => ({
220
233
  const nextHistoryMessagePaginationBySession = {
221
234
  ...state.historyMessagePaginationBySession
222
235
  };
236
+ const nextTimelineAnchorStateBySession = {
237
+ ...state.timelineAnchorStateBySession
238
+ };
223
239
  const sid = session.sessionId;
224
240
  if (nextMessagesBySession[sid] === void 0)
225
241
  nextMessagesBySession[sid] = [];
@@ -247,7 +263,8 @@ var createChatStore = (initialState) => createStore((set, get) => ({
247
263
  isStoppingBySession: nextIsStoppingBySession,
248
264
  sessionMessageLoadStatusBySession: nextSessionMessageLoadStatusBySession,
249
265
  sessionMessageLoadErrorBySession: nextSessionMessageLoadErrorBySession,
250
- historyMessagePaginationBySession: nextHistoryMessagePaginationBySession
266
+ historyMessagePaginationBySession: nextHistoryMessagePaginationBySession,
267
+ timelineAnchorStateBySession: nextTimelineAnchorStateBySession
251
268
  });
252
269
  },
253
270
  startNewChat: () => {
@@ -317,10 +334,17 @@ var createChatStore = (initialState) => createStore((set, get) => ({
317
334
  const nextHistoryMessagePaginationBySession = {
318
335
  ...state.historyMessagePaginationBySession
319
336
  };
337
+ const nextTimelineAnchorStateBySession = {
338
+ ...state.timelineAnchorStateBySession
339
+ };
320
340
  if (previousSessionId in nextHistoryMessagePaginationBySession) {
321
341
  nextHistoryMessagePaginationBySession[nextSessionId] = nextHistoryMessagePaginationBySession[previousSessionId] ?? createHistoryMessagePaginationState();
322
342
  delete nextHistoryMessagePaginationBySession[previousSessionId];
323
343
  }
344
+ if (previousSessionId in nextTimelineAnchorStateBySession) {
345
+ nextTimelineAnchorStateBySession[nextSessionId] = nextTimelineAnchorStateBySession[previousSessionId] ?? {};
346
+ delete nextTimelineAnchorStateBySession[previousSessionId];
347
+ }
324
348
  const nextActiveSessionId = state.activeSessionId === previousSessionId ? nextSessionId : state.activeSessionId;
325
349
  set({
326
350
  sessions: nextSessions,
@@ -332,6 +356,7 @@ var createChatStore = (initialState) => createStore((set, get) => ({
332
356
  sessionMessageLoadStatusBySession: nextSessionMessageLoadStatusBySession,
333
357
  sessionMessageLoadErrorBySession: nextSessionMessageLoadErrorBySession,
334
358
  historyMessagePaginationBySession: nextHistoryMessagePaginationBySession,
359
+ timelineAnchorStateBySession: nextTimelineAnchorStateBySession,
335
360
  activeSessionId: nextActiveSessionId
336
361
  });
337
362
  },
@@ -372,6 +397,9 @@ var createChatStore = (initialState) => createStore((set, get) => ({
372
397
  const nextHistoryMessagePaginationBySession = {
373
398
  ...state.historyMessagePaginationBySession
374
399
  };
400
+ const nextTimelineAnchorStateBySession = {
401
+ ...state.timelineAnchorStateBySession
402
+ };
375
403
  nextSessions.forEach((session) => {
376
404
  const sid = session.sessionId;
377
405
  if (nextErrorBySession[sid] === void 0)
@@ -398,7 +426,8 @@ var createChatStore = (initialState) => createStore((set, get) => ({
398
426
  isStoppingBySession: nextIsStoppingBySession,
399
427
  sessionMessageLoadStatusBySession: nextSessionMessageLoadStatusBySession,
400
428
  sessionMessageLoadErrorBySession: nextSessionMessageLoadErrorBySession,
401
- historyMessagePaginationBySession: nextHistoryMessagePaginationBySession
429
+ historyMessagePaginationBySession: nextHistoryMessagePaginationBySession,
430
+ timelineAnchorStateBySession: nextTimelineAnchorStateBySession
402
431
  });
403
432
  },
404
433
  // ---- Message operations ------------------------------------------------
@@ -513,6 +542,23 @@ var createChatStore = (initialState) => createStore((set, get) => ({
513
542
  }
514
543
  });
515
544
  },
545
+ setTimelineAnchorState: (sessionId, messageId, snapshot) => {
546
+ const state = get();
547
+ const currentSessionSnapshots = state.timelineAnchorStateBySession[sessionId] ?? {};
548
+ const currentSnapshot = currentSessionSnapshots[messageId];
549
+ if (isSameTimelineAnchorSnapshot(currentSnapshot, snapshot)) {
550
+ return;
551
+ }
552
+ set({
553
+ timelineAnchorStateBySession: {
554
+ ...state.timelineAnchorStateBySession,
555
+ [sessionId]: {
556
+ ...currentSessionSnapshots,
557
+ [messageId]: snapshot
558
+ }
559
+ }
560
+ });
561
+ },
516
562
  startStreamingMessage: (sessionId, message) => {
517
563
  const state = get();
518
564
  set({
@@ -1503,13 +1549,39 @@ var createTimelineAnchorState = ({
1503
1549
  timelineBlockAnchors: {},
1504
1550
  visibleTimelineBlockKeys: {}
1505
1551
  });
1552
+ var restoreTimelineAnchorState = ({
1553
+ messageId,
1554
+ currentBlockKeys,
1555
+ persistedState
1556
+ }) => {
1557
+ if (!persistedState) {
1558
+ return createTimelineAnchorState({ messageId, currentBlockKeys });
1559
+ }
1560
+ const currentBlockKeySet = new Set(currentBlockKeys);
1561
+ return {
1562
+ messageId,
1563
+ previousBlockKeys: persistedState.previousBlockKeys.filter(
1564
+ (blockKey) => currentBlockKeySet.has(blockKey)
1565
+ ),
1566
+ timelineBlockAnchors: Object.fromEntries(
1567
+ Object.entries(persistedState.timelineBlockAnchors).filter(
1568
+ ([blockKey]) => currentBlockKeySet.has(blockKey)
1569
+ )
1570
+ ),
1571
+ visibleTimelineBlockKeys: Object.fromEntries(
1572
+ Object.entries(persistedState.visibleTimelineBlockKeys).filter(
1573
+ ([blockKey]) => currentBlockKeySet.has(blockKey)
1574
+ )
1575
+ )
1576
+ };
1577
+ };
1506
1578
  var createInitialTimelineAnchorState = ({
1507
- messageId
1508
- }) => ({
1509
1579
  messageId,
1510
- previousBlockKeys: [],
1511
- timelineBlockAnchors: {},
1512
- visibleTimelineBlockKeys: {}
1580
+ currentBlockKeys,
1581
+ persistedState
1582
+ }) => ({
1583
+ ...restoreTimelineAnchorState({ messageId, currentBlockKeys, persistedState }),
1584
+ previousBlockKeys: persistedState?.previousBlockKeys ?? []
1513
1585
  });
1514
1586
  var timelineAnchorReducer = (state, action) => {
1515
1587
  switch (action.type) {
@@ -1517,7 +1589,7 @@ var timelineAnchorReducer = (state, action) => {
1517
1589
  if (state.messageId === action.messageId) {
1518
1590
  return state;
1519
1591
  }
1520
- return createTimelineAnchorState(action);
1592
+ return restoreTimelineAnchorState(action);
1521
1593
  case "sync-anchors": {
1522
1594
  const previousBlockKeys = new Set(state.previousBlockKeys);
1523
1595
  const nextAnchors = action.currentBlockKeys.reduce(
@@ -1586,10 +1658,15 @@ var useTimelineBlockAnchors = ({
1586
1658
  message,
1587
1659
  messageRenderOrder
1588
1660
  }) => {
1661
+ const { store } = useChatContext();
1589
1662
  const currentTimelineBlockKeys = useMemo3(
1590
1663
  () => blocks.map((block, index3) => getTimelineBlockKey(block, index3)).filter((blockKey) => Boolean(blockKey)),
1591
1664
  [blocks]
1592
1665
  );
1666
+ const persistedState = useMemo3(
1667
+ () => store.getState().timelineAnchorStateBySession[message.sessionId]?.[message.id],
1668
+ [message.id, message.sessionId, store]
1669
+ );
1593
1670
  const timelineTextStreamLength = useMemo3(
1594
1671
  () => getTimelineDisplayUnitCount(getTimelineTextStream(message.content, blocks)),
1595
1672
  [blocks, message.content]
@@ -1598,7 +1675,8 @@ var useTimelineBlockAnchors = ({
1598
1675
  timelineAnchorReducer,
1599
1676
  {
1600
1677
  messageId: message.id,
1601
- currentBlockKeys: currentTimelineBlockKeys
1678
+ currentBlockKeys: currentTimelineBlockKeys,
1679
+ persistedState
1602
1680
  },
1603
1681
  createInitialTimelineAnchorState
1604
1682
  );
@@ -1633,9 +1711,10 @@ var useTimelineBlockAnchors = ({
1633
1711
  dispatch({
1634
1712
  type: "reset-message",
1635
1713
  messageId: message.id,
1636
- currentBlockKeys: currentTimelineBlockKeys
1714
+ currentBlockKeys: currentTimelineBlockKeys,
1715
+ persistedState
1637
1716
  });
1638
- }, [currentTimelineBlockKeys, message.id]);
1717
+ }, [currentTimelineBlockKeys, message.id, persistedState]);
1639
1718
  useEffect3(() => {
1640
1719
  if (messageRenderOrder !== "timeline" || !isAssistantStreaming) {
1641
1720
  return;
@@ -1662,6 +1741,24 @@ var useTimelineBlockAnchors = ({
1662
1741
  effectiveTimelineBlockAnchors,
1663
1742
  messageRenderOrder
1664
1743
  ]);
1744
+ useEffect3(() => {
1745
+ if (messageRenderOrder !== "timeline") {
1746
+ return;
1747
+ }
1748
+ store.getState().setTimelineAnchorState(message.sessionId, message.id, {
1749
+ previousBlockKeys: state.previousBlockKeys,
1750
+ timelineBlockAnchors: state.timelineBlockAnchors,
1751
+ visibleTimelineBlockKeys: state.visibleTimelineBlockKeys
1752
+ });
1753
+ }, [
1754
+ message.id,
1755
+ message.sessionId,
1756
+ messageRenderOrder,
1757
+ state.previousBlockKeys,
1758
+ state.timelineBlockAnchors,
1759
+ state.visibleTimelineBlockKeys,
1760
+ store
1761
+ ]);
1665
1762
  return {
1666
1763
  timelineBlockAnchors: messageRenderOrder === "timeline" ? effectiveTimelineBlockAnchors : {},
1667
1764
  visibleTimelineBlockKeys: messageRenderOrder === "timeline" ? state.visibleTimelineBlockKeys : {}
@@ -2975,10 +3072,10 @@ var areExecutionProposalsEqual = (previousProposal, nextProposal) => previousPro
2975
3072
  (warning, index3) => warning === nextProposal.warnings?.[index3]
2976
3073
  );
2977
3074
  var areResultSummariesEqual = (previousSummary, nextSummary) => previousSummary.summaryId === nextSummary.summaryId && previousSummary.status === nextSummary.status && previousSummary.headline === nextSummary.headline && previousSummary.details.length === nextSummary.details.length && previousSummary.details.every((detail, index3) => detail === nextSummary.details[index3]);
2978
- var areStringArraysEqual = (previousValues, nextValues) => previousValues.length === nextValues.length && previousValues.every((value, index3) => value === nextValues[index3]);
3075
+ var areStringArraysEqual2 = (previousValues, nextValues) => previousValues.length === nextValues.length && previousValues.every((value, index3) => value === nextValues[index3]);
2979
3076
  var areQuestionAnswersEqual = (previousAnswer, nextAnswer) => {
2980
3077
  if (Array.isArray(previousAnswer) || Array.isArray(nextAnswer)) {
2981
- return Array.isArray(previousAnswer) && Array.isArray(nextAnswer) && areStringArraysEqual(previousAnswer, nextAnswer);
3078
+ return Array.isArray(previousAnswer) && Array.isArray(nextAnswer) && areStringArraysEqual2(previousAnswer, nextAnswer);
2982
3079
  }
2983
3080
  return previousAnswer === nextAnswer;
2984
3081
  };
@@ -3065,7 +3162,7 @@ var areMessageBlocksEqual = (previousBlocks, nextBlocks) => {
3065
3162
  }
3066
3163
  });
3067
3164
  };
3068
- var isSameMessage = (previousMessage, nextMessage, previousMode, nextMode, previousConfirmationSubmit, nextConfirmationSubmit, previousQuestionnaireSubmit, nextQuestionnaireSubmit, previousRenderMessageBlock, nextRenderMessageBlock) => previousMessage.id === nextMessage.id && previousMessage.sessionId === nextMessage.sessionId && previousMessage.role === nextMessage.role && previousMessage.content === nextMessage.content && areStringArraysEqual(previousMessage.skills ?? [], nextMessage.skills ?? []) && areMessageBlocksEqual(previousMessage.blocks, nextMessage.blocks) && previousMessage.localOnly === nextMessage.localOnly && areChatAttachmentsEqual(previousMessage.attachments, nextMessage.attachments) && previousMessage.status === nextMessage.status && previousMessage.createdAt === nextMessage.createdAt && previousMode === nextMode && previousConfirmationSubmit === nextConfirmationSubmit && previousQuestionnaireSubmit === nextQuestionnaireSubmit && previousRenderMessageBlock === nextRenderMessageBlock;
3165
+ var isSameMessage = (previousMessage, nextMessage, previousMode, nextMode, previousConfirmationSubmit, nextConfirmationSubmit, previousQuestionnaireSubmit, nextQuestionnaireSubmit, previousRenderMessageBlock, nextRenderMessageBlock) => previousMessage.id === nextMessage.id && previousMessage.sessionId === nextMessage.sessionId && previousMessage.role === nextMessage.role && previousMessage.content === nextMessage.content && areStringArraysEqual2(previousMessage.skills ?? [], nextMessage.skills ?? []) && areMessageBlocksEqual(previousMessage.blocks, nextMessage.blocks) && previousMessage.localOnly === nextMessage.localOnly && areChatAttachmentsEqual(previousMessage.attachments, nextMessage.attachments) && previousMessage.status === nextMessage.status && previousMessage.createdAt === nextMessage.createdAt && previousMode === nextMode && previousConfirmationSubmit === nextConfirmationSubmit && previousQuestionnaireSubmit === nextQuestionnaireSubmit && previousRenderMessageBlock === nextRenderMessageBlock;
3069
3166
  var ChatMessageItemView = ({
3070
3167
  message,
3071
3168
  mode = "agent",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xinghunm/ai-chat",
3
- "version": "1.4.2",
3
+ "version": "1.4.3",
4
4
  "description": "AI chat React component library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -19,7 +19,7 @@
19
19
  "peerDependencies": {
20
20
  "@emotion/react": ">=11",
21
21
  "@emotion/styled": ">=11",
22
- "@xinghunm/compass-ui": "0.8.3",
22
+ "@xinghunm/compass-ui": "0.9.0",
23
23
  "axios": ">=1.0",
24
24
  "react": ">=18",
25
25
  "react-dom": ">=18",