diff --git a/apps/android/app/src/main/java/ai/openclaw/app/ui/CommandPalette.kt b/apps/android/app/src/main/java/ai/openclaw/app/ui/CommandPalette.kt index bf10cc018e62..798536e65c2f 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/ui/CommandPalette.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/ui/CommandPalette.kt @@ -1,7 +1,6 @@ package ai.openclaw.app.ui import ai.openclaw.app.GatewayModelProviderSummary -import ai.openclaw.app.GatewayModelSummary import ai.openclaw.app.MainViewModel import ai.openclaw.app.ui.design.ClawEmptyState import ai.openclaw.app.ui.design.ClawPanel @@ -64,7 +63,6 @@ internal fun CommandPalette( ) { val isConnected by viewModel.isConnected.collectAsState() val sessions by viewModel.chatSessions.collectAsState() - val models by viewModel.modelCatalog.collectAsState() val providers by viewModel.modelAuthProviders.collectAsState() val pendingRunCount by viewModel.pendingRunCount.collectAsState() var query by rememberSaveable { mutableStateOf("") } @@ -74,7 +72,7 @@ internal fun CommandPalette( CommandItem("Open Chat", "Start or continue a conversation", Icons.Outlined.ChatBubbleOutline, onOpenChat), CommandItem("Start Voice", "Talk or dictate with OpenClaw", Icons.Outlined.MicNone, onOpenVoice), CommandItem("Browse Sessions", "Find previous conversations", Icons.Outlined.AccessTime, onOpenSessions), - CommandItem("Providers & Models", providerCommandSubtitle(isConnected, providers, models), Icons.Outlined.Inventory2, onOpenProviders), + CommandItem("Providers & Models", providerCommandSubtitle(isConnected, providers), Icons.Outlined.Inventory2, onOpenProviders), CommandItem("Settings", "Gateway, voice, notifications, privacy", Icons.Outlined.Settings, onOpenSettings), ) val actionRows = quickActions.filter { it.matches(normalizedQuery) } @@ -300,14 +298,10 @@ private fun CommandSectionLabel(title: String) { internal fun providerCommandSubtitle( isConnected: Boolean, providers: List, - models: List, ): String { - if (!isConnected) return "Connect Gateway to load models" - val expiringProviderCount = expiringModelProviderCount(providers) - if (expiringProviderCount > 0) return "$expiringProviderCount providers expiring" - val readyProviderCount = readyModelProviderCount(providers, models) + if (!isConnected) return "Connect Gateway to manage providers" + val readyProviderCount = providers.count { modelProviderReady(it.status) } if (readyProviderCount > 0) return "$readyProviderCount providers ready" - if (models.isNotEmpty()) return "${models.size} models available" return "Configure model access" } diff --git a/apps/android/app/src/main/java/ai/openclaw/app/ui/ProvidersModelsScreen.kt b/apps/android/app/src/main/java/ai/openclaw/app/ui/ProvidersModelsScreen.kt index 0866ea02e369..af77a6466261 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/ui/ProvidersModelsScreen.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/ui/ProvidersModelsScreen.kt @@ -30,14 +30,12 @@ import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.KeyboardArrowDown import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.Surface @@ -46,9 +44,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -59,7 +54,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -/** Android providers/models browser backed by the gateway catalog. */ +/** Android provider readiness and setup screen backed by the gateway catalog. */ @Composable internal fun ProvidersModelsScreen( viewModel: MainViewModel, @@ -72,9 +67,7 @@ internal fun ProvidersModelsScreen( val refreshing by viewModel.modelCatalogRefreshing.collectAsState() val errorText by viewModel.modelCatalogErrorText.collectAsState() val providerRows = providerRows(providers = providers, models = models) - val modelGroups = sortedModelGroups(models) val setupRows = providerSetupRows(providerRows) - var expandedModelProviders by rememberSaveable { mutableStateOf(emptyList()) } LaunchedEffect(isConnected) { if (isConnected) { @@ -105,7 +98,7 @@ internal fun ProvidersModelsScreen( Column(verticalArrangement = Arrangement.spacedBy(3.dp)) { Text(text = "Providers & Models", style = ClawTheme.type.display.copy(fontSize = 14.8.sp, lineHeight = 18.sp), color = ClawTheme.colors.text, maxLines = 1) Text( - text = "Connect and manage AI providers\nBrowse models and their capabilities.", + text = "Connect and manage AI providers\nReview provider readiness and setup.", style = ClawTheme.type.caption.copy(fontSize = 12.5.sp, lineHeight = 16.sp), color = ClawTheme.colors.textMuted, ) @@ -151,36 +144,6 @@ internal fun ProvidersModelsScreen( } } } - - item { - ProviderSectionLabel(title = "Model catalog") - } - - if (modelGroups.isEmpty()) { - item { - ModelCatalogEmpty( - title = if (refreshing) "Loading models" else "No models loaded", - body = if (isConnected) "Refresh after configuring a provider on the Gateway." else "Connect the Gateway to browse models.", - ) - } - } else { - items(modelGroups, key = { it.first }) { entry -> - val expanded = expandedModelProviders.contains(entry.first) - ModelGroup( - provider = entry.first, - models = entry.second, - expanded = expanded, - onToggle = { - expandedModelProviders = - if (expanded) { - expandedModelProviders - entry.first - } else { - expandedModelProviders + entry.first - } - }, - ) - } - } } ProviderAddButton(onClick = onAddProvider, modifier = Modifier.align(Alignment.BottomCenter)) } @@ -334,15 +297,6 @@ internal fun expiringModelProviderCount(providers: List): List>> = - models - .groupBy { it.provider } - .entries - .sortedWith(compareBy({ providerPriority(it.key) }, { providerDisplayName(it.key).lowercase() })) - .map { it.key to it.value } - private fun providerPriority(row: ProviderRow): Int = providerPriority(row.id) private fun providerPriority(provider: String): Int = @@ -527,79 +481,6 @@ private fun providerInitials(value: String): String = .joinToString("") .ifBlank { "AI" } -@Composable -private fun ModelCatalogEmpty( - title: String, - body: String, -) { - ClawPanel(contentPadding = PaddingValues(horizontal = 11.dp, vertical = 10.dp)) { - Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(4.dp)) { - Text(text = title, style = ClawTheme.type.section, color = ClawTheme.colors.text) - Text(text = body, style = ClawTheme.type.caption.copy(fontSize = 12.5.sp, lineHeight = 16.sp), color = ClawTheme.colors.textMuted) - } - } -} - -@Composable -private fun ModelGroup( - provider: String, - models: List, - expanded: Boolean, - onToggle: () -> Unit, -) { - ClawPanel(contentPadding = PaddingValues(horizontal = 0.dp, vertical = 0.dp)) { - Column { - Surface(onClick = onToggle, color = Color.Transparent, contentColor = ClawTheme.colors.text) { - Row(modifier = Modifier.fillMaxWidth().heightIn(min = 52.dp).padding(horizontal = 10.dp, vertical = 6.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(10.dp)) { - ProviderBadge(text = providerDisplayName(provider)) - Text(text = providerDisplayName(provider), style = ClawTheme.type.body, color = ClawTheme.colors.text, modifier = Modifier.weight(1f), maxLines = 1) - ProviderMiniTag(text = "${models.size} models") - Icon(imageVector = if (expanded) Icons.Default.KeyboardArrowDown else Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = if (expanded) "Collapse ${providerDisplayName(provider)} models" else "Expand ${providerDisplayName(provider)} models", modifier = Modifier.size(14.dp), tint = ClawTheme.colors.textMuted) - } - } - HorizontalDivider(color = ClawTheme.colors.border, thickness = 1.dp) - val visibleModels = if (expanded) models else models.take(3) - visibleModels.forEachIndexed { index, model -> - ModelRow(model) - if (index != visibleModels.lastIndex || models.size > visibleModels.size) { - HorizontalDivider(color = ClawTheme.colors.border, thickness = 1.dp) - } - } - if (models.size > visibleModels.size) { - Surface(onClick = onToggle, color = Color.Transparent, contentColor = ClawTheme.colors.text) { - Row(modifier = Modifier.fillMaxWidth().padding(horizontal = 10.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically) { - Text(text = "View all models", style = ClawTheme.type.caption.copy(fontSize = 12.5.sp, lineHeight = 16.sp), color = ClawTheme.colors.textMuted, modifier = Modifier.weight(1f)) - Icon(imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = "View all models", modifier = Modifier.size(14.dp), tint = ClawTheme.colors.text) - } - } - } - } - } -} - -@Composable -private fun ModelRow(model: GatewayModelSummary) { - val available = modelAvailabilityUsable(model) - Row(modifier = Modifier.fillMaxWidth().heightIn(min = 48.dp).padding(horizontal = 10.dp, vertical = 5.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp)) { - Text(text = model.name, style = ClawTheme.type.mono, color = ClawTheme.colors.text, modifier = Modifier.weight(1f), maxLines = 1, overflow = TextOverflow.Ellipsis) - modelCapabilityLabels(model).take(3).forEach { label -> - ProviderMiniTag(text = label) - } - Box(modifier = Modifier.size(4.5.dp).clip(CircleShape).background(if (available) ClawTheme.colors.success else ClawTheme.colors.warning)) - } -} - -/** Derives compact capability chips for model catalog rows. */ -private fun modelCapabilityLabels(model: GatewayModelSummary): List = - buildList { - if (model.supportsReasoning) add("Reasoning") - if (model.supportsVision) add("Vision") - if (model.supportsAudio) add("Voice") - if (model.supportsDocuments) add("Docs") - if ((model.contextTokens ?: 0L) >= 100_000L) add("Long context") - if (isEmpty()) add("Fast") - } - @Composable private fun ProviderSectionLabel(title: String) { Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween) { @@ -651,15 +532,3 @@ private fun ProviderAddButton( } } } - -@Composable -private fun ProviderMiniTag(text: String) { - Surface( - shape = RoundedCornerShape(5.dp), - color = Color.Transparent, - border = BorderStroke(1.dp, ClawTheme.colors.border), - contentColor = ClawTheme.colors.textMuted, - ) { - Text(text = text, modifier = Modifier.padding(horizontal = 4.dp, vertical = 0.5.dp), style = ClawTheme.type.caption.copy(fontSize = 12.5.sp, lineHeight = 16.sp), maxLines = 1) - } -} diff --git a/apps/android/app/src/main/java/ai/openclaw/app/ui/ShellScreen.kt b/apps/android/app/src/main/java/ai/openclaw/app/ui/ShellScreen.kt index 27153c89990c..c8f31a1afadd 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/ui/ShellScreen.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/ui/ShellScreen.kt @@ -345,14 +345,12 @@ private fun OverviewScreen( val sessions by viewModel.chatSessions.collectAsState() val pendingRunCount by viewModel.pendingRunCount.collectAsState() val statusText by viewModel.statusText.collectAsState() - val models by viewModel.modelCatalog.collectAsState() val providers by viewModel.modelAuthProviders.collectAsState() val pendingToolCalls by viewModel.chatPendingToolCalls.collectAsState() val cronStatus by viewModel.cronStatus.collectAsState() val nodesDevicesSummary by viewModel.nodesDevicesSummary.collectAsState() val channelsSummary by viewModel.channelsSummary.collectAsState() - val readyProviderCount = readyModelProviderCount(providers, models) - val expiringProviderCount = expiringModelProviderCount(providers) + val readyProviderCount = providers.count { modelProviderReady(it.status) } val attentionRows = homeAttentionRows( isConnected = isConnected, @@ -360,7 +358,6 @@ private fun OverviewScreen( channelsSummary = channelsSummary, nodesDevicesSummary = nodesDevicesSummary, readyProviderCount = readyProviderCount, - expiringProviderCount = expiringProviderCount, ) LaunchedEffect(isConnected) { @@ -471,8 +468,6 @@ private fun OverviewScreen( when { !isConnected -> "Offline" readyProviderCount > 0 -> "$readyProviderCount ready" - expiringProviderCount > 0 -> "$expiringProviderCount expiring" - models.isNotEmpty() -> "${models.size} models" else -> "Setup" }, icon = Icons.Outlined.Inventory2,