fix(cursor-chat): count totals as rendered rows, not normalized transport rows

Cursor sessions were still showing inflated totals after the earlier total-count
work, producing UX mismatches like "Showing 16 of 31" while only ~16 visible
chat rows existed.

Why this happened:
- Cursor history normalization emits both `tool_use` and `tool_result` entries.
- The UI intentionally does not render `tool_result` as its own message bubble;
  it attaches tool results onto the related `tool_use` card.
- Total counting that includes `tool_result` therefore measures transport
  artifacts, not user-visible conversation rows.
- In practice, this makes totals look wrong and undermines trust in pagination
  status and session message counts.

Why this change:
- `total` is a user-facing semantic value and must represent what the user can
  actually see in the timeline.
- Cursor should follow the same rendered-message counting rule as other
  providers to keep behavior predictable across providers.
- Avoiding hidden/internal row counts in totals prevents confusion in
  "showing X of Y" indicators and load-more expectations.

What was changed:
- Replaced implicit/raw-style counting with explicit provider-side total
  tracking in Cursor history fetch.
- Total tracker increments only for processed messages that map to rendered chat
  rows, excluding standalone `tool_result` entries.
- Pagination windowing remains based on full normalized history payload to avoid
  changing ordering/loading mechanics; only displayed `total` semantics were
  corrected.

Result:
- Cursor `total` now reflects rendered chat rows rather than internal normalized
  event count.
- Session counters are now aligned with what users perceive in the UI.
This commit is contained in:
Haileyesus
2026-05-08 15:35:45 +03:00
parent 13d1d436f8
commit 5554e4e85e

View File

@@ -182,6 +182,7 @@ export function useChatSessionState({
messagesOffsetRef.current = 0;
setHasMoreMessages(false);
setTotalMessages(0);
setTokenBudget(null);
setVisibleMessageCount(INITIAL_VISIBLE_MESSAGES);
setAllMessagesLoaded(false);
@@ -318,7 +319,6 @@ export function useChatSessionState({
if (!hasMoreMessages || !selectedSession || !selectedProject) return false;
const sessionProvider = selectedSession.__provider || 'claude';
if (sessionProvider === 'cursor') return false;
isLoadingMoreRef.current = true;
const previousScrollHeight = container.scrollHeight;
@@ -551,7 +551,6 @@ export function useChatSessionState({
const scrollToTarget = async () => {
if (!allMessagesLoadedRef.current && selectedSession && selectedProject) {
const sessionProvider = selectedSession.__provider || 'claude';
if (sessionProvider !== 'cursor') {
try {
// Load all messages into the store for search navigation
const slot = await sessionStore.fetchFromServer(selectedSession.id, {
@@ -573,7 +572,6 @@ export function useChatSessionState({
} catch {
// Fall through and scroll in current messages
}
}
}
setVisibleMessageCount(Infinity);
@@ -722,15 +720,6 @@ export function useChatSessionState({
if (!selectedSession || !selectedProject) return;
if (isLoadingAllMessages) return;
const sessionProvider = selectedSession.__provider || 'claude';
if (sessionProvider === 'cursor') {
setVisibleMessageCount(Infinity);
setAllMessagesLoaded(true);
allMessagesLoadedRef.current = true;
setLoadAllJustFinished(true);
if (loadAllFinishedTimerRef.current) clearTimeout(loadAllFinishedTimerRef.current);
loadAllFinishedTimerRef.current = setTimeout(() => { setLoadAllJustFinished(false); setShowLoadAllOverlay(false); }, 1000);
return;
}
const requestSessionId = selectedSession.id;
allMessagesLoadedRef.current = true;