1package soy.iko.opencode.ui.components
3import androidx.compose.foundation.layout.Arrangement
4import androidx.compose.foundation.layout.Column
5import androidx.compose.foundation.layout.Row
6import androidx.compose.foundation.layout.padding
7import androidx.compose.foundation.layout.size
8import androidx.compose.material3.Button
9import androidx.compose.material3.Icon
10import androidx.compose.material3.MaterialTheme
11import androidx.compose.material3.OutlinedButton
12import androidx.compose.material3.Text
13import androidx.compose.runtime.Composable
14import androidx.compose.ui.Alignment
15import androidx.compose.ui.Modifier
16import androidx.compose.ui.graphics.vector.ImageVector
17import androidx.compose.ui.platform.testTag
18import androidx.compose.ui.semantics.heading
19import androidx.compose.ui.semantics.semantics
20import androidx.compose.ui.text.style.TextAlign
21import androidx.compose.ui.unit.dp
24 * Shared empty/placeholder state: a centered icon, a title, an optional supporting line,
25 * and an optional call-to-action button. Extracted so every screen's "nothing here yet"
26 * state reads and behaves the same (see EmptySessions/EmptyServers/etc. which predate this).
28 * An optional [secondaryActionLabel] / [onSecondaryAction] pair renders a secondary
29 * OutlinedButton below the primary CTA, for cases where two recovery paths are equally
30 * valid (e.g. "Add server" vs "Import from QR image" on a first-run server list).
36 modifier: Modifier = Modifier,
37 description: String? = null,
38 actionIcon: ImageVector? = null,
39 actionLabel: String? = null,
40 onAction: (() -> Unit)? = null,
41 secondaryActionIcon: ImageVector? = null,
42 secondaryActionLabel: String? = null,
43 onSecondaryAction: (() -> Unit)? = null,
46 modifier = modifier.padding(32.dp),
47 horizontalAlignment = Alignment.CenterHorizontally,
48 verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically),
52 contentDescription = null,
53 modifier = Modifier.size(56.dp),
54 tint = MaterialTheme.colorScheme.onSurfaceVariant,
58 style = MaterialTheme.typography.titleMedium,
59 textAlign = TextAlign.Center,
60 modifier = Modifier.semantics { heading() },
62 if (description != null) {
65 style = MaterialTheme.typography.bodyMedium,
66 color = MaterialTheme.colorScheme.onSurfaceVariant,
67 textAlign = TextAlign.Center,
70 if (actionLabel != null && onAction != null) {
75 .testTag("empty_state_action"),
77 if (actionIcon != null) {
78 Icon(actionIcon, contentDescription = null, modifier = Modifier.size(18.dp))
79 Text(actionLabel, modifier = Modifier.padding(start = 6.dp))
85 if (secondaryActionLabel != null && onSecondaryAction != null) {
87 onClick = onSecondaryAction,
88 modifier = Modifier.testTag("empty_state_secondary_action"),
90 if (secondaryActionIcon != null) {
91 Icon(secondaryActionIcon, contentDescription = null, modifier = Modifier.size(18.dp))
92 Text(secondaryActionLabel, modifier = Modifier.padding(start = 6.dp))
94 Text(secondaryActionLabel)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list