fix: restore coding history and quota feedback
This commit is contained in:
@@ -1265,6 +1265,19 @@ function processItemIsFailed(item: ProcessItem): boolean {
|
||||
return item.kind === 'notice' && item.node.level === 'error';
|
||||
}
|
||||
|
||||
function persistedProviderFailureMessage(items: ProcessItem[]): string | undefined {
|
||||
for (let index = items.length - 1; index >= 0; index -= 1) {
|
||||
const item = items[index];
|
||||
if (item?.kind === 'notice'
|
||||
&& item.node.level === 'error'
|
||||
&& (item.node.code === 'CODING_PROVIDER_AUTH_REQUIRED'
|
||||
|| item.node.code === 'CODING_PROVIDER_QUOTA_EXHAUSTED')) {
|
||||
return item.node.message;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function processGroupIsFailed(
|
||||
items: ProcessItem[],
|
||||
run: ConversationRunState,
|
||||
@@ -1326,10 +1339,13 @@ const ProcessGroup = memo(function ProcessGroup({
|
||||
? runningDurationLabel(run, now)
|
||||
: settledDurationLabel(run)
|
||||
: null;
|
||||
const failureMessage = failed
|
||||
? (latest ? run.error?.message : undefined) ?? persistedProviderFailureMessage(items)
|
||||
: undefined;
|
||||
const summary = active
|
||||
? `处理中${duration ? ` ${duration}` : ''}`
|
||||
: failed
|
||||
? `处理失败${duration ? ` · ${duration}` : ''}`
|
||||
? `${failureMessage ?? '处理失败'}${duration ? ` · ${duration}` : ''}`
|
||||
: `已处理${duration ? ` ${duration}` : ''}`;
|
||||
|
||||
return (
|
||||
@@ -1491,11 +1507,33 @@ export const CodingConversationTimeline = memo(function CodingConversationTimeli
|
||||
const nodes = useCodingConversationStore(selectNodes);
|
||||
const run = useCodingConversationStore(selectRun);
|
||||
const [visibleLimit, setVisibleLimit] = useState(INITIAL_WINDOW);
|
||||
const scrollRef = useRef<HTMLDivElement | null>(null);
|
||||
const stickToBottomRef = useRef(true);
|
||||
const prependAnchorRef = useRef<{ scrollHeight: number; scrollTop: number } | null>(null);
|
||||
const windowStart = Math.max(0, nodes.length - visibleLimit);
|
||||
const visibleNodes = nodes.slice(windowStart);
|
||||
const turns = useMemo(() => timelineTurns(visibleNodes), [visibleNodes]);
|
||||
const scrollRef = useRef<HTMLDivElement | null>(null);
|
||||
const stickToBottomRef = useRef(true);
|
||||
|
||||
const loadEarlier = useCallback(() => {
|
||||
if (windowStart === 0 || prependAnchorRef.current) return;
|
||||
const element = scrollRef.current;
|
||||
if (!element) return;
|
||||
prependAnchorRef.current = {
|
||||
scrollHeight: element.scrollHeight,
|
||||
scrollTop: element.scrollTop,
|
||||
};
|
||||
stickToBottomRef.current = false;
|
||||
setVisibleLimit((current) => Math.min(nodes.length, current + WINDOW_STEP));
|
||||
}, [nodes.length, windowStart]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const anchor = prependAnchorRef.current;
|
||||
if (!anchor) return;
|
||||
prependAnchorRef.current = null;
|
||||
const element = scrollRef.current;
|
||||
if (!element) return;
|
||||
element.scrollTop = anchor.scrollTop + (element.scrollHeight - anchor.scrollHeight);
|
||||
}, [visibleLimit]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!stickToBottomRef.current) return;
|
||||
@@ -1513,6 +1551,7 @@ export const CodingConversationTimeline = memo(function CodingConversationTimeli
|
||||
onScroll={(event) => {
|
||||
const element = event.currentTarget;
|
||||
stickToBottomRef.current = element.scrollHeight - element.scrollTop - element.clientHeight < 64;
|
||||
if (element.scrollTop <= 48) loadEarlier();
|
||||
}}
|
||||
>
|
||||
<div className="mx-auto flex w-full max-w-[50rem] flex-col gap-8">
|
||||
@@ -1521,7 +1560,7 @@ export const CodingConversationTimeline = memo(function CodingConversationTimeli
|
||||
type="button"
|
||||
variant="ghost"
|
||||
className="mx-auto min-h-9 rounded-lg px-4 text-xs transition-transform duration-150 ease-out active:scale-[0.96]"
|
||||
onClick={() => setVisibleLimit((current) => current + WINDOW_STEP)}
|
||||
onClick={loadEarlier}
|
||||
>
|
||||
加载更早内容
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user