返回 DeepSeek-Reasonix
historyWindowState.ts
根目录 / desktop / frontend / src / lib / historyWindowState.ts
1 import { compactArchivedToolItems } from "./archivedToolItems";
2 import { duplicateLiveItemIds } from "./hydrateHistoryApply";
3 import { historyRevisionIsOlder } from "./sessionTranscriptMode";
4 import type { HistoryMutation, Item } from "./useController";
5 import { canonicalUserConfirmations, settleLocalSubmissions, type LocalSubmissionFields } from "./localSubmissionState";
6 import { reconcileSessionOperationItems } from "./sessionMaintenanceOperation";
7
8 type WindowFields = LocalSubmissionFields & {
9 items: Item[];
10 historyPrefixCount: number;
11 hydrateHistoryLoaded?: boolean;
12 hydratePlaceholderItems?: Item[];
13 historyStartTurn: number;
14 historyEndTurn: number;
15 historyTotalTurns: number;
16 historyHasOlder: boolean;
17 historyHasNewer: boolean;
18 historyOlderLoading: boolean;
19 historyOlderError?: string;
20 historyNewerLoading: boolean;
21 historyNewerError?: string;
22 historyRevision?: number;
23 historyDigest?: string;
24 historyLayoutRevision: number;
25 historyMutation: HistoryMutation;
26 pendingSubmissionId?: string;
27 };
28
29 export type HistoryWindowMutationAction = {
30 type: "history_replace" | "history_rebase" | "history_prepend" | "history_append";
31 items: Item[];
32 removeIds?: string[];
33 startTurn: number;
34 endTurn?: number;
35 totalTurns: number;
36 hasOlder: boolean;
37 hasNewer?: boolean;
38 revision?: number;
39 digest?: string;
40 };
41
42 function common<S extends WindowFields>(state: S, action: HistoryWindowMutationAction, items: Item[], prefix: number, kind: "replace" | "prepend" | "append"): S {
43 return {
44 ...state,
45 items: compactArchivedToolItems(items),
46 historyPrefixCount: prefix,
47 hydrateHistoryLoaded: true,
48 hydratePlaceholderItems: undefined,
49 historyStartTurn: action.startTurn,
50 historyEndTurn: action.endTurn ?? (kind === "prepend" ? state.historyEndTurn : action.totalTurns),
51 historyTotalTurns: action.totalTurns,
52 historyHasOlder: action.hasOlder,
53 historyHasNewer: Boolean(action.hasNewer),
54 historyOlderLoading: false,
55 historyOlderError: undefined,
56 historyNewerLoading: false,
57 historyNewerError: undefined,
58 historyRevision: action.revision,
59 historyDigest: action.digest,
60 historyMutation: { seq: state.historyMutation.seq + 1, kind },
61 };
62 }
63
64 export function reduceHistoryWindowState<S extends WindowFields>(state: S, action: HistoryWindowMutationAction): S {
65 if (historyRevisionIsOlder(state.historyRevision, action.revision)) return state;
66 if (action.type === "history_replace") {
67 const items = reconcileSessionOperationItems(action.items, state.items);
68 return settleLocalSubmissions(common(state, action, items, items.length, "replace"), items);
69 }
70 if (action.type === "history_rebase") {
71 const liveTail = state.items.slice(Math.min(state.historyPrefixCount, state.items.length));
72 const duplicates = new Set(duplicateLiveItemIds(action.items, liveTail));
73 const items = reconcileSessionOperationItems([...action.items, ...liveTail.filter((item) => !duplicates.has(item.id))], state.items);
74 return settleLocalSubmissions({ ...common(state, action, items, action.items.length, "replace"), historyLayoutRevision: state.historyLayoutRevision + 1 }, items, canonicalUserConfirmations(action.items));
75 }
76 if (action.type === "history_prepend") {
77 const remove = action.removeIds?.length ? new Set(action.removeIds) : undefined;
78 const rest = remove ? state.items.filter((item) => !remove.has(item.id)) : state.items;
79 const prefix = state.items.slice(0, Math.min(state.historyPrefixCount, state.items.length));
80 const retainedPrefix = remove ? prefix.filter((item) => !remove.has(item.id)) : prefix;
81 const incoming = new Set(action.items.map(item => item.id));
82 const items = reconcileSessionOperationItems([...new Map(action.items.map(item => [item.id, item])).values(), ...rest.filter(item => !incoming.has(item.id))], state.items);
83 return settleLocalSubmissions(common(state, action, items, action.items.length + retainedPrefix.filter(item => !incoming.has(item.id)).length, "prepend"), items, canonicalUserConfirmations(action.items));
84 }
85 const items = reconcileSessionOperationItems(action.items, state.items);
86 return settleLocalSubmissions({
87 ...common(state, action, items, items.length, "append"),
88 historyLayoutRevision: state.historyLayoutRevision + 1,
89 }, items);
90 }
91
91 lines TYPESCRIPT