Stabilize web timeline height estimates
This commit is contained in:
@@ -54,6 +54,16 @@ function toolCall(id: string, seed: number): StreamItem {
|
||||
};
|
||||
}
|
||||
|
||||
function thought(id: string, seed: number): StreamItem {
|
||||
return {
|
||||
kind: "thought",
|
||||
id,
|
||||
text: id,
|
||||
status: "ready",
|
||||
timestamp: createTimestamp(seed),
|
||||
};
|
||||
}
|
||||
|
||||
function indexEntries(items: StreamItem[]): IndexedStreamItem[] {
|
||||
return items.map((item, index) => ({ item, index }));
|
||||
}
|
||||
@@ -111,6 +121,11 @@ describe("splitWebVirtualizedHistory", () => {
|
||||
});
|
||||
|
||||
describe("estimateStreamItemHeight", () => {
|
||||
it("uses compact estimates for collapsed tool sequence rows", () => {
|
||||
expect(estimateStreamItemHeight(toolCall("tool", 1))).toBe(40);
|
||||
expect(estimateStreamItemHeight(thought("thought", 2))).toBe(40);
|
||||
});
|
||||
|
||||
it("uses a larger estimate for user messages with image attachments", () => {
|
||||
const item: StreamItem = {
|
||||
kind: "user_message",
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { StreamItem } from "@/types/stream";
|
||||
import { estimateAssistantMessageHeightFromCache } from "@/utils/assistant-image-metadata";
|
||||
import { estimateAssistantMessageHeightFromCache } from "@/utils/assistant-message-height-estimate";
|
||||
|
||||
export const DEFAULT_WEB_PARTIAL_VIRTUALIZATION_THRESHOLD = 100;
|
||||
export const DEFAULT_WEB_MOUNTED_RECENT_STREAM_ITEMS = 50;
|
||||
const COLLAPSED_TOOL_SEQUENCE_ROW_HEIGHT_ESTIMATE = 40;
|
||||
|
||||
type BottomAnchorE2ETestGlobals = typeof globalThis & {
|
||||
__PASEO_E2E_WEB_PARTIAL_VIRTUALIZATION_THRESHOLD?: unknown;
|
||||
@@ -48,9 +49,9 @@ export function estimateStreamItemHeight(item: StreamItem): number {
|
||||
case "assistant_message":
|
||||
return estimateAssistantMessageHeightFromCache(item.text) ?? 220;
|
||||
case "tool_call":
|
||||
return 136;
|
||||
return COLLAPSED_TOOL_SEQUENCE_ROW_HEIGHT_ESTIMATE;
|
||||
case "thought":
|
||||
return 112;
|
||||
return COLLAPSED_TOOL_SEQUENCE_ROW_HEIGHT_ESTIMATE;
|
||||
case "todo_list":
|
||||
return 144;
|
||||
case "activity_log":
|
||||
|
||||
@@ -29,8 +29,6 @@ import Markdown, {
|
||||
type ASTNode,
|
||||
type RenderRules,
|
||||
} from "react-native-markdown-display";
|
||||
|
||||
type MarkdownStyles = Record<string, TextStyle & ViewStyle & { [key: string]: unknown }>;
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import MaskedView from "@react-native-masked-view/masked-view";
|
||||
import {
|
||||
@@ -83,13 +81,13 @@ import {
|
||||
getAssistantImageMetadata,
|
||||
setAssistantImageMetadata,
|
||||
} from "@/utils/assistant-image-metadata";
|
||||
import { setAssistantMarkdownBlockHeight } from "@/utils/assistant-message-height-estimate";
|
||||
import { resolveAssistantImageSource } from "@/utils/assistant-image-source";
|
||||
import {
|
||||
createPreviewAttachmentId,
|
||||
getFileNameFromPath,
|
||||
parseImageDataUrl,
|
||||
} from "@/attachments/utils";
|
||||
export type { InlinePathTarget } from "@/utils/inline-path";
|
||||
import { PlanCard } from "./plan-card";
|
||||
import { useToolCallSheet } from "./tool-call-sheet";
|
||||
import { ToolCallDetailsContent } from "./tool-call-details";
|
||||
@@ -97,6 +95,9 @@ import { useAttachmentPreviewUrl } from "@/attachments/use-attachment-preview-ur
|
||||
import { persistAttachmentFromBase64, persistAttachmentFromDataUrl } from "@/attachments/service";
|
||||
import type { DaemonClient } from "@server/client/daemon-client";
|
||||
import { isWeb, isNative } from "@/constants/platform";
|
||||
export type { InlinePathTarget } from "@/utils/inline-path";
|
||||
|
||||
type MarkdownStyles = Record<string, TextStyle & ViewStyle & { [key: string]: unknown }>;
|
||||
|
||||
interface UserMessageProps {
|
||||
message: string;
|
||||
@@ -1186,16 +1187,29 @@ function NativeShimmerPeakSvg({ gradientId }: { gradientId: string }) {
|
||||
}
|
||||
|
||||
interface AssistantMessageBlockContainerProps {
|
||||
block: string;
|
||||
marginBottom: number;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
function AssistantMessageBlockContainer({
|
||||
block,
|
||||
marginBottom,
|
||||
children,
|
||||
}: AssistantMessageBlockContainerProps) {
|
||||
const style = useMemo(() => (marginBottom > 0 ? { marginBottom } : undefined), [marginBottom]);
|
||||
return <View style={style}>{children}</View>;
|
||||
const handleLayout = useCallback(
|
||||
(event: LayoutChangeEvent) => {
|
||||
const { width, height } = event.nativeEvent.layout;
|
||||
setAssistantMarkdownBlockHeight({ block, width, height });
|
||||
},
|
||||
[block],
|
||||
);
|
||||
return (
|
||||
<View style={style} onLayout={isWeb ? handleLayout : undefined}>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
interface MemoizedMarkdownBlockProps {
|
||||
@@ -1572,6 +1586,7 @@ export const AssistantMessage = memo(function AssistantMessage({
|
||||
{keyedBlocks.map(({ key, block }, index) => (
|
||||
<AssistantMessageBlockContainer
|
||||
key={key}
|
||||
block={block}
|
||||
marginBottom={index < keyedBlocks.length - 1 ? theme.spacing[3] : 0}
|
||||
>
|
||||
<MemoizedMarkdownBlock
|
||||
|
||||
Reference in New Issue
Block a user