mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-12 01:17:48 +00:00
fix(shell): restore terminal focus when switching to the shell tab
Pass shell activity state from MainContent through StandaloneShell and use it inside Shell to explicitly focus the xterm instance once the terminal is both initialized and connected. Previously, switching to the Shell tab left focus on the tab button because isActive was being ignored and the terminal never called focus() after the tab activation lifecycle completed. As a result, users had to click inside the terminal before keyboard input would be accepted. This change wires isActive through the shell stack, removes the unused prop handling in Shell, and adds a focus effect that runs when the shell becomes active and ready. The effect uses both requestAnimationFrame and a zero-delay timeout so focus is applied reliably after rendering and connection state updates settle. This restores immediate typing when opening the shell tab and also improves the reconnect path by re-focusing the terminal after the shell connection is ready.
This commit is contained in:
@@ -146,7 +146,12 @@ function MainContent({
|
||||
|
||||
{activeTab === 'shell' && (
|
||||
<div className="h-full w-full overflow-hidden">
|
||||
<StandaloneShell project={selectedProject} session={selectedSession} showHeader={false} />
|
||||
<StandaloneShell
|
||||
project={selectedProject}
|
||||
session={selectedSession}
|
||||
showHeader={false}
|
||||
isActive={activeTab === 'shell'}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function Shell({
|
||||
onProcessComplete = null,
|
||||
minimal = false,
|
||||
autoConnect = false,
|
||||
isActive,
|
||||
isActive = true,
|
||||
}: ShellProps) {
|
||||
const { t } = useTranslation('chat');
|
||||
const [isRestarting, setIsRestarting] = useState(false);
|
||||
@@ -48,9 +48,6 @@ export default function Shell({
|
||||
const promptCheckTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const onOutputRef = useRef<(() => void) | null>(null);
|
||||
|
||||
// Keep the public API stable for existing callers that still pass `isActive`.
|
||||
void isActive;
|
||||
|
||||
const {
|
||||
terminalContainerRef,
|
||||
terminalRef,
|
||||
@@ -157,6 +154,24 @@ export default function Shell({
|
||||
}
|
||||
}, [isConnected]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isActive || !isInitialized || !isConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
const focusTerminal = () => {
|
||||
terminalRef.current?.focus();
|
||||
};
|
||||
|
||||
const animationFrameId = window.requestAnimationFrame(focusTerminal);
|
||||
const timeoutId = window.setTimeout(focusTerminal, 0);
|
||||
|
||||
return () => {
|
||||
window.cancelAnimationFrame(animationFrameId);
|
||||
window.clearTimeout(timeoutId);
|
||||
};
|
||||
}, [isActive, isConnected, isInitialized, terminalRef]);
|
||||
|
||||
const sendInput = useCallback(
|
||||
(data: string) => {
|
||||
sendSocketMessage(wsRef.current, { type: 'input', data });
|
||||
|
||||
@@ -9,6 +9,7 @@ type StandaloneShellProps = {
|
||||
session?: ProjectSession | null;
|
||||
command?: string | null;
|
||||
isPlainShell?: boolean | null;
|
||||
isActive?: boolean;
|
||||
autoConnect?: boolean;
|
||||
onComplete?: ((exitCode: number) => void) | null;
|
||||
onClose?: (() => void) | null;
|
||||
@@ -24,6 +25,7 @@ export default function StandaloneShell({
|
||||
session = null,
|
||||
command = null,
|
||||
isPlainShell = null,
|
||||
isActive = true,
|
||||
autoConnect = true,
|
||||
onComplete = null,
|
||||
onClose = null,
|
||||
@@ -64,6 +66,7 @@ export default function StandaloneShell({
|
||||
selectedSession={session}
|
||||
initialCommand={command}
|
||||
isPlainShell={shouldUsePlainShell}
|
||||
isActive={isActive}
|
||||
onProcessComplete={handleProcessComplete}
|
||||
minimal={minimal}
|
||||
autoConnect={minimal ? true : autoConnect}
|
||||
|
||||
Reference in New Issue
Block a user