






















@@ -8,20 +8,27 @@ import ai.openclaw.app.ui.design.ClawSecondaryButton
88import ai.openclaw.app.ui.design.ClawStatus
99import ai.openclaw.app.ui.design.ClawStatusPill
1010import ai.openclaw.app.ui.design.ClawTheme
11+import androidx.activity.compose.BackHandler
12+import androidx.compose.foundation.clickable
1113import androidx.compose.foundation.layout.Arrangement
1214import androidx.compose.foundation.layout.Column
1315import androidx.compose.foundation.layout.PaddingValues
1416import androidx.compose.foundation.layout.Row
1517import androidx.compose.foundation.layout.fillMaxWidth
1618import androidx.compose.foundation.layout.padding
1719import androidx.compose.material.icons.Icons
20+import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
1821import androidx.compose.material.icons.filled.Settings
1922import androidx.compose.material3.HorizontalDivider
23+import androidx.compose.material3.Icon
2024import androidx.compose.material3.Text
2125import androidx.compose.runtime.Composable
2226import androidx.compose.runtime.LaunchedEffect
2327import androidx.compose.runtime.collectAsState
2428import androidx.compose.runtime.getValue
29+import androidx.compose.runtime.mutableStateOf
30+import androidx.compose.runtime.remember
31+import androidx.compose.runtime.setValue
2532import androidx.compose.ui.Alignment
2633import androidx.compose.ui.Modifier
2734import androidx.compose.ui.text.style.TextOverflow
@@ -43,6 +50,7 @@ internal fun HealthLogsSettingsScreen(
4350val logsSummary by viewModel.healthLogsSummary.collectAsState()
4451val logsRefreshing by viewModel.healthLogsRefreshing.collectAsState()
4552val logsErrorText by viewModel.healthLogsErrorText.collectAsState()
53+var selectedLogEntry by remember { mutableStateOf<GatewayLogEntry?>(null) }
46544755LaunchedEffect(isConnected) {
4856if (isConnected) {
@@ -52,6 +60,11 @@ internal fun HealthLogsSettingsScreen(
5260 }
5361 }
546263+ selectedLogEntry?.let { entry ->
64+GatewayLogDetailSettingsScreen(entry = entry, onBack = { selectedLogEntry = null })
65+return
66+ }
67+5568SettingsDetailFrame(
5669 title = "Health",
5770 subtitle = "Gateway status, phone node readiness, and recent log stream.",
@@ -93,7 +106,46 @@ internal fun HealthLogsSettingsScreen(
93106Text(text = error, style = ClawTheme.type.body, color = ClawTheme.colors.warning)
94107 }
95108 }
96-GatewayLogsPanel(isConnected = isConnected, summary = logsSummary)
109+GatewayLogsPanel(isConnected = isConnected, summary = logsSummary, onLogClick = { selectedLogEntry = it })
110+ }
111+}
112+113+@Composable
114+private fun GatewayLogDetailSettingsScreen(
115+entry: GatewayLogEntry,
116+onBack: () -> Unit,
117+) {
118+BackHandler(onBack = onBack)
119+SettingsDetailFrame(
120+ title = "Log Entry",
121+ subtitle = "Readable gateway log detail.",
122+ icon = Icons.Default.Settings,
123+ onBack = onBack,
124+ ) {
125+SettingsMetricPanel(
126+ rows =
127+listOf(
128+SettingsMetric("Time", compactLogTime(entry.time)),
129+SettingsMetric("Level", entry.level?.uppercase() ?: "LOG"),
130+SettingsMetric("Subsystem", entry.subsystem ?: "Unknown"),
131+ ),
132+ )
133+ClawPanel {
134+Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
135+Text(text = "Message", style = ClawTheme.type.section, color = ClawTheme.colors.text)
136+Text(text = entry.message, style = ClawTheme.type.body, color = ClawTheme.colors.text)
137+ }
138+ }
139+ClawPanel {
140+Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
141+Text(text = "Raw", style = ClawTheme.type.section, color = ClawTheme.colors.text)
142+Text(
143+ text = entry.raw.take(4_000),
144+ style = ClawTheme.type.caption,
145+ color = ClawTheme.colors.textMuted,
146+ )
147+ }
148+ }
97149 }
98150}
99151@@ -148,6 +200,7 @@ private fun HealthStatusRow(
148200private fun GatewayLogsPanel(
149201isConnected: Boolean,
150202summary: GatewayHealthLogsSummary,
203+onLogClick: (GatewayLogEntry) -> Unit,
151204) {
152205Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
153206Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
@@ -170,7 +223,7 @@ private fun GatewayLogsPanel(
170223val entries = summary.entries.takeLast(12)
171224Column {
172225 entries.forEachIndexed { index, entry ->
173-GatewayLogRow(entry = entry)
226+GatewayLogRow(entry = entry, onClick = { onLogClick(entry) })
174227if (index != entries.lastIndex) {
175228HorizontalDivider(color = ClawTheme.colors.border, thickness = 1.dp)
176229 }
@@ -185,9 +238,16 @@ private fun GatewayLogsPanel(
185238}
186239187240@Composable
188-private fun GatewayLogRow(entry: GatewayLogEntry) {
241+private fun GatewayLogRow(
242+entry: GatewayLogEntry,
243+onClick: () -> Unit,
244+) {
189245Row(
190- modifier = Modifier.fillMaxWidth().padding(horizontal = 10.dp, vertical = 7.dp),
246+ modifier =
247+Modifier
248+ .fillMaxWidth()
249+ .clickable(onClickLabel = "Open log entry", onClick = onClick)
250+ .padding(horizontal = 10.dp, vertical = 7.dp),
191251 verticalAlignment = Alignment.Top,
192252 horizontalArrangement = Arrangement.spacedBy(9.dp),
193253 ) {
@@ -199,6 +259,11 @@ private fun GatewayLogRow(entry: GatewayLogEntry) {
199259 }
200260 }
201261ClawStatusPill(text = entry.level?.uppercase() ?: "LOG", status = logLevelStatus(entry.level))
262+Icon(
263+ imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
264+ contentDescription = null,
265+ tint = ClawTheme.colors.textSubtle,
266+ )
202267 }
203268}
204269此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。