fix: rewrite ToolEventsBlock to match web frontend logic
Parse tool events into thinking/analyzing/inProgress/completed states instead of showing all as generic "Tool" rows. Handles type "thinking" (shows "Verarbeitet…"), "analyzing" (shows filename), "start"/"end"/ "error" with proper icons and elapsed time. Empty/thinking-only events no longer show a confusing wrench icon.
This commit is contained in:
parent
cad445f87c
commit
5c54b9b9e3
3 changed files with 61 additions and 39 deletions
|
|
@ -26,7 +26,9 @@ import androidx.compose.material.icons.rounded.ExpandLess
|
||||||
import androidx.compose.material.icons.rounded.ExpandMore
|
import androidx.compose.material.icons.rounded.ExpandMore
|
||||||
import androidx.compose.material.icons.rounded.Language
|
import androidx.compose.material.icons.rounded.Language
|
||||||
import androidx.compose.material.icons.rounded.Psychology
|
import androidx.compose.material.icons.rounded.Psychology
|
||||||
|
import androidx.compose.material.icons.rounded.AutoAwesome
|
||||||
import androidx.compose.material.icons.rounded.Build
|
import androidx.compose.material.icons.rounded.Build
|
||||||
|
import androidx.compose.material.icons.rounded.Description
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -45,8 +47,10 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import dev.kaizen.app.net.SearchSource
|
import dev.kaizen.app.net.SearchSource
|
||||||
import dev.kaizen.app.net.ToolEvent
|
import dev.kaizen.app.net.ToolEvent
|
||||||
|
import dev.kaizen.app.R
|
||||||
import dev.kaizen.app.ui.shape.KaizenShapes
|
import dev.kaizen.app.ui.shape.KaizenShapes
|
||||||
import dev.kaizen.app.ui.theme.LocalKaizenAccent
|
import dev.kaizen.app.ui.theme.LocalKaizenAccent
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ReasoningBlock(reasoning: String, streaming: Boolean = false) {
|
fun ReasoningBlock(reasoning: String, streaming: Boolean = false) {
|
||||||
|
|
@ -103,51 +107,65 @@ fun ToolEventsBlock(tools: List<ToolEvent>, streaming: Boolean = false) {
|
||||||
val cs = MaterialTheme.colorScheme
|
val cs = MaterialTheme.colorScheme
|
||||||
val isDark = isSystemInDarkTheme()
|
val isDark = isSystemInDarkTheme()
|
||||||
val accent = LocalKaizenAccent.current
|
val accent = LocalKaizenAccent.current
|
||||||
val bg = if (isDark) Color.White.copy(alpha = 0.04f) else Color.Black.copy(alpha = 0.02f)
|
|
||||||
val borderColor = if (isDark) Color.White.copy(alpha = 0.06f) else Color.Black.copy(alpha = 0.04f)
|
|
||||||
|
|
||||||
Column(
|
val completed = mutableListOf<ToolEvent>()
|
||||||
Modifier
|
val inProgress = mutableListOf<String>()
|
||||||
.fillMaxWidth()
|
val seen = mutableSetOf<String>()
|
||||||
.clip(KaizenShapes.sm)
|
var thinking = false
|
||||||
.background(bg)
|
var analyzing: String? = null
|
||||||
.border(1.dp, borderColor, KaizenShapes.sm)
|
|
||||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
for (ev in tools) {
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
when (ev.type) {
|
||||||
) {
|
"thinking" -> { thinking = true; analyzing = null }
|
||||||
tools.forEach { tool ->
|
"analyzing" -> { analyzing = ev.name; thinking = false }
|
||||||
|
"start" -> {
|
||||||
|
analyzing = null; thinking = false
|
||||||
|
if (ev.name !in seen) { seen.add(ev.name); inProgress.add(ev.name) }
|
||||||
|
}
|
||||||
|
"end", "error" -> {
|
||||||
|
inProgress.remove(ev.name)
|
||||||
|
completed.add(ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val hasContent = analyzing != null || thinking || completed.isNotEmpty() || inProgress.isNotEmpty()
|
||||||
|
if (!hasContent) return
|
||||||
|
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
|
if (analyzing != null) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
val icon = when (tool.type) {
|
Icon(Icons.Rounded.Description, null, tint = accent.primary.copy(alpha = 0.6f), modifier = Modifier.size(14.dp))
|
||||||
"end" -> Icons.Rounded.Check
|
Spacer(Modifier.width(6.dp))
|
||||||
"error" -> Icons.Rounded.Error
|
Text(stringResource(R.string.stream_tool_analyzing, analyzing!!), color = cs.onSurfaceVariant.copy(alpha = 0.6f), fontSize = 12.sp)
|
||||||
else -> Icons.Rounded.Build
|
|
||||||
}
|
}
|
||||||
val tint = when (tool.type) {
|
|
||||||
"end" -> Color(0xFF10B981)
|
|
||||||
"error" -> Color(0xFFEF4444)
|
|
||||||
else -> accent.primary
|
|
||||||
}
|
}
|
||||||
|
if (thinking && analyzing == null && completed.isEmpty() && inProgress.isEmpty()) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Icon(Icons.Rounded.AutoAwesome, null, tint = accent.primary.copy(alpha = 0.6f), modifier = Modifier.size(14.dp))
|
||||||
|
Spacer(Modifier.width(6.dp))
|
||||||
|
Text(stringResource(R.string.stream_tool_thinking), color = cs.onSurfaceVariant.copy(alpha = 0.6f), fontSize = 12.sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
completed.forEach { tool ->
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
val icon = if (tool.type == "error") Icons.Rounded.Error else Icons.Rounded.Check
|
||||||
|
val tint = if (tool.type == "error") Color(0xFFEF4444) else Color(0xFF10B981)
|
||||||
Icon(icon, null, tint = tint, modifier = Modifier.size(14.dp))
|
Icon(icon, null, tint = tint, modifier = Modifier.size(14.dp))
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(6.dp))
|
||||||
Text(
|
Text(tool.name, color = cs.onSurfaceVariant.copy(alpha = 0.6f), fontSize = 12.sp)
|
||||||
tool.name.ifEmpty { "Tool" },
|
|
||||||
color = cs.onBackground,
|
|
||||||
fontSize = 13.sp,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
)
|
|
||||||
if (tool.elapsed != null) {
|
if (tool.elapsed != null) {
|
||||||
Spacer(Modifier.width(6.dp))
|
Spacer(Modifier.width(4.dp))
|
||||||
Text(
|
Text("${tool.elapsed}ms", color = cs.onSurfaceVariant.copy(alpha = 0.35f), fontSize = 11.sp)
|
||||||
"${tool.elapsed}ms",
|
|
||||||
color = cs.onSurfaceVariant.copy(alpha = 0.5f),
|
|
||||||
fontSize = 11.sp,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if (tool.error != null) {
|
|
||||||
Spacer(Modifier.width(6.dp))
|
|
||||||
Text(tool.error, color = Color(0xFFEF4444), fontSize = 11.sp, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
inProgress.forEach { name ->
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Icon(Icons.Rounded.Build, null, tint = accent.primary.copy(alpha = 0.6f), modifier = Modifier.size(14.dp))
|
||||||
|
Spacer(Modifier.width(6.dp))
|
||||||
|
Text(name, color = cs.onSurfaceVariant.copy(alpha = 0.7f), fontSize = 12.sp, fontWeight = FontWeight.Medium)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@
|
||||||
<!-- Stream blocks -->
|
<!-- Stream blocks -->
|
||||||
<string name="stream_reasoning">Reasoning</string>
|
<string name="stream_reasoning">Reasoning</string>
|
||||||
<string name="stream_sources">Sources</string>
|
<string name="stream_sources">Sources</string>
|
||||||
|
<string name="stream_tool_thinking">Processing …</string>
|
||||||
|
<string name="stream_tool_analyzing">Analyzing %1$s …</string>
|
||||||
|
|
||||||
<!-- Chat errors -->
|
<!-- Chat errors -->
|
||||||
<string name="error_session_expired">Session expired. Please sign in again.</string>
|
<string name="error_session_expired">Session expired. Please sign in again.</string>
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@
|
||||||
<!-- Stream blocks -->
|
<!-- Stream blocks -->
|
||||||
<string name="stream_reasoning">Gedankengang</string>
|
<string name="stream_reasoning">Gedankengang</string>
|
||||||
<string name="stream_sources">Quellen</string>
|
<string name="stream_sources">Quellen</string>
|
||||||
|
<string name="stream_tool_thinking">Verarbeitet …</string>
|
||||||
|
<string name="stream_tool_analyzing">Analysiert %1$s …</string>
|
||||||
|
|
||||||
<!-- Chat errors -->
|
<!-- Chat errors -->
|
||||||
<string name="error_session_expired">Sitzung abgelaufen. Bitte erneut anmelden.</string>
|
<string name="error_session_expired">Sitzung abgelaufen. Bitte erneut anmelden.</string>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue