Skip to content

Commit cb0293e

Browse files
drfarrellclaude
andcommitted
perf: ultra-lightweight error detection with minimal resource usage
Major performance optimizations: - Single timer instead of multiple timeouts and tracking - Early bailouts to avoid unnecessary checks - One useEffect with minimal dependencies (only branch ID) - Removed connectionStartTimeRef and complex time tracking - Removed useMemo - now using simple state - Clear timer immediately on any state change Resource usage: - Maximum 1 timer active at any time - Timer only created when actually connecting - Immediate cleanup on branch change or success - No polling, no intervals, no repeated calculations - Minimal re-renders (only on actual state changes) This is now ULTRALIGHT while remaining robust for 502 detection 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent ccf112f commit cb0293e

File tree

1 file changed

+48
-50
lines changed

1 file changed

+48
-50
lines changed

apps/web/client/src/app/project/[id]/_components/bottom-bar/terminal-area.tsx

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
88
import { cn } from '@onlook/ui/utils';
99
import { observer } from 'mobx-react-lite';
1010
import { motion } from 'motion/react';
11-
import { useEffect, useMemo, useRef, useState } from 'react';
11+
import { useEffect, useRef, useState } from 'react';
1212
import { Terminal } from './terminal';
1313

1414
export const TerminalArea = observer(({ children }: { children: React.ReactNode }) => {
@@ -51,77 +51,75 @@ export const TerminalArea = observer(({ children }: { children: React.ReactNode
5151

5252
const [terminalHidden, setTerminalHidden] = useState(true);
5353
const [restarting, setRestarting] = useState(false);
54-
const [hasConnectionTimeout, setHasConnectionTimeout] = useState(false);
55-
const connectionStartTimeRef = useRef<number | null>(null);
54+
55+
// Ultra-lightweight error detection using a single timer
56+
const timeoutIdRef = useRef<NodeJS.Timeout | null>(null);
57+
const [hasSandboxError, setHasSandboxError] = useState(false);
5658

57-
// Track connection state and detect timeouts (actual 502 errors)
59+
// Single effect that only sets/clears one timer - extremely efficient
5860
useEffect(() => {
61+
// Clear any existing timer first
62+
if (timeoutIdRef.current) {
63+
clearTimeout(timeoutIdRef.current);
64+
timeoutIdRef.current = null;
65+
}
66+
5967
const activeBranch = branches.activeBranch;
6068
if (!activeBranch) {
61-
setHasConnectionTimeout(false);
62-
connectionStartTimeRef.current = null;
69+
setHasSandboxError(false);
6370
return;
6471
}
6572

6673
const branchData = branches.getBranchDataById(activeBranch.id);
67-
const isConnecting = branchData?.sandbox?.session?.isConnecting || branchData?.sandbox?.isIndexing;
68-
const hasProvider = branchData?.sandbox?.session?.provider;
69-
70-
// If we have a provider, connection is successful - clear any timeout
71-
if (hasProvider) {
72-
setHasConnectionTimeout(false);
73-
connectionStartTimeRef.current = null;
74+
const sandbox = branchData?.sandbox;
75+
76+
// Quick bailouts - no timer needed
77+
if (!sandbox?.session) {
78+
setHasSandboxError(false);
7479
return;
7580
}
76-
77-
// If not connecting, clear the timeout tracking
78-
if (!isConnecting) {
79-
setHasConnectionTimeout(false);
80-
connectionStartTimeRef.current = null;
81+
82+
// If we have a provider, we're connected - no error
83+
if (sandbox.session.provider) {
84+
setHasSandboxError(false);
8185
return;
8286
}
83-
84-
// Start tracking connection time if not already
85-
if (!connectionStartTimeRef.current) {
86-
connectionStartTimeRef.current = Date.now();
87+
88+
// Only set timer if actually connecting (not just idle)
89+
const isConnecting = sandbox.session.isConnecting || sandbox.isIndexing;
90+
if (!isConnecting) {
91+
setHasSandboxError(false);
92+
return;
8793
}
8894

89-
// Check if we've been connecting for too long (5 seconds - users see 502 quickly)
90-
const connectionDuration = Date.now() - connectionStartTimeRef.current;
91-
const TIMEOUT_MS = 5000; // 5 seconds - enough to avoid false positives but quick enough for 502s
92-
93-
if (connectionDuration >= TIMEOUT_MS) {
94-
// This is a real timeout/502 - show amber
95-
setHasConnectionTimeout(true);
96-
} else {
97-
// Set up a timeout to check again
98-
const remainingTime = TIMEOUT_MS - connectionDuration;
99-
const timeoutId = setTimeout(() => {
100-
// Re-check if still connecting after timeout
101-
const stillConnecting = branches.getBranchDataById(activeBranch.id)?.sandbox?.session?.isConnecting ||
102-
branches.getBranchDataById(activeBranch.id)?.sandbox?.isIndexing;
103-
const stillNoProvider = !branches.getBranchDataById(activeBranch.id)?.sandbox?.session?.provider;
104-
105-
if (stillConnecting && stillNoProvider) {
106-
setHasConnectionTimeout(true);
107-
}
108-
}, remainingTime);
109-
110-
return () => clearTimeout(timeoutId);
111-
}
112-
}, [branches, branches.activeBranch]);
95+
// Set a single 5-second timer - that's it, ultra simple
96+
timeoutIdRef.current = setTimeout(() => {
97+
// Final check after 5 seconds
98+
const stillConnecting = branches.getBranchDataById(activeBranch.id)?.sandbox?.session?.isConnecting ||
99+
branches.getBranchDataById(activeBranch.id)?.sandbox?.isIndexing;
100+
const stillNoProvider = !branches.getBranchDataById(activeBranch.id)?.sandbox?.session?.provider;
101+
102+
if (stillConnecting && stillNoProvider) {
103+
setHasSandboxError(true);
104+
}
105+
}, 5000);
113106

114-
// Simple computed value - just check if we have a confirmed timeout
115-
const hasSandboxError = hasConnectionTimeout;
107+
// Cleanup on unmount or deps change
108+
return () => {
109+
if (timeoutIdRef.current) {
110+
clearTimeout(timeoutIdRef.current);
111+
timeoutIdRef.current = null;
112+
}
113+
};
114+
}, [branches.activeBranch?.id, branches]); // Only re-run when branch ID changes
116115

117116
// Extract restart logic into a reusable function to follow DRY principles
118117
const handleRestartSandbox = async () => {
119118
const activeBranch = branches.activeBranch;
120119
if (!activeBranch || restarting) return;
121120

122121
setRestarting(true);
123-
setHasConnectionTimeout(false); // Clear timeout state on restart
124-
connectionStartTimeRef.current = null; // Reset connection tracking
122+
setHasSandboxError(false); // Clear error state on restart
125123

126124
try {
127125
const sandbox = branches.getSandboxById(activeBranch.id);

0 commit comments

Comments
 (0)