fix(app): reject stale timeline range indexes (#33488)
This commit is contained in:
parent
3f3f120825
commit
f0849a697c
4 changed files with 35 additions and 4 deletions
|
|
@ -73,6 +73,7 @@ import { makeTimer } from "@solid-primitives/timer"
|
||||||
import { scheduleConnectedMeasure } from "./measure"
|
import { scheduleConnectedMeasure } from "./measure"
|
||||||
import { createTimelineProjection } from "./projection"
|
import { createTimelineProjection } from "./projection"
|
||||||
import { MessageComment, SummaryDiff, TimelineRow, TimelineRowMap } from "./rows"
|
import { MessageComment, SummaryDiff, TimelineRow, TimelineRowMap } from "./rows"
|
||||||
|
import { filterVirtualIndexes } from "./virtual-items"
|
||||||
|
|
||||||
const emptyMessages: MessageType[] = []
|
const emptyMessages: MessageType[] = []
|
||||||
const emptyParts: PartType[] = []
|
const emptyParts: PartType[] = []
|
||||||
|
|
@ -452,7 +453,10 @@ export function MessageTimeline(props: {
|
||||||
const id = activeMessageID()
|
const id = activeMessageID()
|
||||||
const active = id ? (messageLastRowIndex().get(id) ?? -1) : -1
|
const active = id ? (messageLastRowIndex().get(id) ?? -1) : -1
|
||||||
const indexes = defaultRangeExtractor({ ...range, overscan: renderOverscan() })
|
const indexes = defaultRangeExtractor({ ...range, overscan: renderOverscan() })
|
||||||
return [...new Set([...resizePinnedIndexes, ...indexes, ...(active < 0 ? [] : [active])])].sort((a, b) => a - b)
|
return filterVirtualIndexes(
|
||||||
|
[...new Set([...resizePinnedIndexes, ...indexes, ...(active < 0 ? [] : [active])])].sort((a, b) => a - b),
|
||||||
|
range.count,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const resizeItem = virtualizer.resizeItem
|
const resizeItem = virtualizer.resizeItem
|
||||||
|
|
|
||||||
3
packages/app/src/pages/session/timeline/virtual-items.ts
Normal file
3
packages/app/src/pages/session/timeline/virtual-items.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function filterVirtualIndexes(indexes: number[], count: number) {
|
||||||
|
return indexes.filter((index) => index >= 0 && index < count)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { expect, test } from "bun:test"
|
import { expect, test } from "bun:test"
|
||||||
import { createVirtualizer } from "@tanstack/solid-virtual"
|
import { createVirtualizer, defaultRangeExtractor } from "@tanstack/solid-virtual"
|
||||||
import { createRoot, createSignal } from "solid-js"
|
import { createRoot, createSignal } from "solid-js"
|
||||||
|
import { filterVirtualIndexes } from "@/pages/session/timeline/virtual-items"
|
||||||
|
|
||||||
test("reactive count updates preserve measured row sizes", () => {
|
test("reactive count updates preserve measured row sizes", () => {
|
||||||
createRoot((dispose) => {
|
createRoot((dispose) => {
|
||||||
|
|
@ -44,3 +45,26 @@ test("logical scroll offset includes pending measurement adjustments", () => {
|
||||||
dispose()
|
dispose()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("stale pinned indexes do not produce missing virtual items after count shrinks", () => {
|
||||||
|
createRoot((dispose) => {
|
||||||
|
const [count, setCount] = createSignal(2)
|
||||||
|
const pinned = [1]
|
||||||
|
const virtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({
|
||||||
|
get count() {
|
||||||
|
return count()
|
||||||
|
},
|
||||||
|
getScrollElement: () => null,
|
||||||
|
estimateSize: () => 60,
|
||||||
|
initialRect: { width: 800, height: 600 },
|
||||||
|
rangeExtractor: (range) =>
|
||||||
|
filterVirtualIndexes([...new Set([...defaultRangeExtractor(range), ...pinned])], range.count),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(virtualizer.getVirtualItems().map((item) => item.index)).toEqual([0, 1])
|
||||||
|
setCount(1)
|
||||||
|
expect(virtualizer.getVirtualItems().map((item) => item.index)).toEqual([0])
|
||||||
|
expect(() => new Map(virtualizer.getVirtualItems().map((item) => [item.key, item]))).not.toThrow()
|
||||||
|
dispose()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
||||||
|
|
@ -1242,7 +1242,7 @@ it.instance(
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
3_000,
|
10_000,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Queue semantics
|
// Queue semantics
|
||||||
|
|
@ -1669,7 +1669,7 @@ it.instance(
|
||||||
expect(yield* llm.calls).toBe(1)
|
expect(yield* llm.calls).toBe(1)
|
||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
3_000,
|
10_000,
|
||||||
)
|
)
|
||||||
|
|
||||||
unix(
|
unix(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue