| ... | @@ -2705,68 +2705,40 @@ export default function ChatView(props: ChatViewProps) { | ... | @@ -2705,68 +2705,40 @@ export default function ChatView(props: ChatViewProps) { |
| 2705 | unavailableConnection !== null && | 2705 | unavailableConnection !== null && |
| 2706 | (unavailableConnection.phase === "connecting" || | 2706 | (unavailableConnection.phase === "connecting" || |
| 2707 | unavailableConnection.phase === "reconnecting"); | 2707 | unavailableConnection.phase === "reconnecting"); |
| 2708 | // Reconnecting to a version-skewed server with no update in flight | | |
| 2709 | // usually means the server is restarting mid-update and a refresh wiped | | |
| 2710 | // the in-memory update state. Fold the reconnect and version banners | | |
| 2711 | // into one calm line instead of stacking "Failed to connect" on | | |
| 2712 | // "versions differ". A failed update never folds: its error and retry | | |
| 2713 | // action must stay visible. | | |
| 2714 | const reconnectingThroughVersionSkew = | | |
| 2715 | serverUpdateState.status === "idle" && environmentReconnecting && versionMismatch !== null; | | |
| 2716 | // While an update runs, transient connect blips are expected (the server | 2708 | // While an update runs, transient connect blips are expected (the server |
| 2717 | // restarts) and the update banner already shows progress. Hard failure | 2709 | // restarts) and the update banner already shows progress. Hard failure |
| 2718 | // phases still surface so the Reconnect action stays reachable. | 2710 | // phases still surface so the Reconnect action stays reachable. |
| 2719 | const suppressUnavailableBanner = | 2711 | const suppressUnavailableBanner = |
| 2720 | environmentReconnecting && | 2712 | environmentReconnecting && (updateRunning || !reconnectWarningGraceElapsed); |
| 2721 | (updateRunning || (!reconnectingThroughVersionSkew && !reconnectWarningGraceElapsed)); | | |
| 2722 | if (activeEnvironmentUnavailableState && unavailableConnection && !suppressUnavailableBanner) { | 2713 | if (activeEnvironmentUnavailableState && unavailableConnection && !suppressUnavailableBanner) { |
| 2723 | if (reconnectingThroughVersionSkew) { | 2714 | items.push({ |
| 2724 | items.push({ | 2715 | id: `environment-unavailable:${activeEnvironmentUnavailableState.environmentId}`, |
| 2725 | id: `environment-unavailable:${activeEnvironmentUnavailableState.environmentId}`, | 2716 | variant: unavailableConnection.phase === "error" ? "error" : "warning", |
| 2726 | variant: "default", | 2717 | icon: <WifiOffIcon />, |
| 2727 | // Prioritize live connection progress among the notices. | 2718 | title: `${activeEnvironmentUnavailableState.label} is ${environmentReconnecting ? "reconnecting" : "offline"}`, |
| 2728 | priority: "urgent", | 2719 | actions: ( |
| 2729 | icon: ( | 2720 | <> |
| 2730 | <span | 2721 | {!environmentReconnecting ? ( |
| 2731 | className="size-1.5 animate-status-pulse rounded-full bg-foreground" | 2722 | <Button |
| 2732 | aria-hidden="true" | 2723 | size="xs" |
| 2733 | /> | 2724 | variant="ghost" |
| 2734 | ), | 2725 | onClick={() => |
| 2735 | title: `${unavailableConnection.phase === "connecting" ? "Connecting" : "Reconnecting"} to ${activeEnvironmentUnavailableState.label}`, | 2726 | void handleReconnectActiveEnvironment( |
| 2736 | description: "Finishing an update", | 2727 | activeEnvironmentUnavailableState.environmentId, |
| 2737 | actions: disconnectAction, | 2728 | ) |
| 2738 | }); | 2729 | } |
| 2739 | } else { | 2730 | > |
| 2740 | items.push({ | 2731 | Reconnect |
| 2741 | id: `environment-unavailable:${activeEnvironmentUnavailableState.environmentId}`, | 2732 | </Button> |
| 2742 | variant: unavailableConnection.phase === "error" ? "error" : "warning", | 2733 | ) : null} |
| 2743 | icon: <WifiOffIcon />, | 2734 | {disconnectAction} |
| 2744 | title: `${activeEnvironmentUnavailableState.label} is ${environmentReconnecting ? "reconnecting" : "offline"}`, | 2735 | </> |
| 2745 | actions: ( | 2736 | ), |
| 2746 | <> | 2737 | }); |
| 2747 | {!environmentReconnecting ? ( | | |
| 2748 | <Button | | |
| 2749 | size="xs" | | |
| 2750 | variant="ghost" | | |
| 2751 | onClick={() => | | |
| 2752 | void handleReconnectActiveEnvironment( | | |
| 2753 | activeEnvironmentUnavailableState.environmentId, | | |
| 2754 | ) | | |
| 2755 | } | | |
| 2756 | > | | |
| 2757 | Reconnect | | |
| 2758 | </Button> | | |
| 2759 | ) : null} | | |
| 2760 | {disconnectAction} | | |
| 2761 | </> | | |
| 2762 | ), | | |
| 2763 | }); | | |
| 2764 | } | | |
| 2765 | } | 2738 | } |
| 2766 | if ( | 2739 | if ( |
| 2767 | !automaticEnvironment && | 2740 | !automaticEnvironment && |
| 2768 | serverUpdateEnvironmentId && | 2741 | serverUpdateEnvironmentId && |
| 2769 | !reconnectingThroughVersionSkew && | | |
| 2770 | (serverUpdateState.status === "idle" | 2742 | (serverUpdateState.status === "idle" |
| 2771 | ? showVersionMismatchBanner | 2743 | ? showVersionMismatchBanner |
| 2772 | : !serverUpdateFailureDismissed) | 2744 | : !serverUpdateFailureDismissed) |