mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Fix work details stream spacing
This commit is contained in:
parent
6d00c0665d
commit
dc91a461d6
2 changed files with 55 additions and 1 deletions
|
|
@ -52,11 +52,12 @@ interface MessageVirtualizerRuntime {
|
|||
viewportRestoreFrame: number | null;
|
||||
virtualizerChangeFrame: number | null;
|
||||
settleScrollToEndFrame: number | null;
|
||||
anchorClampFrame: number | null;
|
||||
settleScrollToEndAttemptsRemaining: number;
|
||||
userScrollIntentUntil: number;
|
||||
}
|
||||
|
||||
type MessageVirtualizerFrameKey = "viewportRestoreFrame" | "virtualizerChangeFrame" | "settleScrollToEndFrame";
|
||||
type MessageVirtualizerFrameKey = "viewportRestoreFrame" | "virtualizerChangeFrame" | "settleScrollToEndFrame" | "anchorClampFrame";
|
||||
|
||||
export interface MessageStreamVirtualizerOptions {
|
||||
blocks: readonly MessageStreamBlock[];
|
||||
|
|
@ -160,6 +161,7 @@ function createMessageVirtualizerRuntime(): MessageVirtualizerRuntime {
|
|||
viewportRestoreFrame: null,
|
||||
virtualizerChangeFrame: null,
|
||||
settleScrollToEndFrame: null,
|
||||
anchorClampFrame: null,
|
||||
settleScrollToEndAttemptsRemaining: 0,
|
||||
userScrollIntentUntil: 0,
|
||||
};
|
||||
|
|
@ -384,6 +386,7 @@ function scrollMessageVirtualizerByPage(runtime: MessageVirtualizerRuntime, dire
|
|||
function disposeMessageVirtualizer(runtime: MessageVirtualizerRuntime): void {
|
||||
cancelViewportRestoreMessageVirtualizerReset(runtime);
|
||||
cancelDeferredMessageVirtualizerChange(runtime);
|
||||
cancelPreservedMessageVirtualizerEndClamp(runtime);
|
||||
cancelPendingMessageVirtualizerBottomFollow(runtime);
|
||||
runtime.cleanupVirtualizer?.();
|
||||
runtime.cleanupVirtualizer = null;
|
||||
|
|
@ -406,6 +409,7 @@ function attachMessageVirtualizer(runtime: MessageVirtualizerRuntime, container:
|
|||
function detachMessageVirtualizer(runtime: MessageVirtualizerRuntime): void {
|
||||
cancelViewportRestoreMessageVirtualizerReset(runtime);
|
||||
cancelDeferredMessageVirtualizerChange(runtime);
|
||||
cancelPreservedMessageVirtualizerEndClamp(runtime);
|
||||
cancelPendingMessageVirtualizerBottomFollow(runtime);
|
||||
runtime.cleanupVirtualizer?.();
|
||||
runtime.cleanupVirtualizer = null;
|
||||
|
|
@ -419,6 +423,7 @@ function detachMessageVirtualizer(runtime: MessageVirtualizerRuntime): void {
|
|||
function resetMessageVirtualizer(runtime: MessageVirtualizerRuntime, container: HTMLElement): void {
|
||||
cancelViewportRestoreMessageVirtualizerReset(runtime);
|
||||
cancelDeferredMessageVirtualizerChange(runtime);
|
||||
cancelPreservedMessageVirtualizerEndClamp(runtime);
|
||||
cancelSettledMessageVirtualizerScrollToEnd(runtime);
|
||||
runtime.cleanupVirtualizer?.();
|
||||
runtime.cleanupVirtualizer = null;
|
||||
|
|
@ -656,6 +661,31 @@ function cancelSettledMessageVirtualizerScrollToEnd(runtime: MessageVirtualizerR
|
|||
runtime.settleScrollToEndAttemptsRemaining = 0;
|
||||
}
|
||||
|
||||
function schedulePreservedMessageVirtualizerEndClamp(runtime: MessageVirtualizerRuntime): void {
|
||||
const container = runtime.container;
|
||||
if (!container || runtime.anchorClampFrame !== null) return;
|
||||
scheduleMessageVirtualizerFrame(runtime, "anchorClampFrame", container, () => {
|
||||
if (runtime.container !== container || !isMessageVirtualizerPreservingReadingAnchor(runtime)) return;
|
||||
const scrollEnd = Math.max(0, runtime.virtualizer.getTotalSize() - container.clientHeight);
|
||||
if (Math.abs(container.scrollTop - scrollEnd) <= MESSAGE_BOTTOM_THRESHOLD) {
|
||||
updateMessageVirtualizer(runtime.virtualizer);
|
||||
markMessageVirtualizerFollowEndIntent(runtime);
|
||||
notifyMessageVirtualizerChange(runtime);
|
||||
return;
|
||||
}
|
||||
if (container.scrollTop < scrollEnd) return;
|
||||
runtime.virtualizer.scrollToOffset(scrollEnd);
|
||||
syncMessageVirtualizerScrollOffset(runtime);
|
||||
updateMessageVirtualizer(runtime.virtualizer);
|
||||
markMessageVirtualizerFollowEndIntent(runtime);
|
||||
notifyMessageVirtualizerChange(runtime);
|
||||
});
|
||||
}
|
||||
|
||||
function cancelPreservedMessageVirtualizerEndClamp(runtime: MessageVirtualizerRuntime): void {
|
||||
cancelMessageVirtualizerFrame(runtime, "anchorClampFrame");
|
||||
}
|
||||
|
||||
function scheduleMessageVirtualizerFrame(
|
||||
runtime: MessageVirtualizerRuntime,
|
||||
key: MessageVirtualizerFrameKey,
|
||||
|
|
@ -743,6 +773,9 @@ function measureMessageElement(
|
|||
): number {
|
||||
const box = entry?.borderBoxSize[0];
|
||||
const size = box ? Math.round(box.blockSize) : element.offsetHeight || instance.options.estimateSize(instance.indexFromElement(element));
|
||||
if (entry && isMessageVirtualizerPreservingReadingAnchor(runtime)) {
|
||||
schedulePreservedMessageVirtualizerEndClamp(runtime);
|
||||
}
|
||||
if (entry && hasMessageVirtualizerFollowEndIntent(runtime)) {
|
||||
// ResizeObserver measurements can arrive while TanStack and DOM heights are still converging, so keep the bounded settle.
|
||||
scheduleMessageVirtualizerBottomReconcileAfterCommit(runtime);
|
||||
|
|
|
|||
|
|
@ -463,6 +463,27 @@ describe("TestMessageStreamVirtualizer", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("clamps stale unpinned offsets when a visible item shrinks near the end", () => {
|
||||
withResizeObserverEntries((resizeElement, flushFrames) => {
|
||||
const container = messageContainer({ scrollTop: 0, clientHeight: 100 });
|
||||
const controller = createMessageStreamVirtualizerDriver(container);
|
||||
const details = measuredElement("details", 1, 500);
|
||||
|
||||
renderVirtualItems(controller, container, ["first", "details", "last"], [300, 500, 270], "force-bottom");
|
||||
controller.measureElement(details);
|
||||
userScrollTo(container, 720);
|
||||
|
||||
container.dataset["testTotalSize"] = "590";
|
||||
container.dataset["testScrollHeight"] = "1070";
|
||||
resizeElement(details, 20);
|
||||
flushFrames();
|
||||
|
||||
expect(controller.getTotalSize()).toBe(590);
|
||||
expect(container.scrollTop).toBe(490);
|
||||
controller.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps the reading position when the message viewport shrinks away from the end", () => {
|
||||
withResizeObserver((triggerResize) => {
|
||||
const container = messageContainer({ scrollTop: 0, clientHeight: 160 });
|
||||
|
|
|
|||
Loading…
Reference in a new issue