fix: E2E test stability and UI performance improvements (#823)

This commit is contained in:
gsxdsm
2026-03-02 18:46:28 -08:00
committed by GitHub
parent 1c3d6434a8
commit 54d69e907b
9 changed files with 88 additions and 21 deletions

View File

@@ -119,7 +119,6 @@ export function useBoardEffects({
if (featuresFingerprint && !isLoading) {
checkAllContexts();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [featuresFingerprint, isLoading, checkContextExists, setFeaturesWithContext]);
// Re-check context when a feature stops, completes, or errors

View File

@@ -1,4 +1,4 @@
import { useEffect, useRef, useCallback, useState } from 'react';
import { useEffect, useRef, useCallback, useState, useMemo } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import {
@@ -54,6 +54,8 @@ export function DevServerLogsPanel({
const {
logs,
logsVersion,
didTrim,
isRunning,
isLoading,
error,
@@ -81,8 +83,9 @@ export function DevServerLogsPanel({
return;
}
// If logs got shorter (e.g., cleared), rewrite all
if (logs.length < lastLogsLengthRef.current) {
// If logs got shorter (e.g., cleared) or buffer was trimmed (content shifted),
// do a full rewrite so the terminal stays in sync
if (logs.length < lastLogsLengthRef.current || didTrim) {
xtermRef.current.write(logs);
lastLogsLengthRef.current = logs.length;
return;
@@ -94,7 +97,7 @@ export function DevServerLogsPanel({
xtermRef.current.append(newContent);
lastLogsLengthRef.current = logs.length;
}
}, [logs, worktree?.path]);
}, [logs, logsVersion, didTrim, worktree?.path]);
// Reset when panel opens with a new worktree
useEffect(() => {
@@ -123,10 +126,19 @@ export function DevServerLogsPanel({
}
}, []);
const lineCount = useMemo(() => {
if (!logs) return 0;
// Count newlines directly instead of allocating a split array
let count = 1;
for (let i = 0; i < logs.length; i++) {
if (logs.charCodeAt(i) === 10) count++;
}
return count;
}, [logs]);
if (!worktree) return null;
const formattedStartTime = formatStartedAt(startedAt);
const lineCount = logs ? logs.split('\n').length : 0;
return (
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && onClose()}>

View File

@@ -5,9 +5,16 @@ import { pathsEqual } from '@/lib/utils';
const logger = createLogger('DevServerLogs');
// Maximum log buffer size (characters) - matches server-side MAX_SCROLLBACK_SIZE
const MAX_LOG_BUFFER_SIZE = 50_000; // ~50KB
export interface DevServerLogState {
/** The log content (buffered + live) */
logs: string;
/** Incremented whenever logs content changes (including trim+shift) */
logsVersion: number;
/** True when the latest append caused head truncation */
didTrim: boolean;
/** Whether the server is currently running */
isRunning: boolean;
/** Whether initial logs are being fetched */
@@ -52,6 +59,8 @@ interface UseDevServerLogsOptions {
export function useDevServerLogs({ worktreePath, autoSubscribe = true }: UseDevServerLogsOptions) {
const [state, setState] = useState<DevServerLogState>({
logs: '',
logsVersion: 0,
didTrim: false,
isRunning: false,
isLoading: false,
error: null,
@@ -123,6 +132,8 @@ export function useDevServerLogs({ worktreePath, autoSubscribe = true }: UseDevS
const clearLogs = useCallback(() => {
setState({
logs: '',
logsVersion: 0,
didTrim: false,
isRunning: false,
isLoading: false,
error: null,
@@ -136,13 +147,27 @@ export function useDevServerLogs({ worktreePath, autoSubscribe = true }: UseDevS
}, []);
/**
* Append content to logs
* Append content to logs, enforcing a maximum buffer size to prevent
* unbounded memory growth and progressive UI lag.
*/
const appendLogs = useCallback((content: string) => {
setState((prev) => ({
...prev,
logs: prev.logs + content,
}));
setState((prev) => {
const combined = prev.logs + content;
const didTrim = combined.length > MAX_LOG_BUFFER_SIZE;
let newLogs = combined;
if (didTrim) {
const slicePoint = combined.length - MAX_LOG_BUFFER_SIZE;
// Find the next newline after the slice point to avoid cutting a line in half
const firstNewlineIndex = combined.indexOf('\n', slicePoint);
newLogs = combined.slice(firstNewlineIndex > -1 ? firstNewlineIndex + 1 : slicePoint);
}
return {
...prev,
logs: newLogs,
didTrim,
logsVersion: prev.logsVersion + 1,
};
});
}, []);
// Fetch initial logs when worktreePath changes