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