import { useState } from "react"; import { computeStableMessagesTimelineRows, handoffRunsSignature, type MessagesTimelineRowsInput, sameRowsInput, deriveMessagesTimelineRowsWithState, type StableMessagesTimelineRowsState, type MessagesTimelineRow, } from "./MessagesTimeline.logic"; import { type HandoffTimelineRun } from "./V2LifecycleRow"; const EMPTY_TIMELINE_RUNS: ReadonlyArray = []; export function useStableHandoffRuns( runs: ReadonlyArray, ): ReadonlyArray { const [state, setState] = useState<{ readonly source: ReadonlyArray | null; readonly signature: string; readonly value: ReadonlyArray; }>({ source: null, signature: "", value: EMPTY_TIMELINE_RUNS }); if (state.source === runs) return state.value; const signature = handoffRunsSignature(runs); const value = signature === state.signature ? state.value : runs.map((run) => ({ id: run.id, ordinal: run.ordinal, providerInstanceId: run.providerInstanceId, modelSelection: run.modelSelection, })); setState({ source: runs, signature, value }); return value; } /** * Rows derived from the previous projection of the same list, so a streamed * text update patches its message rows instead of deriving every row again. */ export function useIncrementalTimelineRows( identity: string, input: MessagesTimelineRowsInput, ): MessagesTimelineRow[] { const [state, setState] = useState(() => ({ identity, input, projection: deriveMessagesTimelineRowsWithState(input), })); if (state.identity === identity && sameRowsInput(state.input, input)) { return state.projection.rows; } const projection = deriveMessagesTimelineRowsWithState( input, state.identity === identity ? state.projection : null, ); setState({ identity, input, projection }); return projection.rows; } export function useStableRows( rows: MessagesTimelineRow[], identity: string, ): MessagesTimelineRow[] { const [state, setState] = useState<{ readonly identity: string; readonly source: MessagesTimelineRow[] | null; readonly stable: StableMessagesTimelineRowsState; }>({ identity, source: null, stable: { byId: new Map(), result: [] } }); if (state.identity === identity && state.source === rows) return state.stable.result; const stable = computeStableMessagesTimelineRows( rows, state.identity === identity ? state.stable : { byId: new Map(), result: [] }, ); setState({ identity, source: rows, stable }); return stable.result; }