1import type { WorkspaceState } from "../../state/workspaceModel";
2
3export interface WorkspaceConnectionStatusPresentation {
4 readonly label: string;
5 /** True while actively working (connecting/syncing) — render a spinner. False for offline/error/idle states — render a wifi-slash icon. */
6 readonly showsProgress: boolean;
7}
8
9function shouldShowWorkspaceConnectionStatus(state: WorkspaceState): boolean {
10 return (
11 state.networkStatus === "offline" ||
12 state.connectionError !== null ||
13 state.hasConnectingEnvironment ||
14 state.hasPendingShellSnapshot ||
15 (state.hasLoadedShellSnapshot && !state.hasReadyEnvironment)
16 );
17}
18
19function workspaceConnectionStatusLabel(state: WorkspaceState): string {
20 if (state.networkStatus === "offline") return "You are offline";
21 if (state.connectingEnvironments.length === 1) {
22 return `Reconnecting to ${state.connectingEnvironments[0]!.environmentLabel}`;
23 }
24 if (state.connectingEnvironments.length > 1) {
25 return `Reconnecting ${state.connectingEnvironments.length} environments`;
26 }
27 if (state.connectionError !== null) return state.connectionError;
28 if (state.hasPendingShellSnapshot) {
29 return state.hasLoadedShellSnapshot ? "Syncing threads..." : "Loading threads...";
30 }
31 return "Not connected";
32}
33
34/** Header-title presentation of the connection state, or null while connected. */
35export function workspaceConnectionStatusPresentation(
36 state: WorkspaceState,
37): WorkspaceConnectionStatusPresentation | null {
38 if (!shouldShowWorkspaceConnectionStatus(state)) return null;
39 return {
40 label: workspaceConnectionStatusLabel(state),
41 showsProgress:
42 state.networkStatus !== "offline" &&
43 state.connectionError === null &&
44 (state.connectingEnvironments.length > 0 || state.hasPendingShellSnapshot),
45 };
46}