mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-04-13 00:51:30 +00:00
FEAT: improve conversation history loading for long sessions (#371)
* feat: add "Load all messages" button for long conversations Scrolling up through long conversations requires many "load more" cycles. This adds a "Load all messages" floating button that fetches the entire conversation history in one shot. - Floating overlay pill appears after each batch finishes loading, persists 2s - Shows loading spinner while fetching all messages - Shows green "All messages loaded" confirmation for 1s before disappearing - Preserves scroll position when bulk-loading (no viewport jump) - Ref-based guards prevent scroll handler from re-fetching after load-all - Performance warning shown; "Scroll to bottom" resets visible cap - Clean state reset on session switch - i18n keys for en and zh-CN Note: default page size (20) and visible cap (100) are unchanged. These could be increased in a follow-up or made configurable via settings. * re-implement load-all feature for new TS architecture
This commit is contained in:
@@ -37,6 +37,11 @@ interface ChatMessagesPaneProps {
|
||||
visibleMessageCount: number;
|
||||
visibleMessages: ChatMessage[];
|
||||
loadEarlierMessages: () => void;
|
||||
loadAllMessages: () => void;
|
||||
allMessagesLoaded: boolean;
|
||||
isLoadingAllMessages: boolean;
|
||||
loadAllJustFinished: boolean;
|
||||
showLoadAllOverlay: boolean;
|
||||
createDiff: any;
|
||||
onFileOpen?: (filePath: string, diffInfo?: unknown) => void;
|
||||
onShowSettings?: () => void;
|
||||
@@ -76,6 +81,11 @@ export default function ChatMessagesPane({
|
||||
visibleMessageCount,
|
||||
visibleMessages,
|
||||
loadEarlierMessages,
|
||||
loadAllMessages,
|
||||
allMessagesLoaded,
|
||||
isLoadingAllMessages,
|
||||
loadAllJustFinished,
|
||||
showLoadAllOverlay,
|
||||
createDiff,
|
||||
onFileOpen,
|
||||
onShowSettings,
|
||||
@@ -149,7 +159,8 @@ export default function ChatMessagesPane({
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{isLoadingMoreMessages && (
|
||||
{/* Loading indicator for older messages (hide when load-all is active) */}
|
||||
{isLoadingMoreMessages && !isLoadingAllMessages && !allMessagesLoaded && (
|
||||
<div className="text-center text-gray-500 dark:text-gray-400 py-3">
|
||||
<div className="flex items-center justify-center space-x-2">
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-gray-400" />
|
||||
@@ -158,23 +169,69 @@ export default function ChatMessagesPane({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasMoreMessages && !isLoadingMoreMessages && (
|
||||
{/* Indicator showing there are more messages to load (hide when all loaded) */}
|
||||
{hasMoreMessages && !isLoadingMoreMessages && !allMessagesLoaded && (
|
||||
<div className="text-center text-gray-500 dark:text-gray-400 text-sm py-2 border-b border-gray-200 dark:border-gray-700">
|
||||
{totalMessages > 0 && (
|
||||
<span>
|
||||
{t('session.messages.showingOf', { shown: sessionMessagesCount, total: totalMessages })} |
|
||||
{t('session.messages.showingOf', { shown: sessionMessagesCount, total: totalMessages })}{' '}
|
||||
<span className="text-xs">{t('session.messages.scrollToLoad')}</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Floating "Load all messages" overlay */}
|
||||
{(showLoadAllOverlay || isLoadingAllMessages || loadAllJustFinished) && (
|
||||
<div className="sticky top-2 z-20 flex justify-center pointer-events-none">
|
||||
{loadAllJustFinished ? (
|
||||
<div className="px-4 py-1.5 text-xs font-medium text-white bg-green-600 dark:bg-green-500 rounded-full shadow-lg flex items-center space-x-2">
|
||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
<span>{t('session.messages.allLoaded')}</span>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className="pointer-events-auto px-4 py-1.5 text-xs font-medium text-white bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 rounded-full shadow-lg transition-all duration-200 hover:scale-105 disabled:opacity-75 disabled:cursor-wait flex items-center space-x-2"
|
||||
onClick={loadAllMessages}
|
||||
disabled={isLoadingAllMessages}
|
||||
>
|
||||
{isLoadingAllMessages && (
|
||||
<div className="animate-spin rounded-full h-3 w-3 border-2 border-white/30 border-t-white" />
|
||||
)}
|
||||
<span>
|
||||
{isLoadingAllMessages
|
||||
? t('session.messages.loadingAll')
|
||||
: <>{t('session.messages.loadAll')} {totalMessages > 0 && `(${totalMessages})`}</>
|
||||
}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Performance warning when all messages are loaded */}
|
||||
{allMessagesLoaded && (
|
||||
<div className="text-center text-amber-600 dark:text-amber-400 text-xs py-1.5 bg-amber-50 dark:bg-amber-900/20 border-b border-amber-200 dark:border-amber-800">
|
||||
{t('session.messages.perfWarning')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Legacy message count indicator (for non-paginated view) */}
|
||||
{!hasMoreMessages && chatMessages.length > visibleMessageCount && (
|
||||
<div className="text-center text-gray-500 dark:text-gray-400 text-sm py-2 border-b border-gray-200 dark:border-gray-700">
|
||||
{t('session.messages.showingLast', { count: visibleMessageCount, total: chatMessages.length })} |
|
||||
<button className="ml-1 text-blue-600 hover:text-blue-700 underline" onClick={loadEarlierMessages}>
|
||||
{t('session.messages.loadEarlier')}
|
||||
</button>
|
||||
{' | '}
|
||||
<button
|
||||
className="text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 underline"
|
||||
onClick={loadAllMessages}
|
||||
>
|
||||
{t('session.messages.loadAll')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user