diff --git a/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelDialog.kt b/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelDialog.kt
new file mode 100644
index 00000000..0ab0b789
--- /dev/null
+++ b/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelDialog.kt
@@ -0,0 +1,46 @@
+package com.redmadrobot.debug.uikit.components
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogProperties
+import com.redmadrobot.debug.uikit.theme.DebugPanelShapes
+import com.redmadrobot.debug.uikit.theme.DebugPanelTheme
+
+@Composable
+public fun PanelDialog(
+ title: String,
+ onDismiss: () -> Unit,
+ modifier: Modifier = Modifier,
+ content: @Composable ColumnScope.() -> Unit,
+) {
+ Dialog(
+ onDismissRequest = onDismiss,
+ properties = DialogProperties(usePlatformDefaultWidth = false),
+ ) {
+ Surface(
+ shape = DebugPanelShapes.dialog,
+ color = DebugPanelTheme.colors.surface.dialog,
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(horizontal = 24.dp),
+ ) {
+ Column(modifier = Modifier.padding(all = 24.dp)) {
+ Text(
+ text = title,
+ style = DebugPanelTheme.typography.titleLarge,
+ color = DebugPanelTheme.colors.content.primary,
+ modifier = Modifier.padding(bottom = 16.dp),
+ )
+ content()
+ }
+ }
+ }
+}
diff --git a/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelSearchBar.kt b/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelSearchBar.kt
new file mode 100644
index 00000000..066ac3ac
--- /dev/null
+++ b/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelSearchBar.kt
@@ -0,0 +1,94 @@
+package com.redmadrobot.debug.uikit.components
+
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import com.redmadrobot.debug.uikit.R
+import com.redmadrobot.debug.uikit.theme.DebugPanelDimensions
+import com.redmadrobot.debug.uikit.theme.DebugPanelShapes
+import com.redmadrobot.debug.uikit.theme.DebugPanelTheme
+import com.redmadrobot.debug.uikit.theme.MonoFontFamily
+
+@Suppress("LongMethod")
+@Composable
+public fun PanelSearchBar(
+ query: String,
+ placeholder: String,
+ onQueryChange: (String) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val minHeight by animateDpAsState(
+ targetValue = if (query.isNotEmpty()) 48.dp else 40.dp,
+ label = "",
+ )
+
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .heightIn(min = minHeight)
+ .background(
+ color = DebugPanelTheme.colors.surface.secondary,
+ shape = DebugPanelShapes.medium,
+ )
+ .padding(horizontal = 12.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ painter = painterResource(R.drawable.icon_search),
+ contentDescription = null,
+ tint = DebugPanelTheme.colors.content.tertiary,
+ modifier = Modifier.size(size = DebugPanelDimensions.iconSizeSmall),
+ )
+ BasicTextField(
+ value = query,
+ onValueChange = onQueryChange,
+ modifier = Modifier
+ .weight(weight = 1f)
+ .padding(horizontal = 8.dp),
+ textStyle = DebugPanelTheme.typography.bodyMedium.copy(
+ fontFamily = MonoFontFamily,
+ color = DebugPanelTheme.colors.content.primary,
+ ),
+ singleLine = true,
+ cursorBrush = SolidColor(value = DebugPanelTheme.colors.content.accent),
+ decorationBox = { innerTextField ->
+ if (query.isEmpty()) {
+ Text(
+ text = placeholder,
+ style = DebugPanelTheme.typography.bodyMedium,
+ color = DebugPanelTheme.colors.content.tertiary,
+ )
+ }
+ innerTextField()
+ },
+ )
+ if (query.isNotEmpty()) {
+ IconButton(
+ onClick = { onQueryChange("") },
+ modifier = Modifier.size(size = DebugPanelDimensions.iconSizeLarge),
+ ) {
+ Icon(
+ painter = painterResource(R.drawable.icon_clear),
+ contentDescription = null,
+ tint = DebugPanelTheme.colors.content.tertiary,
+ modifier = Modifier.size(size = DebugPanelDimensions.iconSizeSmall),
+ )
+ }
+ }
+ }
+}
diff --git a/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelStyledTextField.kt b/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelStyledTextField.kt
new file mode 100644
index 00000000..28c9f38d
--- /dev/null
+++ b/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelStyledTextField.kt
@@ -0,0 +1,65 @@
+package com.redmadrobot.debug.uikit.components
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextFieldColors
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.dp
+import com.redmadrobot.debug.uikit.theme.DebugPanelTheme
+
+private val defaultTextStyle: TextStyle
+ @Composable get() = DebugPanelTheme.typography.bodyMedium.copy(
+ color = DebugPanelTheme.colors.content.primary,
+ )
+
+private val defaultColors: TextFieldColors
+ @Composable get() = OutlinedTextFieldDefaults.colors(
+ focusedBorderColor = DebugPanelTheme.colors.content.accent,
+ unfocusedBorderColor = DebugPanelTheme.colors.stroke.secondary,
+ focusedLabelColor = DebugPanelTheme.colors.content.accent,
+ unfocusedLabelColor = DebugPanelTheme.colors.content.tertiary,
+ errorBorderColor = DebugPanelTheme.colors.content.error,
+ cursorColor = DebugPanelTheme.colors.content.accent,
+ )
+
+@Composable
+public fun PanelStyledTextField(
+ value: String,
+ label: String,
+ onValueChange: (String) -> Unit,
+ modifier: Modifier = Modifier,
+ isError: Boolean = false,
+ errorMessage: String? = null,
+ keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+ textStyle: TextStyle = defaultTextStyle,
+ colors: TextFieldColors = defaultColors
+) {
+ Column(modifier = modifier.fillMaxWidth()) {
+ OutlinedTextField(
+ value = value,
+ onValueChange = onValueChange,
+ modifier = Modifier.fillMaxWidth(),
+ label = { Text(text = label, style = DebugPanelTheme.typography.bodyMedium) },
+ isError = isError,
+ singleLine = true,
+ keyboardOptions = keyboardOptions,
+ textStyle = textStyle,
+ colors = colors,
+ )
+ if (isError && errorMessage != null) {
+ Text(
+ text = errorMessage,
+ style = DebugPanelTheme.typography.bodySmall,
+ color = DebugPanelTheme.colors.content.error,
+ modifier = Modifier.padding(top = 4.dp),
+ )
+ }
+ }
+}
diff --git a/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelToggle.kt b/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelToggle.kt
new file mode 100644
index 00000000..64416640
--- /dev/null
+++ b/panel-ui-kit/src/main/kotlin/com/redmadrobot/debug/uikit/components/PanelToggle.kt
@@ -0,0 +1,50 @@
+package com.redmadrobot.debug.uikit.components
+
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.redmadrobot.debug.uikit.theme.DebugPanelDimensions
+import com.redmadrobot.debug.uikit.theme.DebugPanelTheme
+
+@Composable
+public fun PanelToggle(
+ checked: Boolean,
+ onCheckedChange: (Boolean) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val (trackColor, thumbOffset) = with(DebugPanelTheme.colors) {
+ if (checked) content.teal to 22.dp else stroke.primary to 2.dp
+ }
+ val animatedTrackColor by animateColorAsState(targetValue = trackColor, label = "")
+ val animatedThumbOffset by animateDpAsState(targetValue = thumbOffset, label = "")
+
+ Box(
+ modifier = modifier
+ .width(width = DebugPanelDimensions.toggleWidth)
+ .height(height = DebugPanelDimensions.toggleHeight)
+ .clip(shape = RoundedCornerShape(size = 12.dp))
+ .background(color = animatedTrackColor)
+ .clickable { onCheckedChange(!checked) },
+ ) {
+ Box(
+ modifier = Modifier
+ .padding(start = animatedThumbOffset, top = 2.dp)
+ .size(size = 20.dp)
+ .background(color = Color.White, shape = CircleShape),
+ )
+ }
+}
diff --git a/panel-ui-kit/src/main/res/drawable/icon_clear.xml b/panel-ui-kit/src/main/res/drawable/icon_clear.xml
new file mode 100644
index 00000000..95cc170f
--- /dev/null
+++ b/panel-ui-kit/src/main/res/drawable/icon_clear.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/panel-ui-kit/src/main/res/drawable/icon_search.xml b/panel-ui-kit/src/main/res/drawable/icon_search.xml
new file mode 100644
index 00000000..afe352ee
--- /dev/null
+++ b/panel-ui-kit/src/main/res/drawable/icon_search.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/EditConfigValueDialog.kt b/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/EditConfigValueDialog.kt
index 9a6bdbda..6d3cc923 100644
--- a/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/EditConfigValueDialog.kt
+++ b/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/EditConfigValueDialog.kt
@@ -1,15 +1,15 @@
package com.redmadrobot.debug.plugin.konfeature.ui
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material.AlertDialog
-import androidx.compose.material.Button
-import androidx.compose.material.Checkbox
-import androidx.compose.material.OutlinedTextField
-import androidx.compose.material.Text
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
@@ -18,13 +18,16 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import com.redmadrobot.debug.plugin.konfeature.R
import com.redmadrobot.debug.plugin.konfeature.ui.data.EditDialogState
-import com.redmadrobot.debug.core.R as CoreR
+import com.redmadrobot.debug.uikit.components.PanelDialog
+import com.redmadrobot.debug.uikit.components.PanelStyledTextField
+import com.redmadrobot.debug.uikit.components.PanelToggle
+import com.redmadrobot.debug.uikit.theme.DebugPanelShapes
+import com.redmadrobot.debug.uikit.theme.DebugPanelTheme
@Composable
internal fun EditConfigValueDialog(
@@ -32,6 +35,7 @@ internal fun EditConfigValueDialog(
onValueChange: (key: String, value: Any) -> Unit,
onValueReset: (key: String) -> Unit,
onDismissRequest: () -> Unit,
+ modifier: Modifier = Modifier,
) {
val initialValue = state.value
var value by remember { mutableStateOf(state.value) }
@@ -40,41 +44,40 @@ internal fun EditConfigValueDialog(
derivedStateOf { !isInputEmpty && initialValue != value }
}
- AlertDialog(
- backgroundColor = colorResource(id = CoreR.color.super_light_gray),
- title = {
- Text(text = stringResource(id = R.string.konfeature_plugin_edit_dialog_title, state.key))
- },
- text = {
- when (initialValue) {
- is Boolean -> BooleanEditInput(initialValue, onValueChange = { value = it })
- is Long -> LongEditInput(
- initialValue,
- onValueChange = { value = it },
- onEmptyInput = { isInputEmpty = it }
- )
-
- is Double -> DoubleEditInput(
- initialValue,
- onValueChange = { value = it },
- onEmptyImput = { isInputEmpty = it }
- )
-
- is String -> StringEditInput(initialValue, onValueChange = { value = it })
- }
- },
- onDismissRequest = onDismissRequest,
- buttons = {
- EditConfigValueButtons(
- state = state,
- saveEnabled = saveEnabled,
- value = value,
- onValueChange = onValueChange,
- onValueReset = onValueReset,
- onDismissRequest = onDismissRequest,
+ PanelDialog(title = state.key, onDismiss = onDismissRequest, modifier = modifier) {
+ when (initialValue) {
+ is Boolean -> BooleanEditInput(
+ value = initialValue,
+ onValueChange = { value = it },
+ )
+
+ is Long -> LongEditInput(
+ value = initialValue,
+ onValueChange = { value = it },
+ onEmptyInput = { isInputEmpty = it },
+ )
+
+ is Double -> DoubleEditInput(
+ value = initialValue,
+ onValueChange = { value = it },
+ onEmptyInput = { isInputEmpty = it },
+ )
+
+ is String -> StringEditInput(
+ value = initialValue,
+ onValueChange = { value = it },
)
}
- )
+ Spacer(modifier = Modifier.height(20.dp))
+ EditConfigValueButtons(
+ state = state,
+ saveEnabled = saveEnabled,
+ value = value,
+ onValueChange = onValueChange,
+ onValueReset = onValueReset,
+ onDismissRequest = onDismissRequest,
+ )
+ }
}
@Composable
@@ -85,58 +88,114 @@ private fun EditConfigValueButtons(
onValueChange: (key: String, value: Any) -> Unit,
onValueReset: (key: String) -> Unit,
onDismissRequest: () -> Unit,
+ modifier: Modifier = Modifier,
) {
Row(
- modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
+ modifier = modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
) {
- Button(onClick = onDismissRequest) {
- Text(text = stringResource(id = R.string.konfeature_plugin_close))
- }
- Spacer(modifier = Modifier.weight(1f))
- Button(
- enabled = saveEnabled,
- onClick = {
- onValueChange.invoke(state.key, value)
- onDismissRequest.invoke()
- }
- ) {
- Text(text = stringResource(id = R.string.konfeature_plugin_save))
- }
+ CloseButton(onDismissRequest = onDismissRequest)
+ Spacer(modifier = Modifier.weight(weight = 1f))
if (state.isDebugSource) {
- Button(
- modifier = Modifier.padding(start = 8.dp),
- onClick = {
- onValueReset.invoke(state.key)
- onDismissRequest.invoke()
- }
- ) {
- Text(text = stringResource(id = R.string.konfeature_plugin_reset))
- }
+ DebugSourceButton(
+ onValueReset = { onValueReset.invoke(state.key) },
+ onDismissRequest = onDismissRequest
+ )
}
+ SaveButton(
+ saveEnabled = saveEnabled,
+ onValueChange = { onValueChange.invoke(state.key, value) },
+ onDismissRequest = onDismissRequest
+ )
+ }
+}
+
+@Composable
+private fun SaveButton(
+ saveEnabled: Boolean,
+ onValueChange: () -> Unit,
+ onDismissRequest: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ Button(
+ modifier = modifier,
+ onClick = {
+ onValueChange()
+ onDismissRequest()
+ },
+ enabled = saveEnabled,
+ shape = DebugPanelShapes.medium,
+ ) {
+ Text(
+ text = stringResource(R.string.konfeature_plugin_save),
+ style = DebugPanelTheme.typography.labelLarge,
+ )
+ }
+}
+
+@Composable
+private fun DebugSourceButton(
+ onValueReset: () -> Unit,
+ onDismissRequest: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ OutlinedButton(
+ modifier = modifier,
+ onClick = {
+ onValueReset()
+ onDismissRequest()
+ },
+ shape = DebugPanelShapes.medium,
+ colors = ButtonDefaults.outlinedButtonColors(
+ contentColor = DebugPanelTheme.colors.content.error,
+ ),
+ ) {
+ Text(
+ text = stringResource(R.string.konfeature_plugin_reset),
+ style = DebugPanelTheme.typography.labelLarge,
+ )
+ }
+}
+
+@Composable
+private fun CloseButton(onDismissRequest: () -> Unit, modifier: Modifier = Modifier) {
+ OutlinedButton(
+ modifier = modifier,
+ onClick = onDismissRequest,
+ shape = DebugPanelShapes.medium,
+ ) {
+ Text(
+ text = stringResource(R.string.konfeature_plugin_close),
+ style = DebugPanelTheme.typography.labelLarge,
+ )
}
}
@Composable
private fun BooleanEditInput(
value: Boolean,
- onValueChange: (Any) -> Unit
+ onValueChange: (Any) -> Unit,
+ modifier: Modifier = Modifier,
) {
var checked by remember { mutableStateOf(value) }
+
Row(
- modifier = Modifier.fillMaxWidth()
+ modifier = modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
) {
Text(
- modifier = Modifier
- .weight(1f)
- .align(Alignment.CenterVertically),
- text = stringResource(id = R.string.konfeature_plugin_edit_dialog_hint_boolean)
+ text = stringResource(R.string.konfeature_plugin_edit_dialog_hint_boolean),
+ style = DebugPanelTheme.typography.bodyMedium,
+ color = DebugPanelTheme.colors.content.primary,
+ modifier = Modifier.weight(weight = 1f),
)
- Checkbox(
+ PanelToggle(
checked = checked,
onCheckedChange = { newChecked ->
checked = newChecked
- onValueChange.invoke(newChecked)
- }
+ onValueChange(newChecked)
+ },
)
}
}
@@ -146,22 +205,23 @@ private fun LongEditInput(
value: Long,
onValueChange: (Any) -> Unit,
onEmptyInput: (Boolean) -> Unit,
+ modifier: Modifier = Modifier,
) {
var text by remember { mutableStateOf(value.toString()) }
- OutlinedTextField(
- modifier = Modifier.fillMaxWidth(),
- label = { Text(text = stringResource(id = R.string.konfeature_plugin_edit_dialog_hint_long)) },
+ PanelStyledTextField(
value = text,
onValueChange = { newText ->
val newValue = newText.toLongOrNull()
if (newValue != null || newText.isEmpty()) {
text = newText
newValue?.let(onValueChange)
- onEmptyInput.invoke(newText.isEmpty())
+ onEmptyInput(newText.isEmpty())
}
},
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
+ label = stringResource(R.string.konfeature_plugin_edit_dialog_hint_long),
+ modifier = modifier,
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
)
}
@@ -169,23 +229,24 @@ private fun LongEditInput(
private fun DoubleEditInput(
value: Double,
onValueChange: (Any) -> Unit,
- onEmptyImput: (Boolean) -> Unit,
+ onEmptyInput: (Boolean) -> Unit,
+ modifier: Modifier = Modifier,
) {
var text by remember { mutableStateOf(value.toBigDecimal().toPlainString()) }
- OutlinedTextField(
- modifier = Modifier.fillMaxWidth(),
- label = { Text(text = stringResource(id = R.string.konfeature_plugin_edit_dialog_hint_double)) },
+ PanelStyledTextField(
value = text,
onValueChange = { newText ->
val newValue = newText.toDoubleOrNull()
if (newValue != null || newText.isEmpty()) {
text = newText
newValue?.let(onValueChange)
- onEmptyImput.invoke(newText.isEmpty())
+ onEmptyInput(newText.isEmpty())
}
},
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal)
+ label = stringResource(R.string.konfeature_plugin_edit_dialog_hint_double),
+ modifier = modifier,
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
)
}
@@ -193,16 +254,17 @@ private fun DoubleEditInput(
private fun StringEditInput(
value: String,
onValueChange: (Any) -> Unit,
+ modifier: Modifier = Modifier,
) {
var text by remember { mutableStateOf(value) }
- OutlinedTextField(
- modifier = Modifier.fillMaxWidth(),
- label = { Text(text = stringResource(id = R.string.konfeature_plugin_edit_dialog_hint_string)) },
+ PanelStyledTextField(
value = text,
onValueChange = { newText ->
text = newText
- onValueChange.invoke(newText)
+ onValueChange(newText)
},
+ label = stringResource(R.string.konfeature_plugin_edit_dialog_hint_string),
+ modifier = modifier,
)
}
diff --git a/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/KonfeatureScreen.kt b/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/KonfeatureScreen.kt
index 384ccede..c8a6bc63 100644
--- a/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/KonfeatureScreen.kt
+++ b/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/KonfeatureScreen.kt
@@ -1,29 +1,29 @@
package com.redmadrobot.debug.plugin.konfeature.ui
-import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
+import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material.Button
-import androidx.compose.material.Divider
-import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
-import androidx.compose.material.OutlinedTextField
-import androidx.compose.material.Text
-import androidx.compose.material.TextFieldDefaults
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@@ -34,9 +34,13 @@ import com.redmadrobot.debug.plugin.konfeature.KonfeaturePluginContainer
import com.redmadrobot.debug.plugin.konfeature.R
import com.redmadrobot.debug.plugin.konfeature.ui.data.KonfeatureItem
import com.redmadrobot.debug.plugin.konfeature.ui.data.KonfeatureViewState
-import com.redmadrobot.debug.core.R as CoreR
+import com.redmadrobot.debug.uikit.components.PanelSearchBar
+import com.redmadrobot.debug.uikit.components.PanelToggle
+import com.redmadrobot.debug.uikit.theme.DebugPanelDimensions
+import com.redmadrobot.debug.uikit.theme.DebugPanelShapes
+import com.redmadrobot.debug.uikit.theme.DebugPanelTheme
+import com.redmadrobot.debug.uikit.theme.MonoFontFamily
-@OptIn(ExperimentalMaterialApi::class)
@Composable
internal fun KonfeatureScreen(
viewModel: KonfeatureViewModel = provideViewModel {
@@ -45,7 +49,7 @@ internal fun KonfeatureScreen(
.createKonfeatureViewModel()
},
) {
- val state by viewModel.state.collectAsState(KonfeatureViewState())
+ val state by viewModel.state.collectAsState()
KonfeatureLayout(
state = state,
@@ -55,6 +59,7 @@ internal fun KonfeatureScreen(
onHeaderClick = viewModel::onConfigHeaderClick,
onEditClick = viewModel::onEditClick,
onSearchQueryChange = viewModel::onSearchQueryChanged,
+ onBooleanToggle = viewModel::onValueChanged,
)
state.editDialogState?.let { dialogState ->
@@ -62,12 +67,11 @@ internal fun KonfeatureScreen(
state = dialogState,
onValueChange = viewModel::onValueChanged,
onValueReset = viewModel::onValueReset,
- onDismissRequest = viewModel::onEditDialogCloseClicked
+ onDismissRequest = viewModel::onEditDialogCloseClicked,
)
}
}
-@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun KonfeatureLayout(
state: KonfeatureViewState,
@@ -77,170 +81,330 @@ internal fun KonfeatureLayout(
onResetAllClick: () -> Unit,
onHeaderClick: (String) -> Unit,
onSearchQueryChange: (String) -> Unit,
+ onBooleanToggle: (String, Boolean) -> Unit,
+ modifier: Modifier = Modifier,
) {
- LazyColumn {
- stickyHeader {
- KonfeatureHeader(
- searchQuery = state.searchQuery,
- onSearchQueryChange = onSearchQueryChange,
- onRefreshClick = onRefreshClick,
- onCollapseAllClick = onCollapseAllClick,
- onResetAllClick = onResetAllClick,
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .background(color = DebugPanelTheme.colors.background.primary)
+ ) {
+ ToolbarChips(
+ onRefreshClick = onRefreshClick,
+ onCollapseAllClick = onCollapseAllClick,
+ onResetAllClick = onResetAllClick,
+ )
+ PanelSearchBar(
+ query = state.searchQuery,
+ onQueryChange = onSearchQueryChange,
+ placeholder = stringResource(R.string.konfeature_plugin_search_hint),
+ modifier = Modifier.padding(horizontal = 12.dp),
+ )
+ AnimatedVisibility(visible = state.shouldShowEmptySearchItemsHint) {
+ Text(
+ text = stringResource(R.string.konfeature_plugin_search_empty),
+ style = DebugPanelTheme.typography.bodyMedium,
+ color = DebugPanelTheme.colors.content.tertiary,
+ modifier = Modifier.padding(all = 16.dp),
)
}
+ LazyColumn(modifier = Modifier.weight(weight = 1f)) {
+ konfeatureItems(
+ state = state,
+ onHeaderClick = onHeaderClick,
+ onEditClick = onEditClick,
+ onBooleanToggle = onBooleanToggle
+ )
+ }
+ }
+}
- if (state.shouldShowEmptySearchItemsHint) {
- item {
- Text(
- text = stringResource(R.string.konfeature_plugin_search_empty),
- modifier = Modifier.padding(16.dp)
+private fun LazyListScope.konfeatureItems(
+ state: KonfeatureViewState,
+ onHeaderClick: (String) -> Unit,
+ onEditClick: (String, Any, Boolean) -> Unit,
+ onBooleanToggle: (String, Boolean) -> Unit,
+) {
+ items(
+ items = state.filteredItems,
+ key = { item ->
+ when (item) {
+ is KonfeatureItem.Config -> "config_${item.name}"
+ is KonfeatureItem.Value -> "value_${item.key}"
+ }
+ },
+ ) { item ->
+ when (item) {
+ is KonfeatureItem.Config -> {
+ val isCollapsed = !state.isSearchActive && item.name in state.collapsedConfigs
+ val overrideCount = state.values.count { value ->
+ value.configName == item.name && value.isDebugSource
+ }
+ ConfigGroupHeader(
+ name = item.description.takeIf { it.isNotEmpty() } ?: item.name,
+ overrideCount = overrideCount,
+ isCollapsed = isCollapsed,
+ onClick = { onHeaderClick(item.name) },
)
}
- }
- state.filteredItems.forEach { item ->
- if (item is KonfeatureItem.Config) {
- item(item.name) {
- ConfigItem(
+ is KonfeatureItem.Value -> {
+ val isVisible = state.isSearchActive || item.configName !in state.collapsedConfigs
+ if (isVisible) {
+ ConfigValueItem(
item = item,
- isCollapsed = !state.isSearchActive && item.name in state.collapsedConfigs,
- onHeaderClick = onHeaderClick
+ onEditClick = onEditClick,
+ onBooleanToggle = onBooleanToggle,
)
}
}
-
- val isExpanded = state.isSearchActive || item is KonfeatureItem.Value &&
- item.configName !in state.collapsedConfigs
- if (item is KonfeatureItem.Value && isExpanded) {
- item(item.key) { ValueItem(item = item, onEditClick) }
- item { Divider(modifier = Modifier.fillMaxWidth()) }
- }
}
}
}
@Composable
-private fun KonfeatureHeader(
- searchQuery: String,
- onSearchQueryChange: (String) -> Unit,
+private fun ToolbarChips(
onRefreshClick: () -> Unit,
onCollapseAllClick: () -> Unit,
onResetAllClick: () -> Unit,
modifier: Modifier = Modifier,
) {
- Column(
+ Row(
modifier = modifier
- .background(colorResource(id = CoreR.color.super_light_gray))
- .padding(horizontal = 16.dp)
+ .fillMaxWidth()
+ .padding(horizontal = 12.dp, vertical = 12.dp),
+ horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
- KonfeatureSearchBar(
- query = searchQuery,
- onQueryChange = onSearchQueryChange,
- modifier = Modifier.padding(vertical = 8.dp)
+ ActionChip(
+ label = stringResource(R.string.konfeature_plugin_refresh),
+ onClick = onRefreshClick,
+ )
+ ActionChip(
+ label = stringResource(R.string.konfeature_plugin_collapse_all),
+ onClick = onCollapseAllClick,
+ )
+ ActionChip(
+ label = stringResource(R.string.konfeature_plugin_reset_all),
+ onClick = onResetAllClick,
)
- Row {
- Button(onClick = onRefreshClick) {
- Text(text = stringResource(id = R.string.konfeature_plugin_refresh))
- }
- Spacer(modifier = Modifier.weight(1f))
- Button(onClick = onCollapseAllClick) {
- Text(text = stringResource(id = R.string.konfeature_plugin_collapse_all))
- }
- Spacer(modifier = Modifier.weight(1f))
- Button(onClick = onResetAllClick) {
- Text(text = stringResource(id = R.string.konfeature_plugin_reset_all))
- }
- }
}
}
@Composable
-private fun KonfeatureSearchBar(
- query: String,
- onQueryChange: (String) -> Unit,
- modifier: Modifier = Modifier
+private fun ActionChip(
+ label: String,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
) {
- OutlinedTextField(
- value = query,
- onValueChange = onQueryChange,
- modifier = modifier.fillMaxWidth(),
- placeholder = {
- Text(text = stringResource(R.string.konfeature_plugin_search_hint))
- },
- leadingIcon = {
- Icon(
- painter = painterResource(R.drawable.icon_search),
- contentDescription = null
+ Text(
+ text = label,
+ style = DebugPanelTheme.typography.labelLarge,
+ color = DebugPanelTheme.colors.content.secondary,
+ modifier = modifier
+ .clip(shape = DebugPanelShapes.medium)
+ .border(
+ width = 1.dp,
+ color = DebugPanelTheme.colors.stroke.primary,
+ shape = DebugPanelShapes.medium,
)
- },
- trailingIcon = {
- if (query.isNotEmpty()) {
- IconButton(onClick = { onQueryChange("") }) {
- Icon(
- painter = painterResource(R.drawable.icon_clear),
- contentDescription = stringResource(R.string.konfeature_plugin_search_clear)
- )
- }
- }
- },
- singleLine = true,
- colors = TextFieldDefaults.outlinedTextFieldColors(
- backgroundColor = Color.White
- )
+ .clickable(onClick = onClick)
+ .padding(horizontal = 12.dp, vertical = 4.dp),
)
}
@Composable
-private fun ConfigItem(
+private fun ConfigGroupHeader(
+ name: String,
+ overrideCount: Int,
isCollapsed: Boolean,
- item: KonfeatureItem.Config,
- onHeaderClick: (String) -> Unit
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
) {
Row(
- modifier = Modifier
+ modifier = modifier
.fillMaxWidth()
- .clickable { onHeaderClick.invoke(item.name) }
- .background(colorResource(id = CoreR.color.super_light_gray))
- .padding(horizontal = 16.dp, vertical = 8.dp)
+ .clip(shape = DebugPanelShapes.medium)
+ .clickable(onClick = onClick)
+ .padding(all = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
- Text(
- modifier = Modifier.weight(1f),
- text = item.description.takeIf { it.isNotEmpty() } ?: item.name
- )
- val icon = if (isCollapsed) R.drawable.icon_keyboard_arrow_up else R.drawable.icon_keyboard_arrow_down
-
Icon(
- painter = painterResource(icon),
- modifier = Modifier.align(Alignment.CenterVertically),
- contentDescription = null
+ painter = painterResource(
+ id = if (isCollapsed) {
+ R.drawable.icon_keyboard_arrow_up
+ } else {
+ R.drawable.icon_keyboard_arrow_down
+ }
+ ),
+ contentDescription = null,
+ tint = DebugPanelTheme.colors.content.tertiary,
+ modifier = Modifier.size(size = DebugPanelDimensions.iconSizeSmall),
+ )
+ Text(
+ text = name,
+ style = DebugPanelTheme.typography.titleMedium,
+ color = DebugPanelTheme.colors.content.primary,
+ modifier = Modifier.weight(weight = 1f),
)
+ if (overrideCount > 0) {
+ Text(
+ text = overrideCount.toString(),
+ style = DebugPanelTheme.typography.labelSmall,
+ color = DebugPanelTheme.colors.content.accent,
+ modifier = Modifier
+ .background(
+ color = DebugPanelTheme.colors.surface.tertiary,
+ shape = DebugPanelShapes.small,
+ )
+ .padding(horizontal = 8.dp, vertical = 2.dp),
+ )
+ }
}
}
@Composable
-internal fun ValueItem(item: KonfeatureItem.Value, onEditClick: (String, Any, Boolean) -> Unit) {
+private fun ConfigValueItem(
+ item: KonfeatureItem.Value,
+ onEditClick: (String, Any, Boolean) -> Unit,
+ onBooleanToggle: (String, Boolean) -> Unit,
+ modifier: Modifier = Modifier,
+) {
Row(
- modifier = Modifier
+ modifier = modifier
.fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 4.dp)
+ .padding(start = 32.dp, end = 8.dp)
+ .padding(vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
- Column(Modifier.weight(1f)) {
- Text(text = item.description)
- Text(text = stringResource(id = R.string.konfeature_plugin_item_key, item.key))
- Text(text = stringResource(id = R.string.konfeature_plugin_item_value, item.value.toString()))
- Text(
- color = item.sourceColor,
- text = stringResource(id = R.string.konfeature_plugin_item_source, item.sourceName)
+ ValueInfoColumn(
+ item = item,
+ modifier = Modifier.weight(weight = 1f),
+ )
+
+ when {
+ item.value is Boolean -> PanelToggle(
+ checked = item.value,
+ onCheckedChange = { newValue -> onBooleanToggle(item.key, newValue) },
+ )
+
+ item.editAvailable -> EditButton(
+ onClick = { onEditClick(item.key, item.value, item.isDebugSource) },
)
}
+ }
+}
- if (item.editAvailable) {
- IconButton(
- modifier = Modifier.align(alignment = Alignment.CenterVertically),
- onClick = { onEditClick.invoke(item.key, item.value, item.isDebugSource) }
- ) {
- Icon(painterResource(R.drawable.icon_edit), contentDescription = null)
- }
+@Composable
+private fun ValueInfoColumn(
+ item: KonfeatureItem.Value,
+ modifier: Modifier = Modifier,
+) {
+ Column(modifier = modifier) {
+ Text(
+ text = item.key,
+ style = DebugPanelTheme.typography.bodyMedium.copy(fontFamily = MonoFontFamily),
+ color = DebugPanelTheme.colors.content.secondary,
+ )
+ if (item.description.isNotEmpty()) {
+ Text(
+ modifier = Modifier.padding(top = 4.dp),
+ text = item.description,
+ style = DebugPanelTheme.typography.bodyMedium.copy(fontFamily = MonoFontFamily),
+ color = DebugPanelTheme.colors.content.tertiary,
+ )
+ }
+ if (item.value is Boolean) {
+ ValueSourceLabel(item = item, modifier = Modifier.padding(top = 8.dp))
+ } else {
+ ValueWithSource(item = item)
}
}
}
+
+@Composable
+private fun ValueWithSource(
+ item: KonfeatureItem.Value,
+ modifier: Modifier = Modifier,
+) {
+ Row(
+ modifier = modifier.padding(top = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
+ ) {
+ Text(
+ text = formatValue(value = item.value),
+ style = DebugPanelTheme.typography.labelMedium,
+ color = sourceColor(item = item),
+ )
+ ValueSourceLabel(item = item)
+ }
+}
+
+@Composable
+private fun ValueSourceLabel(
+ item: KonfeatureItem.Value,
+ modifier: Modifier = Modifier,
+) {
+ when {
+ item.isDebugSource -> SourceLabel(
+ source = item.sourceName,
+ isDebug = true,
+ modifier = modifier,
+ )
+
+ item.sourceName != "Default" -> SourceLabel(
+ source = item.sourceName,
+ isDebug = false,
+ modifier = modifier,
+ )
+ }
+}
+
+@Composable
+private fun EditButton(
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ IconButton(
+ onClick = onClick,
+ modifier = modifier.size(size = DebugPanelDimensions.iconSizeLarge),
+ ) {
+ Icon(
+ painter = painterResource(R.drawable.icon_edit),
+ contentDescription = null,
+ tint = DebugPanelTheme.colors.content.accent,
+ modifier = Modifier.size(size = DebugPanelDimensions.iconSizeSmall),
+ )
+ }
+}
+
+@Composable
+private fun SourceLabel(
+ source: String,
+ isDebug: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ Text(
+ text = source,
+ style = DebugPanelTheme.typography.labelMedium,
+ color = if (isDebug) {
+ DebugPanelTheme.colors.content.teal
+ } else {
+ DebugPanelTheme.colors.source.remoteText
+ },
+ modifier = modifier,
+ )
+}
+
+private fun formatValue(value: Any): String {
+ return if (value is String) "\"$value\"" else value.toString()
+}
+
+@Composable
+private fun sourceColor(item: KonfeatureItem.Value): Color = when {
+ item.isDebugSource -> DebugPanelTheme.colors.content.teal
+ item.sourceName != "Default" -> DebugPanelTheme.colors.source.remoteText
+ else -> DebugPanelTheme.colors.content.tertiary
+}
diff --git a/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/KonfeatureViewModel.kt b/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/KonfeatureViewModel.kt
index 49abcdd6..ec720919 100644
--- a/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/KonfeatureViewModel.kt
+++ b/plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/KonfeatureViewModel.kt
@@ -13,8 +13,8 @@ import com.redmadrobot.konfeature.Konfeature
import com.redmadrobot.konfeature.source.FeatureValueSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
@@ -32,7 +32,7 @@ internal class KonfeatureViewModel(
private val _state = MutableStateFlow(KonfeatureViewState())
private val _searchQueryFlow = MutableStateFlow("")
- val state: Flow = _state.asStateFlow()
+ val state: StateFlow = _state.asStateFlow()
init {
observeKonfeatureValues()
diff --git a/plugins/plugin-servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersScreen.kt b/plugins/plugin-servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersScreen.kt
index 33afda59..d5b425f7 100644
--- a/plugins/plugin-servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersScreen.kt
+++ b/plugins/plugin-servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersScreen.kt
@@ -19,8 +19,6 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
@@ -44,6 +42,7 @@ import com.redmadrobot.debug.plugin.servers.ServersPlugin
import com.redmadrobot.debug.plugin.servers.ServersPluginContainer
import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
import com.redmadrobot.debug.uikit.components.PanelBottomSheet
+import com.redmadrobot.debug.uikit.components.PanelStyledTextField
import com.redmadrobot.debug.uikit.theme.DebugPanelDimensions
import com.redmadrobot.debug.uikit.theme.DebugPanelShapes
import com.redmadrobot.debug.uikit.theme.DebugPanelTheme
@@ -273,7 +272,7 @@ private fun ServerBottomSheet(
title = title,
onDismiss = onDismiss,
) {
- ServerTextField(
+ PanelStyledTextField(
value = state.serverName,
onValueChange = onNameChange,
label = stringResource(R.string.name),
@@ -281,7 +280,7 @@ private fun ServerBottomSheet(
errorMessage = state.inputErrors?.nameError?.let { stringResource(it) },
)
Spacer(modifier = Modifier.height(16.dp))
- ServerTextField(
+ PanelStyledTextField(
value = state.serverUrl,
onValueChange = onUrlChange,
label = stringResource(R.string.server_host_hint),
@@ -302,43 +301,3 @@ private fun ServerBottomSheet(
Spacer(modifier = Modifier.height(24.dp))
}
}
-
-@Composable
-private fun ServerTextField(
- value: String,
- label: String,
- onValueChange: (String) -> Unit,
- modifier: Modifier = Modifier,
- isError: Boolean = false,
- errorMessage: String? = null,
-) {
- Column(modifier = modifier.fillMaxWidth()) {
- OutlinedTextField(
- value = value,
- onValueChange = onValueChange,
- modifier = Modifier.fillMaxWidth(),
- label = { Text(label, style = DebugPanelTheme.typography.bodyMedium) },
- isError = isError,
- singleLine = true,
- textStyle = DebugPanelTheme.typography.bodyMedium.copy(
- color = DebugPanelTheme.colors.content.primary,
- ),
- colors = OutlinedTextFieldDefaults.colors(
- focusedBorderColor = DebugPanelTheme.colors.content.accent,
- unfocusedBorderColor = DebugPanelTheme.colors.stroke.secondary,
- focusedLabelColor = DebugPanelTheme.colors.content.accent,
- unfocusedLabelColor = DebugPanelTheme.colors.content.tertiary,
- errorBorderColor = DebugPanelTheme.colors.content.error,
- cursorColor = DebugPanelTheme.colors.content.accent,
- ),
- )
- if (isError && errorMessage != null) {
- Text(
- text = errorMessage,
- style = DebugPanelTheme.typography.bodySmall,
- color = DebugPanelTheme.colors.content.error,
- modifier = Modifier.padding(top = 4.dp),
- )
- }
- }
-}