@@ -898,32 +898,38 @@ private fun SettingsShellScreen(
898898 ProfilePanel (displayName = displayName.ifBlank { " OpenClaw" }, onClick = { onRouteChange(SettingsRoute .Profile ) })
899899 }
900900
901- item {
902- SettingsGroup (
903- rows =
904- listOf (
905- SettingsRow (" Profile" , displayName.ifBlank { " Local device" }, Icons .Default .Person , route = SettingsRoute .Profile ),
906- SettingsRow (" Voice" , if (speakerEnabled) " Speaker on" else " Speaker muted" , Icons .Default .Mic , route = SettingsRoute .Voice ),
907- SettingsRow (" Agents" , if (agents.isEmpty()) " Load from gateway" else " ${agents.size} available" , Icons .Default .Person , status = agents.isNotEmpty(), route = SettingsRoute .Agents ),
908- SettingsRow (" Approvals" , approvalsSummary(pendingToolCalls.size), Icons .Default .Lock , status = approvalsStatus(pendingToolCalls.size), route = SettingsRoute .Approvals ),
909- SettingsRow (" Cron Jobs" , cronJobsSummary(cronStatus.jobs), Icons .Outlined .AccessTime , status = if (cronStatus.jobs > 0 ) cronStatus.enabled else null , route = SettingsRoute .CronJobs ),
910- SettingsRow (" Usage" , usageSummaryText(usageSummary.providers.size), Icons .Default .Storage , status = if (usageSummary.providers.isNotEmpty()) true else null , route = SettingsRoute .Usage ),
911- SettingsRow (" Skills" , skillsSummaryText(skillsSummary.skills), Icons .Default .Settings , status = skillsStatus(skillsSummary.skills), route = SettingsRoute .Skills ),
912- SettingsRow (" Nodes & Devices" , nodesDevicesSummaryText(nodesDevicesSummary), Icons .Default .Cloud , status = nodesDevicesStatus(nodesDevicesSummary), route = SettingsRoute .NodesDevices ),
913- SettingsRow (" Channels" , channelsSummaryText(channelsSummary), Icons .Default .Notifications , status = channelsStatus(channelsSummary), route = SettingsRoute .Channels ),
914- SettingsRow (" Dreaming" , dreamingSummaryText(dreamingSummary), Icons .Default .Storage , status = dreamingStatus(dreamingSummary), route = SettingsRoute .Dreaming ),
915- SettingsRow (" Canvas" , " Screen surface" , Icons .AutoMirrored .Filled .ScreenShare , status = isConnected, route = SettingsRoute .Canvas ),
916- SettingsRow (" Notifications" , if (notificationForwardingEnabled) " Smart delivery" else " Off" , Icons .Default .Notifications , route = SettingsRoute .Notifications ),
917- SettingsRow (" Phone Capabilities" , if (cameraEnabled) " Camera enabled" else " Locked" , Icons .Default .Lock , status = ! cameraEnabled, route = SettingsRoute .PhoneCapabilities ),
918- SettingsRow (" Gateway" , gatewaySummary(statusText, isConnected), Icons .Default .Cloud , status = isConnected, route = SettingsRoute .Gateway ),
919- SettingsRow (" Appearance" , appearanceThemeSummary(appearanceThemeMode), Icons .Default .Palette , route = SettingsRoute .Appearance ),
920- SettingsRow (" Health" , " Diagnostics" , Icons .Default .Settings , status = isConnected, route = SettingsRoute .Health ),
921- SettingsRow (" About" , " Version and update" , Icons .Default .Storage , route = SettingsRoute .About ),
922- ),
923- onOpen = onRouteChange,
901+ val settingsRows =
902+ listOf (
903+ SettingsRow (" Gateway" , gatewaySummary(statusText, isConnected), Icons .Default .Cloud , status = isConnected, route = SettingsRoute .Gateway ),
904+ SettingsRow (" Nodes & Devices" , nodesDevicesSummaryText(nodesDevicesSummary), Icons .Default .Cloud , status = nodesDevicesStatus(nodesDevicesSummary), route = SettingsRoute .NodesDevices ),
905+ SettingsRow (" Channels" , channelsSummaryText(channelsSummary), Icons .Default .Notifications , status = channelsStatus(channelsSummary), route = SettingsRoute .Channels ),
906+ SettingsRow (" Agents" , if (agents.isEmpty()) " Load from gateway" else " ${agents.size} available" , Icons .Default .Person , status = agents.isNotEmpty(), route = SettingsRoute .Agents ),
907+ SettingsRow (" Approvals" , approvalsSummary(pendingToolCalls.size), Icons .Default .Lock , status = approvalsStatus(pendingToolCalls.size), route = SettingsRoute .Approvals ),
908+ SettingsRow (" Cron Jobs" , cronJobsSummary(cronStatus.jobs), Icons .Outlined .AccessTime , status = if (cronStatus.jobs > 0 ) cronStatus.enabled else null , route = SettingsRoute .CronJobs ),
909+ SettingsRow (" Usage" , usageSummaryText(usageSummary.providers.size), Icons .Default .Storage , status = if (usageSummary.providers.isNotEmpty()) true else null , route = SettingsRoute .Usage ),
910+ SettingsRow (" Skills" , skillsSummaryText(skillsSummary.skills), Icons .Default .Settings , status = skillsStatus(skillsSummary.skills), route = SettingsRoute .Skills ),
911+ SettingsRow (" Dreaming" , dreamingSummaryText(dreamingSummary), Icons .Default .Storage , status = dreamingStatus(dreamingSummary), route = SettingsRoute .Dreaming ),
912+ SettingsRow (" Voice" , if (speakerEnabled) " Speaker on" else " Speaker muted" , Icons .Default .Mic , route = SettingsRoute .Voice ),
913+ SettingsRow (" Canvas" , " Screen surface" , Icons .AutoMirrored .Filled .ScreenShare , status = isConnected, route = SettingsRoute .Canvas ),
914+ SettingsRow (" Notifications" , if (notificationForwardingEnabled) " Smart delivery" else " Off" , Icons .Default .Notifications , route = SettingsRoute .Notifications ),
915+ SettingsRow (" Phone Capabilities" , if (cameraEnabled) " Camera enabled" else " Locked" , Icons .Default .Lock , status = ! cameraEnabled, route = SettingsRoute .PhoneCapabilities ),
916+ SettingsRow (" Appearance" , appearanceThemeSummary(appearanceThemeMode), Icons .Default .Palette , route = SettingsRoute .Appearance ),
917+ SettingsRow (" About" , " Version and update" , Icons .Default .Storage , route = SettingsRoute .About ),
918+ SettingsRow (" Health" , " Diagnostics" , Icons .Default .Settings , status = isConnected, route = SettingsRoute .Health ),
924919 )
920+
921+ settingsSections(settingsRows).forEach { section ->
922+ item {
923+ SettingsSectionTitle (section.title)
924+ }
925+ item {
926+ SettingsGroup (rows = section.rows, onOpen = onRouteChange)
927+ }
925928 }
926929
930+ item {
931+ SettingsSectionTitle (" Account" )
932+ }
927933 item {
928934 SettingsGroup (
929935 rows = listOf (SettingsRow (" Sign Out" , " Disconnect" , Icons .AutoMirrored .Filled .ExitToApp )),
@@ -1057,14 +1063,73 @@ private fun dreamingStatus(summary: GatewayDreamingSummary): Boolean? =
10571063 else -> null
10581064 }
10591065
1060- private data class SettingsRow (
1066+ internal data class SettingsRow (
10611067 val title : String ,
10621068 val value : String ,
10631069 val icon : ImageVector ,
10641070 val status : Boolean? = null ,
10651071 val route : SettingsRoute ? = null ,
10661072)
10671073
1074+ internal data class SettingsSection (
1075+ val title : String ,
1076+ val rows : List <SettingsRow >,
1077+ )
1078+
1079+ internal fun settingsSections (rows : List <SettingsRow >): List <SettingsSection > =
1080+ settingsSectionOrder.mapNotNull { title ->
1081+ val sectionRows = rows.filter { row -> row.route?.let (::settingsSectionTitleForRoute) == title }
1082+ if (sectionRows.isEmpty()) null else SettingsSection (title = title, rows = sectionRows)
1083+ }
1084+
1085+ private val settingsSectionOrder =
1086+ listOf (
1087+ " Connection" ,
1088+ " Agents & automation" ,
1089+ " Phone context & privacy" ,
1090+ " Profile & device" ,
1091+ " Diagnostics" ,
1092+ )
1093+
1094+ internal fun settingsSectionTitleForRoute (route : SettingsRoute ): String =
1095+ when (route) {
1096+ SettingsRoute .Gateway ,
1097+ SettingsRoute .NodesDevices ,
1098+ SettingsRoute .Channels ,
1099+ -> " Connection"
1100+
1101+ SettingsRoute .Agents ,
1102+ SettingsRoute .Approvals ,
1103+ SettingsRoute .CronJobs ,
1104+ SettingsRoute .Usage ,
1105+ SettingsRoute .Skills ,
1106+ SettingsRoute .Dreaming ,
1107+ -> " Agents & automation"
1108+
1109+ SettingsRoute .Voice ,
1110+ SettingsRoute .Canvas ,
1111+ SettingsRoute .Notifications ,
1112+ SettingsRoute .PhoneCapabilities ,
1113+ -> " Phone context & privacy"
1114+
1115+ SettingsRoute .Profile ,
1116+ SettingsRoute .Appearance ,
1117+ SettingsRoute .About ,
1118+ -> " Profile & device"
1119+
1120+ SettingsRoute .Health -> " Diagnostics"
1121+ SettingsRoute .Home -> " Diagnostics"
1122+ }
1123+
1124+ @Composable
1125+ private fun SettingsSectionTitle (title : String ) {
1126+ Text (
1127+ text = title.uppercase(),
1128+ style = ClawTheme .type.caption.copy(fontSize = 12 .sp, lineHeight = 16 .sp),
1129+ color = ClawTheme .colors.textMuted,
1130+ )
1131+ }
1132+
10681133@Composable
10691134private fun ProfilePanel (
10701135 displayName : String ,
0 commit comments