fix: stabilize ai design history switching

This commit is contained in:
2026-08-19 10:31:36 +08:00
parent abece81fdb
commit bf0b805d9f
14 changed files with 573 additions and 48 deletions

View File

@@ -930,6 +930,8 @@ export function ImageCanvas() {
const conversation = useImageWorkspaceStore((state) => state.conversation);
const tasks = useImageWorkspaceStore((state) => state.tasks);
const pendingTurn = useImageWorkspaceStore((state) => state.pendingTurn);
const loadingOlderMessages = useImageWorkspaceStore((state) => state.loadingOlderMessages);
const olderMessagesError = useImageWorkspaceStore((state) => state.olderMessagesError);
const workspaceError = useImageWorkspaceStore((state) => state.error);
const load = useImageWorkspaceStore((state) => state.load);
const connectTaskStream = useImageWorkspaceStore((state) => state.connectTaskStream);
@@ -937,6 +939,7 @@ export function ImageCanvas() {
const markTaskStreamNeedsReconciliation = useImageWorkspaceStore(
(state) => state.markTaskStreamNeedsReconciliation,
);
const loadOlderMessages = useImageWorkspaceStore((state) => state.loadOlderMessages);
const sendMessage = useImageWorkspaceStore((state) => state.sendMessage);
const updateGenerationQuote = useImageWorkspaceStore(
(state) => state.updateGenerationQuote,
@@ -966,6 +969,7 @@ export function ImageCanvas() {
const conversationEndRef = useRef<HTMLDivElement | null>(null);
const conversationWindowStartRef = useRef<number | null>(null);
const conversationShouldStickRef = useRef(true);
const olderMessagesRequestRef = useRef(false);
const promptRef = useRef<HTMLTextAreaElement | null>(null);
const referenceUploadInputRef = useRef<HTMLInputElement | null>(null);
const promptSelectionRef = useRef({ start: 0, end: 0 });
@@ -1111,6 +1115,7 @@ export function ImageCanvas() {
useEffect(() => {
conversationWindowStartRef.current = null;
conversationShouldStickRef.current = true;
olderMessagesRequestRef.current = false;
setConversationWindowStart(null);
setPrompt('');
setReferenceAssets([]);
@@ -1162,6 +1167,22 @@ export function ImageCanvas() {
setConversationWindowStart(null);
}, [conversationMessages.length]);
const requestOlderMessages = useCallback(() => {
const element = conversationScrollRef.current;
if (!element || olderMessagesRequestRef.current || !conversation?.messagePage?.hasOlder) return;
const previousHeight = element.scrollHeight;
olderMessagesRequestRef.current = true;
conversationShouldStickRef.current = false;
void loadOlderMessages().finally(() => {
olderMessagesRequestRef.current = false;
window.requestAnimationFrame(() => {
const nextElement = conversationScrollRef.current;
if (!nextElement) return;
nextElement.scrollTop += nextElement.scrollHeight - previousHeight;
});
});
}, [conversation?.messagePage?.hasOlder, loadOlderMessages]);
const handleConversationScroll = useCallback(() => {
const element = conversationScrollRef.current;
if (!element) return;
@@ -1177,11 +1198,13 @@ export function ImageCanvas() {
if (!nextElement) return;
nextElement.scrollTop += nextElement.scrollHeight - previousHeight;
});
} else if (element.scrollTop < 96 && currentStart === 0) {
requestOlderMessages();
}
conversationShouldStickRef.current = element.scrollHeight
- element.scrollTop
- element.clientHeight <= CANVAS_BOTTOM_THRESHOLD_PX;
}, [conversationMessages.length]);
}, [conversationMessages.length, requestOlderMessages]);
const repriceQuote = async (
requestedQuoteId: string,
@@ -1787,9 +1810,30 @@ export function ImageCanvas() {
setConversationWindowStart(0);
}}
>
加载更早消息({effectiveConversationWindowStart})
显示已加载的更早消息({effectiveConversationWindowStart})
</button>
) : null}
{effectiveConversationWindowStart === 0 && loadingOlderMessages ? (
<div
data-testid="image-workspace-loading-older-messages"
className="mx-auto flex items-center gap-2 py-1 text-xs text-muted-foreground"
>
<Loader2 className="h-3.5 w-3.5 animate-spin" />
正在读取更早消息…
</div>
) : null}
{effectiveConversationWindowStart === 0
&& !loadingOlderMessages
&& olderMessagesError
&& conversation?.messagePage?.hasOlder ? (
<button
type="button"
className="mx-auto block rounded-md border border-border/70 px-3 py-1.5 text-xs text-muted-foreground hover:bg-surface-subtle hover:text-foreground"
onClick={requestOlderMessages}
>
重新加载更早消息
</button>
) : null}
{windowedConversationMessages.map((message) => {
const assistant = message.role === 'assistant';
return (