



















@@ -24,10 +24,10 @@ import androidx.compose.foundation.shape.CircleShape
2424import androidx.compose.foundation.shape.RoundedCornerShape
2525import androidx.compose.material.icons.Icons
2626import androidx.compose.material.icons.filled.KeyboardArrowDown
27-import androidx.compose.material.icons.filled.MoreVert
2827import androidx.compose.material.icons.filled.Search
2928import androidx.compose.material.icons.filled.StarBorder
3029import androidx.compose.material.icons.filled.Storage
30+import androidx.compose.material.icons.filled.SwapVert
3131import androidx.compose.material.icons.outlined.AccessTime
3232import androidx.compose.material.icons.outlined.ChatBubbleOutline
3333import androidx.compose.material.icons.outlined.MicNone
@@ -39,6 +39,9 @@ import androidx.compose.runtime.Composable
3939import androidx.compose.runtime.LaunchedEffect
4040import androidx.compose.runtime.collectAsState
4141import androidx.compose.runtime.getValue
42+import androidx.compose.runtime.mutableStateOf
43+import androidx.compose.runtime.saveable.rememberSaveable
44+import androidx.compose.runtime.setValue
4245import androidx.compose.ui.Alignment
4346import androidx.compose.ui.Modifier
4447import androidx.compose.ui.draw.clip
@@ -57,6 +60,23 @@ internal fun V2SessionsScreen(
5760val sessions by viewModel.chatSessions.collectAsState()
5861val chatSessionKey by viewModel.chatSessionKey.collectAsState()
5962val isConnected by viewModel.isConnected.collectAsState()
63+var filter by rememberSaveable { mutableStateOf(V2SessionFilter.Recent) }
64+var compactLayout by rememberSaveable { mutableStateOf(false) }
65+var recentFirst by rememberSaveable { mutableStateOf(true) }
66+val visibleSessions =
67+ sessions
68+ .let { rows ->
69+when (filter) {
70+V2SessionFilter.Recent -> rows
71+V2SessionFilter.Live -> rows.filter { it.key == chatSessionKey }
72+ }
73+ }.let { rows ->
74+if (recentFirst) {
75+ rows.sortedByDescending { it.updatedAtMs ?: 0L }
76+ } else {
77+ rows.sortedBy { it.updatedAtMs ?: 0L }
78+ }
79+ }
60806181LaunchedEffect(isConnected) {
6282if (isConnected) {
@@ -74,51 +94,48 @@ internal fun V2SessionsScreen(
7494 ) {
7595Text(text = "Sessions", style = ClawTheme.type.display.copy(fontSize = 17.4.sp, lineHeight = 21.sp), color = ClawTheme.colors.text, modifier = Modifier.weight(1f))
7696V2SessionPlainIconButton(icon = Icons.Default.Search, contentDescription = "Search sessions", onClick = onOpenCommand)
77-V2SessionPlainIconButton(icon = Icons.Default.MoreVert, contentDescription = "Session options", onClick = {})
97+V2SessionPlainIconButton(icon = Icons.Default.SwapVert, contentDescription = "Reverse session sort", onClick = { recentFirst = !recentFirst })
7898 }
7999 }
8010081101 item {
82102Row(horizontalArrangement = Arrangement.spacedBy(5.dp)) {
83-V2FilterPill(text = "Recent", icon = Icons.Outlined.AccessTime, active = true)
84-V2FilterPill(text = "Live", icon = Icons.Outlined.MicNone, active = false, live = true)
85-V2FilterPill(text = "Pinned", icon = Icons.Default.StarBorder, active = false)
103+V2FilterPill(text = "Recent", icon = Icons.Outlined.AccessTime, active = filter == V2SessionFilter.Recent, onClick = { filter = V2SessionFilter.Recent })
104+V2FilterPill(text = "Live", icon = Icons.Outlined.MicNone, active = filter == V2SessionFilter.Live, live = visibleSessions.any { it.key == chatSessionKey }, onClick = { filter = V2SessionFilter.Live })
86105 }
87106 }
8810789108 item {
90109Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
91-Row(horizontalArrangement = Arrangement.spacedBy(5.dp)) {
92-V2FilterPill(text = "Channel", dropdown = true)
93-V2FilterPill(text = "Agent", dropdown = true)
110+Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
111+Text(text = "Sort: ${if (recentFirst) "Newest" else "Oldest"}", style = ClawTheme.type.body, color = ClawTheme.colors.textMuted)
112+Icon(imageVector = Icons.Default.KeyboardArrowDown, contentDescription = null, modifier = Modifier.size(11.dp), tint = ClawTheme.colors.textMuted)
94113 }
95-V2SessionOutlineIconButton(icon = Icons.Default.Storage, contentDescription = "Session layout", onClick = {})
114+V2SessionOutlineIconButton(icon = Icons.Default.Storage, contentDescription = "Toggle session layout", onClick = { compactLayout = !compactLayout })
96115 }
97116 }
9811799118 item {
100-Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
101-Text(text = "Sort: Recent", style = ClawTheme.type.body, color = ClawTheme.colors.textMuted)
102-Icon(imageVector = Icons.Default.KeyboardArrowDown, contentDescription = null, modifier = Modifier.size(11.dp), tint = ClawTheme.colors.textMuted)
103- }
119+Text(text = if (compactLayout) "Layout: Compact" else "Layout: Detailed", style = ClawTheme.type.caption, color = ClawTheme.colors.textSubtle)
104120 }
105121106-if (sessions.isEmpty()) {
122+if (visibleSessions.isEmpty()) {
107123 item {
108124ClawEmptyState(
109- title = "No sessions yet",
110- body = "Start a new conversation and it will show up here.",
125+ title = emptySessionTitle(filter),
126+ body = emptySessionBody(filter),
111127 action = { ClawPrimaryButton(text = "Start Chat", onClick = onOpenChat) },
112128 )
113129 }
114130 } else {
115- items(sessions, key = { it.key }) { session ->
131+ items(visibleSessions, key = { it.key }) { session ->
116132val active = session.key == chatSessionKey
117133V2SessionRow(
118134 title = displaySessionTitle(session.displayName),
119135 subtitle = if (active) "Current session" else "OpenClaw session",
120136 metadata = session.updatedAtMs?.let(::relativeSessionTime) ?: "now",
121137 active = active,
138+ compact = compactLayout,
122139 onClick = {
123140 viewModel.switchChatSession(session.key)
124141 onOpenChat()
@@ -141,8 +158,11 @@ private fun V2FilterPill(
141158active: Boolean = false,
142159live: Boolean = false,
143160dropdown: Boolean = false,
161+onClick: (() -> Unit)? = null,
144162) {
145163Surface(
164+ onClick = onClick ?: {},
165+ enabled = onClick != null,
146166 shape = RoundedCornerShape(7.dp),
147167 color = if (active) ClawTheme.colors.surfaceRaised else Color.Transparent,
148168 contentColor = ClawTheme.colors.text,
@@ -171,6 +191,7 @@ private fun V2SessionRow(
171191subtitle: String,
172192metadata: String,
173193active: Boolean,
194+compact: Boolean,
174195onClick: () -> Unit,
175196) {
176197Surface(onClick = onClick, color = ClawTheme.colors.canvas, contentColor = ClawTheme.colors.text) {
@@ -210,15 +231,17 @@ private fun V2SessionRow(
210231Box(modifier = Modifier.size(3.5.dp).clip(CircleShape).background(ClawTheme.colors.success))
211232 }
212233 }
213-Text(text = subtitle, style = ClawTheme.type.caption.copy(fontSize = 12.5.sp, lineHeight = 16.sp), color = ClawTheme.colors.textMuted, maxLines = 1)
214-Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
215-V2SessionMiniTag(text = "Workspace")
216-V2SessionMiniTag(text = if (active) "Active" else "OpenClaw")
234+if (!compact) {
235+Text(text = subtitle, style = ClawTheme.type.caption.copy(fontSize = 12.5.sp, lineHeight = 16.sp), color = ClawTheme.colors.textMuted, maxLines = 1)
236+Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
237+V2SessionMiniTag(text = "Workspace")
238+V2SessionMiniTag(text = if (active) "Active" else "OpenClaw")
239+ }
217240 }
218241 }
219242220243Column(horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.spacedBy(5.dp)) {
221-Icon(imageVector = Icons.Default.MoreVert, contentDescription = "Session options", modifier = Modifier.size(13.dp), tint = ClawTheme.colors.textMuted)
244+Icon(imageVector = Icons.Outlined.ChatBubbleOutline, contentDescription = null, modifier = Modifier.size(13.dp), tint = ClawTheme.colors.textMuted)
222245Text(text = metadata, style = ClawTheme.type.caption.copy(fontSize = 12.5.sp, lineHeight = 16.sp), color = ClawTheme.colors.textMuted, maxLines = 1)
223246 }
224247 }
@@ -272,6 +295,23 @@ private fun V2SessionMiniTag(text: String) {
272295 }
273296}
274297298+private enum class V2SessionFilter {
299+Recent,
300+Live,
301+}
302+303+private fun emptySessionTitle(filter: V2SessionFilter): String =
304+when (filter) {
305+V2SessionFilter.Recent -> "No sessions yet"
306+V2SessionFilter.Live -> "No live session"
307+ }
308+309+private fun emptySessionBody(filter: V2SessionFilter): String =
310+when (filter) {
311+V2SessionFilter.Recent -> "Start a new conversation and it will show up here."
312+V2SessionFilter.Live -> "Open Chat to start or resume the current session."
313+ }
314+275315private fun relativeSessionTime(updatedAtMs: Long): String {
276316val deltaMs = (System.currentTimeMillis() - updatedAtMs).coerceAtLeast(0L)
277317val minutes = deltaMs / 60_000L
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。