Skip to content

Commit a075f9c

Browse files
davindicodeclaude
andcommitted
Fix connection status: auto-reconnect terminals, accurate indicator
- On socket disconnect: mark all sessions as disconnected - On socket reconnect: auto re-create all terminal PTY sessions - Header status: online (green), reconnecting (yellow pulse), offline (red) - No longer shows "online" when terminals are actually dead Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 95a164d commit a075f9c

2 files changed

Lines changed: 64 additions & 6 deletions

File tree

app/components/Header.tsx

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
11
import { useTerminalStore } from "~/stores/terminalStore";
22

33
export default function Header() {
4-
const connected = useTerminalStore((s) => s.socketConnected);
4+
const socketConnected = useTerminalStore((s) => s.socketConnected);
5+
const sessions = useTerminalStore((s) => s.sessions);
6+
7+
// Check if any session is actually connected (PTY alive)
8+
const hasActiveSessions = Object.values(sessions).some(
9+
(s) => s.status === "connected" || s.status === "connecting"
10+
);
11+
12+
// online = socket connected AND at least one session alive
13+
// reconnecting = socket connected but all sessions dead (just reconnected)
14+
// offline = socket disconnected
15+
const status = !socketConnected
16+
? "offline"
17+
: hasActiveSessions
18+
? "online"
19+
: Object.keys(sessions).length > 0
20+
? "reconnecting"
21+
: "online";
522

623
return (
724
<header className="flex items-center justify-between px-3 py-1.5 bg-[#0d0d1a] border-b border-gray-800 shrink-0">
@@ -11,10 +28,22 @@ export default function Header() {
1128
</div>
1229
<div className="flex items-center gap-2">
1330
<span
14-
className={`w-2 h-2 rounded-full ${connected ? "bg-green-500" : "bg-red-500"}`}
31+
className={`w-2 h-2 rounded-full ${
32+
status === "online"
33+
? "bg-green-500"
34+
: status === "reconnecting"
35+
? "bg-yellow-500 animate-pulse"
36+
: "bg-red-500"
37+
}`}
1538
/>
16-
<span className={`text-xs ${connected ? "text-green-500" : "text-red-400"}`}>
17-
{connected ? "online" : "offline"}
39+
<span className={`text-xs ${
40+
status === "online"
41+
? "text-green-500"
42+
: status === "reconnecting"
43+
? "text-yellow-500"
44+
: "text-red-400"
45+
}`}>
46+
{status}
1847
</span>
1948
</div>
2049
</header>

app/stores/terminalStore.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,37 @@ export const useTerminalStore = create<TerminalState>((set, get) => ({
4848

4949
const socket = getSocket();
5050

51-
socket.on("connect", () => set({ socketConnected: true }));
52-
socket.on("disconnect", () => set({ socketConnected: false }));
51+
socket.on("connect", () => {
52+
set({ socketConnected: true });
53+
54+
// On reconnect, re-create all existing sessions (server killed PTYs on disconnect)
55+
const { sessions } = get();
56+
for (const session of Object.values(sessions)) {
57+
if (session.status === "disconnected" || session.status === "error") {
58+
socket.emit("create_terminal", {
59+
sessionId: session.id,
60+
cwd: get().defaultCwd || undefined,
61+
});
62+
set({
63+
sessions: {
64+
...get().sessions,
65+
[session.id]: { ...get().sessions[session.id], status: "connecting" },
66+
},
67+
});
68+
}
69+
}
70+
});
71+
72+
socket.on("disconnect", () => {
73+
set({ socketConnected: false });
74+
// Mark all sessions as disconnected
75+
const { sessions } = get();
76+
const updated: Record<string, typeof sessions[string]> = {};
77+
for (const [id, session] of Object.entries(sessions)) {
78+
updated[id] = { ...session, status: "disconnected" };
79+
}
80+
set({ sessions: updated });
81+
});
5382

5483
socket.on("terminal_ready", (data: { sessionId: string }) => {
5584
const { sessions } = get();

0 commit comments

Comments
 (0)