Open History to see the last commit
1import { useState } from "react";
2import {
3 computeStableMessagesTimelineRows,
4 handoffRunsSignature,
5 type MessagesTimelineRowsInput,
6 sameRowsInput,
7 deriveMessagesTimelineRowsWithState,
8 type StableMessagesTimelineRowsState,
9 type MessagesTimelineRow,
10} from "./MessagesTimeline.logic";
11import { type HandoffTimelineRun } from "./V2LifecycleRow";
12
13const EMPTY_TIMELINE_RUNS: ReadonlyArray<HandoffTimelineRun> = [];
14
15export function useStableHandoffRuns(
16 runs: ReadonlyArray<HandoffTimelineRun>,
17): ReadonlyArray<HandoffTimelineRun> {
18 const [state, setState] = useState<{
19 readonly source: ReadonlyArray<HandoffTimelineRun> | null;
20 readonly signature: string;
21 readonly value: ReadonlyArray<HandoffTimelineRun>;
22 }>({ source: null, signature: "", value: EMPTY_TIMELINE_RUNS });
23 if (state.source === runs) return state.value;
24 const signature = handoffRunsSignature(runs);
25 const value =
26 signature === state.signature
27 ? state.value
28 : runs.map((run) => ({
29 id: run.id,
30 ordinal: run.ordinal,
31 providerInstanceId: run.providerInstanceId,
32 modelSelection: run.modelSelection,
33 }));
34 setState({ source: runs, signature, value });
35 return value;
36}
37
38/**
39 * Rows derived from the previous projection of the same list, so a streamed
40 * text update patches its message rows instead of deriving every row again.
41 */
42export function useIncrementalTimelineRows(
43 identity: string,
44 input: MessagesTimelineRowsInput,
45): MessagesTimelineRow[] {
46 const [state, setState] = useState(() => ({
47 identity,
48 input,
49 projection: deriveMessagesTimelineRowsWithState(input),
50 }));
51 if (state.identity === identity && sameRowsInput(state.input, input)) {
52 return state.projection.rows;
53 }
54 const projection = deriveMessagesTimelineRowsWithState(
55 input,
56 state.identity === identity ? state.projection : null,
57 );
58 setState({ identity, input, projection });
59 return projection.rows;
60}
61
62export function useStableRows(
63 rows: MessagesTimelineRow[],
64 identity: string,
65): MessagesTimelineRow[] {
66 const [state, setState] = useState<{
67 readonly identity: string;
68 readonly source: MessagesTimelineRow[] | null;
69 readonly stable: StableMessagesTimelineRowsState;
70 }>({ identity, source: null, stable: { byId: new Map(), result: [] } });
71 if (state.identity === identity && state.source === rows) return state.stable.result;
72 const stable = computeStableMessagesTimelineRows(
73 rows,
74 state.identity === identity ? state.stable : { byId: new Map(), result: [] },
75 );
76 setState({ identity, source: rows, stable });
77 return stable.result;
78}