Keep older chat history and image previews stable (#2490)
* fix(app): keep timeline history and previews stable Treat persisted timeline replicas as display-only so authoritative pagination always comes from the daemon. Require renewed user intent between older-history pages unless the viewport genuinely remains at the history edge. Model assistant image acquisition as loading, loaded, or failed so recreating a preview URL cannot be rendered as an error. * fix(app): keep file image previews current * fix(app): stabilize history pagination edges * fix(app): recover transient timeline loads * fix(app): retain assistant image previews * Stabilize history pagination lifecycle * Stabilize history settlement and image previews * Retry file images after reconnect * Keep active previews and pagination requests alive * Replace image hook tests with typed ports * Integrate timeline hydration with submission authority * Harden pre-hydration timeline reconciliation * Make rewind E2E use real scroll intent * Preserve live timeline state through hydration * Keep mounted image attachments retained * Protect preview persistence and lifecycle hydration * Preserve idless live assistant continuations * Preserve timeline rows across delayed hydration * test(app): cover real assistant image files * Preserve assistant tool ordering during hydration
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
export interface AssistantImageDimensions {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
interface AssistantImageDimensionCandidate {
|
||||
width?: unknown;
|
||||
height?: unknown;
|
||||
}
|
||||
|
||||
export interface AssistantImageRenderedDimensionsReader {
|
||||
read(renderedImage: unknown): AssistantImageDimensions | null;
|
||||
}
|
||||
|
||||
function readPositiveDimensions(
|
||||
candidate: AssistantImageDimensionCandidate | null | undefined,
|
||||
): AssistantImageDimensions | null {
|
||||
if (
|
||||
typeof candidate?.width !== "number" ||
|
||||
typeof candidate.height !== "number" ||
|
||||
candidate.width <= 0 ||
|
||||
candidate.height <= 0
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return { width: candidate.width, height: candidate.height };
|
||||
}
|
||||
|
||||
export function resolveAssistantImageLoadDimensions(input: {
|
||||
source?: AssistantImageDimensionCandidate | null;
|
||||
target?: { naturalWidth?: unknown; naturalHeight?: unknown } | null;
|
||||
renderedImage: unknown;
|
||||
renderedDimensions: AssistantImageRenderedDimensionsReader;
|
||||
}): AssistantImageDimensions | null {
|
||||
const source = readPositiveDimensions(input.source);
|
||||
if (source) {
|
||||
return source;
|
||||
}
|
||||
|
||||
const target = readPositiveDimensions({
|
||||
width: input.target?.naturalWidth,
|
||||
height: input.target?.naturalHeight,
|
||||
});
|
||||
return target ?? input.renderedDimensions.read(input.renderedImage);
|
||||
}
|
||||
Reference in New Issue
Block a user