diff --git a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt
index 5bb3d838..938e09ce 100644
--- a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt
+++ b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt
@@ -54,14 +54,12 @@ import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material.icons.filled.Search
-import androidx.compose.material.icons.filled.Stop
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.FilledTonalButton
-import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -106,6 +104,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.m57.hermescontrol.NavigationController
import com.m57.hermescontrol.R
+import com.m57.hermescontrol.data.ws.CommandCatalog
import com.m57.hermescontrol.data.ws.ConnectionStatus
import com.m57.hermescontrol.notification.NotificationHelper
import com.m57.hermescontrol.theme.StatusRed
@@ -294,9 +293,9 @@ fun ChatScreen(
}
}
},
- onInterrupt = viewModel::interruptSession,
isAgentTyping = state.isAgentTyping,
isConnected = state.isConnected,
+ commandCatalog = state.commandCatalog,
)
}
}
@@ -365,9 +364,9 @@ private fun ChatInputBar(
inputText: String,
onInputChange: (String) -> Unit,
onSend: () -> Unit,
- onInterrupt: () -> Unit,
isAgentTyping: Boolean,
isConnected: Boolean,
+ commandCatalog: CommandCatalog,
) {
// Allow sending slash commands even while agent is typing
val isSlashCommand = inputText.startsWith("/")
@@ -396,23 +395,18 @@ private fun ChatInputBar(
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp),
) {
Column {
- val commands =
- listOf(
- "/help",
- "/status",
- "/sessions",
- "/stats",
- "/system",
- "/new",
- "/stop",
- "/interrupt",
- )
+ // Build command list from dynamic catalog (pairs = [name, description])
+ val allCommands = commandCatalog.pairs.associate { (name, desc) -> name to desc }
+ // Commands hidden from suggestion menu (still work when manually typed)
+ val hiddenSlashDisplay = setOf("/stop", "/interrupt")
+ val commandNames = allCommands.keys.filter { it !in hiddenSlashDisplay }
+
androidx.compose.animation.AnimatedVisibility(
visible = inputText.startsWith("/") && !inputText.contains(" "),
enter = androidx.compose.animation.fadeIn() + androidx.compose.animation.expandVertically(),
exit = androidx.compose.animation.fadeOut() + androidx.compose.animation.shrinkVertically(),
) {
- val filteredCommands = commands.filter { it.startsWith(inputText, ignoreCase = true) }
+ val filteredCommands = commandNames.filter { it.startsWith(inputText, ignoreCase = true) }
if (filteredCommands.isNotEmpty()) {
androidx.compose.material3.Surface(
modifier =
@@ -431,18 +425,7 @@ private fun ChatInputBar(
modifier = Modifier.heightIn(max = 200.dp),
) {
items(filteredCommands, key = { it }) { cmd ->
- val description =
- when (cmd) {
- "/help" -> stringResource(R.string.command_help_desc)
- "/status" -> stringResource(R.string.command_status_desc)
- "/sessions" -> stringResource(R.string.command_sessions_desc)
- "/stats" -> stringResource(R.string.command_stats_desc)
- "/system" -> stringResource(R.string.command_system_desc)
- "/new" -> stringResource(R.string.command_new_desc)
- "/stop" -> stringResource(R.string.command_stop_desc)
- "/interrupt" -> stringResource(R.string.command_stop_desc)
- else -> ""
- }
+ val description = allCommands[cmd] ?: ""
DropdownMenuItem(
text = {
Row(
@@ -527,26 +510,6 @@ private fun ChatInputBar(
contentDescription = stringResource(R.string.chat_send_desc),
)
}
-
- // Compact stop icon — visible only while agent is streaming
- if (isAgentTyping) {
- Spacer(modifier = Modifier.width(4.dp))
- FilledTonalIconButton(
- onClick = onInterrupt,
- modifier =
- Modifier
- .size(32.dp)
- .testTag("interrupt_button"),
- shape = CircleShape,
- ) {
- Icon(
- imageVector = Icons.Filled.Stop,
- contentDescription = stringResource(R.string.content_desc_interrupt),
- modifier = Modifier.size(18.dp),
- tint = MaterialTheme.colorScheme.error,
- )
- }
- }
}
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1563b208..2a6a522f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -91,16 +91,8 @@
Tool
Show Raw JSON
Show Parsed Output
- Show help menu
- Check gateway and platform status
- List all chat sessions
- Check system resource usage (stats)
- Check system resource usage (system)
- Create a new chat session
- Interrupt the active run
New Chat
Scroll to bottom
- Interrupt
Copy
Edit