diff --git a/README.md b/README.md index 3679870..3ec2908 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,11 @@ authentication is considered complete. The client is a Svelte-based control surface distributed as an x86-64 AppImage. It communicates with `trixie-server` through the documented versioned HTTP and authenticated WebSocket interfaces. +Connection recovery follows the server policy: the first retry is immediate, +then retries wait 3, 10, and 15 seconds, followed by an indefinite 30-second +interval until the server reconnects. Manual connection checks do not create +overlapping retry loops. + Implementation has not started. See the parent workspace's `PLAN.md`, `TODO.md`, and `AGENTS.md` before beginning work. Consumes the versioned contracts from the sibling `trixie-api` repository. diff --git a/src/App.svelte b/src/App.svelte index 2b40cb7..e05b927 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -20,6 +20,7 @@ let authError = ''; let authBusy = false; let connectionNotice = ''; + let connectionCheckRunning = false; let realtimeState = 'DISCONNECTED'; let realtimeSocket: WebSocket | undefined; let activityEvents: Array<{ event_id: string; event_type: string; occurred_at: string }> = []; @@ -133,13 +134,16 @@ } async function checkServer() { + if (connectionCheckRunning) return; + connectionCheckRunning = true; connectionState = 'CONNECTING'; - const retryDelays = [0, 3000, 10000, 15000, 30000]; - for (let attempt = 0; attempt < retryDelays.length; attempt += 1) { - if (retryDelays[attempt] > 0) { + let attempt = 0; + while (true) { + const delay = attempt === 0 ? 0 : attempt === 1 ? 3000 : attempt === 2 ? 10000 : attempt === 3 ? 15000 : 30000; + if (delay > 0) { connectionState = 'RECONNECTING'; - connectionNotice = `Retry ${attempt + 1}/${retryDelays.length} in ${retryDelays[attempt] / 1000}s`; - await new Promise((resolve) => window.setTimeout(resolve, retryDelays[attempt])); + connectionNotice = `Retry ${attempt + 1} in ${delay / 1000}s`; + await new Promise((resolve) => window.setTimeout(resolve, delay)); } else { connectionNotice = 'Connecting to server'; } @@ -152,18 +156,20 @@ serverCompatibility = `Requires client ${info.minimum_client_version}–${info.maximum_client_version}`; connectionState = 'INCOMPATIBLE'; connectionNotice = 'Client update required'; + connectionCheckRunning = false; return; } serverCompatibility = info.minimum_client_version && info.maximum_client_version ? `${info.minimum_client_version}–${info.maximum_client_version}` : ''; connectionState = 'CONNECTED'; connectionNotice = ''; + connectionCheckRunning = false; return; } catch { + connectionState = 'SERVER UNAVAILABLE'; connectionNotice = 'Server unavailable'; } + attempt += 1; } - connectionState = 'SERVER UNAVAILABLE'; - connectionNotice = 'Reconnect attempts exhausted. Test connection to try again.'; } async function login() {