@@ -142,54 +142,59 @@ fun McpScreen(container: AppContainer, onBack: () -> Unit) {
142142 ConnectionBannerFor(container)
143143 // Crossfade between content states so transitions read as a smooth fade instead
144144 // of an instant snap. Matches the session list's Crossfade pattern; reduced
145− // motion is honored by Crossfade's default spec.
145+ // motion is honored by Crossfade's default spec. The content lambda branches
146+ // on its target-state parameter (not the captured `state`) so the outgoing
147+ // layer keeps rendering the OLD state type while it fades out — reading `state`
148+ // directly would recompose both layers to the latest content and defeat the
149+ // crossfade into an instant snap.
146150 val stateKey = state::class.simpleName
147− @Suppress("UnusedCrossfadeTargetStateParameter")
148151 Crossfade(
149152 targetState = stateKey,
150153 animationSpec = tween(soy.iko.opencode.data.network.NetworkConfig.motionFadeDurationMs.toInt()),
151154 label = "mcp_state",
152− ) {
153− when (val s = state) {
154− is McpViewModel.State.Loading -> Column(
155+ ) { key ->
156+ when (key) {
157+ "Loading" -> Column(
155158 modifier = Modifier.fillMaxSize().padding(16.dp),
156159 verticalArrangement = Arrangement.spacedBy(12.dp),
157160 ) {
158161 repeat(6) { SkeletonRow() }
159162 }
160− is McpViewModel.State.Disconnected -> EmptyState(
163+ "Disconnected" -> EmptyState(
161164 icon = Icons.Filled.CloudOff,
162165 title = stringResource(R.string.not_connected),
163166 modifier = Modifier.align(Alignment.Center),
164167 )
165− is McpViewModel.State.Error -> EmptyState(
168+ "Error" -> EmptyState(
166169 icon = Icons.Filled.ErrorOutline,
167170 title = stringResource(R.string.mcp_failed),
168− description = s.message,
171+ description = (state as? McpViewModel.State.Error)?.message,
169172 modifier = Modifier.align(Alignment.Center),
170173 actionLabel = stringResource(R.string.retry),
171174 onAction = { vm.load() },
172175 )
173− is McpViewModel.State.Ready ->
174− if (s.servers.isEmpty()) {
175− EmptyState(
176− icon = Icons.Filled.Hub,
177− title = stringResource(R.string.mcp_empty),
178− modifier = Modifier.align(Alignment.Center),
179− actionIcon = Icons.Filled.Add,
180− actionLabel = stringResource(R.string.mcp_add_server),
181− onAction = { showAddDialog = true },
182− )
183− } else {
184− LazyColumn(
185− modifier = Modifier.fillMaxWidth(),
186− contentPadding = PaddingValues(
187− start = 16.dp, end = 16.dp, top = 16.dp, bottom = soy.iko.opencode.data.network.NetworkConfig.listFabInsetDp.dp,
188− ),
189− verticalArrangement = Arrangement.spacedBy(12.dp),
190− ) {
191− items(s.servers, key = { it.name }) {
192− McpServerCard(it, modifier = Modifier.then(reducedMotionAnimateItem()))
176+ "Ready" ->
177+ (state as? McpViewModel.State.Ready)?.let { s ->
178+ if (s.servers.isEmpty()) {
179+ EmptyState(
180+ icon = Icons.Filled.Hub,
181+ title = stringResource(R.string.mcp_empty),
182+ modifier = Modifier.align(Alignment.Center),
183+ actionIcon = Icons.Filled.Add,
184+ actionLabel = stringResource(R.string.mcp_add_server),
185+ onAction = { showAddDialog = true },
186+ )
187+ } else {
188+ LazyColumn(
189+ modifier = Modifier.fillMaxWidth(),
190+ contentPadding = PaddingValues(
191+ start = 16.dp, end = 16.dp, top = 16.dp, bottom = soy.iko.opencode.data.network.NetworkConfig.listFabInsetDp.dp,
192+ ),
193+ verticalArrangement = Arrangement.spacedBy(12.dp),
194+ ) {
195+ items(s.servers, key = { it.name }) {
196+ McpServerCard(it, modifier = Modifier.then(reducedMotionAnimateItem()))
197+ }
193198 }
194199 }
195200 }