Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/web-ui/src/flow_chat/components/CodePreview.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,25 @@ describe('CodePreview', () => {
expect(highlighter.textContent).not.toContain('line 090');
expect(Number(highlighter.dataset.startingLineNumber)).toBeLessThanOrEqual(104);
});

it('fits the streaming tail in the viewport when nested autoscroll is disabled', async () => {
await act(async () => {
root.render(
<CodePreview
content={makeLines(120)}
filePath="src/generated.ts"
isStreaming={true}
maxHeight={88}
autoScrollToBottom={false}
/>
);
});

const highlighter = container.querySelector('[data-testid="syntax-highlighter"]') as HTMLElement;
expect(highlighter).not.toBeNull();
expect(highlighter.textContent).toContain('line 120');
expect(highlighter.textContent).toContain('line 117');
expect(highlighter.textContent).not.toContain('line 116');
expect(Number(highlighter.dataset.startingLineNumber)).toBe(117);
});
});
19 changes: 12 additions & 7 deletions src/web-ui/src/flow_chat/components/CodePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,24 @@ function countNewlines(value: string, endExclusive = value.length): number {
return count;
}

function getStreamingTailLineLimit(maxHeight: number): number {
const visibleLines = Math.ceil(maxHeight / CODE_PREVIEW_STREAMING_LINE_HEIGHT_PX);
function getStreamingTailLineLimit(maxHeight: number, includeOverscan: boolean): number {
const visibleLines = Math.max(1, Math.ceil(maxHeight / CODE_PREVIEW_STREAMING_LINE_HEIGHT_PX));
const desiredLines = includeOverscan
? visibleLines + STREAMING_TAIL_OVERSCAN_LINES
: visibleLines;
const minimumLines = includeOverscan ? STREAMING_TAIL_MIN_LINES : 1;

return Math.min(
STREAMING_TAIL_MAX_LINES,
Math.max(STREAMING_TAIL_MIN_LINES, visibleLines + STREAMING_TAIL_OVERSCAN_LINES),
Math.max(minimumLines, desiredLines),
);
}

function getStreamingTailDisplayContent(content: string, maxHeight: number): {
function getStreamingTailDisplayContent(content: string, maxHeight: number, includeOverscan: boolean): {
content: string;
startingLineNumber: number;
} {
const tailLineLimit = getStreamingTailLineLimit(maxHeight);
const tailLineLimit = getStreamingTailLineLimit(maxHeight, includeOverscan);
const totalLineCount = countNewlines(content) + 1;

if (totalLineCount <= tailLineLimit && content.length <= STREAMING_TAIL_MAX_CHARS) {
Expand Down Expand Up @@ -142,8 +147,8 @@ export const CodePreview: React.FC<CodePreviewProps> = memo(({
return { content: deferredContent, startingLineNumber: 1 };
}

return getStreamingTailDisplayContent(deferredContent, maxHeight);
}, [isStreaming, deferredContent, maxHeight]);
return getStreamingTailDisplayContent(deferredContent, maxHeight, autoScrollToBottom);
}, [isStreaming, deferredContent, maxHeight, autoScrollToBottom]);

const displayContent = displayContentInfo.content;

Expand Down
7 changes: 4 additions & 3 deletions src/web-ui/src/flow_chat/tool-cards/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ Current examples:

When the preview uses a nested scrolling code viewer, avoid forcing that nested
viewer to auto-scroll while params are streaming. Streaming code previews already
render the latest viewport-sized tail, and the outer conversation list owns the
high-level follow behavior. Writing `scrollTop` on every preview batch can force
layout work inside the WebView and make long code output less responsive.
render the latest viewport-sized tail without overscan when nested auto-scroll is
disabled, and the outer conversation list owns the high-level follow behavior.
Writing `scrollTop` on every preview batch can force layout work inside the
WebView and make long code output less responsive.

Preferred pattern:

Expand Down
Loading