App 聊天历史分页改走连拉循环
首屏与「显示更早」共用 readDirectHistoryPages,不再各自单发一页 首屏基线取空并在守卫后一次性并入聊天 reducer,失败仍按原有语义抛出 「显示更早」按当前视图条目判断新回合,取到多页一次性并入并推进锚点 连拉中途失败保留已取到的页,报错统一走同一个出口
This commit is contained in:
@@ -232,17 +232,19 @@ import {
|
||||
} from './features/project-workspace/chatComposerQueue';
|
||||
import { DeveloperProjectPanels } from './features/project-workspace/DeveloperProjectPanels';
|
||||
import { DeveloperRuntimePanels } from './features/project-workspace/DeveloperRuntimePanels';
|
||||
import { readDirectHistoryPages } from './features/project-workspace/directHistoryPaging';
|
||||
import {
|
||||
applyDirectThreadConsumeResult,
|
||||
type DirectThreadChatState,
|
||||
emptyDirectThreadChatState,
|
||||
mergeDirectThreadHistorySlice,
|
||||
mergeDirectHistoryItems,
|
||||
resolveDirectThreadBootstrap,
|
||||
selectDirectChatEntries,
|
||||
} from './features/project-workspace/directThreadChat';
|
||||
import {
|
||||
type DirectThreadConsumeResult,
|
||||
type DirectThreadHistorySlice,
|
||||
type DirectThreadItem,
|
||||
type DirectThreadSubscriptionBootstrap,
|
||||
} from './features/project-workspace/directThreadEvents';
|
||||
import type { DirectCodexUserContentPart } from './features/project-workspace/generated';
|
||||
@@ -3491,21 +3493,38 @@ export function App({
|
||||
let runtimeError = '';
|
||||
let loadedDirectHistoryHasMore = false;
|
||||
let loadedDirectHistoryFirstItemId: string | null = null;
|
||||
// 首屏切片先暂存,待下方 staleness 守卫通过后再并入聊天 reducer:
|
||||
// 首屏连拉到的条目先暂存,待下方 staleness 守卫通过后再并入聊天 reducer:
|
||||
// 迟到的切片属于已经切走的项目,不能在守卫之前就写进全局聊天状态。
|
||||
let loadedDirectHistorySlice: DirectThreadHistorySlice | null = null;
|
||||
let loadedDirectHistoryItems: DirectThreadItem[] = [];
|
||||
const projectConversation = directCodexProductRuntime
|
||||
? (() => {
|
||||
return invoke<DirectThreadHistorySlice>(
|
||||
'read_direct_project_history_slice',
|
||||
{
|
||||
projectPath: nextProjectPath,
|
||||
limit: CONVERSATION_INITIAL_VISIBLE_COUNT,
|
||||
},
|
||||
).then((slice) => {
|
||||
loadedDirectHistoryHasMore = slice.hasMore;
|
||||
loadedDirectHistoryFirstItemId = slice.firstItemId;
|
||||
loadedDirectHistorySlice = slice;
|
||||
// 首屏铺的就是这份视图的基线,"新回合"比较没有意义:判据退化为"这一页得有能渲染
|
||||
// 的条目",所以基线取空,免得拿上一份对话的残留状态去比。首屏失败仍按原有语义
|
||||
// 抛出,交给调用方的 catch 处理。
|
||||
return readDirectHistoryPages({
|
||||
existingEntries: [],
|
||||
beforeItemId: null,
|
||||
readSlice: (beforeItemId) =>
|
||||
invoke<DirectThreadHistorySlice>(
|
||||
'read_direct_project_history_slice',
|
||||
beforeItemId
|
||||
? {
|
||||
projectPath: nextProjectPath,
|
||||
beforeItemId,
|
||||
limit: CONVERSATION_INITIAL_VISIBLE_COUNT,
|
||||
}
|
||||
: {
|
||||
projectPath: nextProjectPath,
|
||||
limit: CONVERSATION_INITIAL_VISIBLE_COUNT,
|
||||
},
|
||||
),
|
||||
}).then((pages) => {
|
||||
if (pages.error) {
|
||||
throw pages.error;
|
||||
}
|
||||
loadedDirectHistoryHasMore = pages.hasMore;
|
||||
loadedDirectHistoryFirstItemId = pages.firstItemId;
|
||||
loadedDirectHistoryItems = pages.items;
|
||||
return {
|
||||
path: nextProjectPath,
|
||||
agentId: null,
|
||||
@@ -3579,12 +3598,10 @@ export function App({
|
||||
if (directCodexProductRuntime) {
|
||||
setDirectHistoryHasMore(loadedDirectHistoryHasMore);
|
||||
directHistoryOldestItemIdRef.current = loadedDirectHistoryFirstItemId;
|
||||
if (loadedDirectHistorySlice) {
|
||||
const slice = loadedDirectHistorySlice;
|
||||
if (loadedDirectHistoryItems.length > 0) {
|
||||
const items = loadedDirectHistoryItems;
|
||||
// 首屏历史进聊天 reducer;`messages` 在 DirectProject 下只剩运行期本地消息。
|
||||
setDirectThreadChat((state) =>
|
||||
mergeDirectThreadHistorySlice(state, slice),
|
||||
);
|
||||
setDirectThreadChat((state) => mergeDirectHistoryItems(state, items));
|
||||
}
|
||||
}
|
||||
setMessages((current) => {
|
||||
@@ -11807,24 +11824,37 @@ export function App({
|
||||
if (invoke && projectPath && !directHistoryLoadingRef.current) {
|
||||
directHistoryLoadingRef.current = true;
|
||||
try {
|
||||
const slice = await invoke<DirectThreadHistorySlice>(
|
||||
'read_direct_project_history_slice',
|
||||
{
|
||||
projectPath,
|
||||
beforeItemId: directHistoryOldestItemIdRef.current,
|
||||
limit: CONVERSATION_VISIBLE_STEP,
|
||||
},
|
||||
);
|
||||
// 一次点击连拉,直到出现新回合或取不动为止:一屏全是工具卡片 / 思考文本时,
|
||||
// 单发一页会让用户点了"显示更早"却看不到任何变化。
|
||||
const pages = await readDirectHistoryPages({
|
||||
existingEntries: selectDirectChatEntries(directThreadChat),
|
||||
beforeItemId: directHistoryOldestItemIdRef.current,
|
||||
readSlice: (beforeItemId) =>
|
||||
invoke<DirectThreadHistorySlice>(
|
||||
'read_direct_project_history_slice',
|
||||
beforeItemId
|
||||
? {
|
||||
projectPath,
|
||||
beforeItemId,
|
||||
limit: CONVERSATION_VISIBLE_STEP,
|
||||
}
|
||||
: { projectPath, limit: CONVERSATION_VISIBLE_STEP },
|
||||
),
|
||||
});
|
||||
if (localProjectPathRef.current !== projectPath) {
|
||||
return;
|
||||
}
|
||||
// 更早的一屏历史进聊天 reducer:与运行态同形、同身份,重复读取只补不重。
|
||||
// 连拉到的页一次性进聊天 reducer:与运行态同形、同身份,重复读取只补不重。
|
||||
setDirectThreadChat((state) =>
|
||||
mergeDirectThreadHistorySlice(state, slice),
|
||||
mergeDirectHistoryItems(state, pages.items),
|
||||
);
|
||||
setDirectHistoryHasMore(slice.hasMore);
|
||||
setDirectHistoryHasMore(pages.hasMore);
|
||||
directHistoryOldestItemIdRef.current =
|
||||
slice.firstItemId ?? directHistoryOldestItemIdRef.current;
|
||||
pages.firstItemId ?? directHistoryOldestItemIdRef.current;
|
||||
if (pages.error) {
|
||||
// 已经取到的页照常进视图,失败只走下面同一个报错出口。
|
||||
throw pages.error;
|
||||
}
|
||||
} catch (error) {
|
||||
setWorkspaceStatus(
|
||||
`读取更早的对话历史失败:${error instanceof Error ? error.message : String(error)}`,
|
||||
|
||||
Reference in New Issue
Block a user