()
| 208 | } |
| 209 | |
| 210 | function connect(): void { |
| 211 | setStatus('connecting') |
| 212 | |
| 213 | ws = new WebSocket(getWsUrl()) |
| 214 | |
| 215 | ws.addEventListener('open', () => { |
| 216 | connected = true |
| 217 | reconnectDelay = RECONNECT_BASE_MS |
| 218 | setStatus('connected') |
| 219 | hideOverlay(loadingOverlay) |
| 220 | hideOverlay(reconnectOverlay) |
| 221 | // Re-sync size in case the window changed while connecting |
| 222 | fitAddon.fit() |
| 223 | sendJSON({ type: 'resize', cols: term.cols, rows: term.rows }) |
| 224 | startPing() |
| 225 | }) |
| 226 | |
| 227 | ws.addEventListener('message', ({ data }: MessageEvent<string>) => { |
| 228 | // All messages from the server are strings. |
| 229 | // Try JSON control message first; fall back to raw PTY output. |
| 230 | if (data.startsWith('{')) { |
| 231 | try { |
| 232 | handleControlMessage(JSON.parse(data) as ServerMessage) |
| 233 | return |
| 234 | } catch { |
| 235 | // Not JSON — fall through to write as PTY output |
| 236 | } |
| 237 | } |
| 238 | term.write(data) |
| 239 | }) |
| 240 | |
| 241 | ws.addEventListener('close', onDisconnect) |
| 242 | ws.addEventListener('error', () => { |
| 243 | // 'error' always fires before 'close'; let onDisconnect handle reconnect |
| 244 | }) |
| 245 | } |
| 246 | |
| 247 | function handleControlMessage(msg: ServerMessage): void { |
| 248 | switch (msg.type) { |
no test coverage detected