Some small fix related to the network UI and some Shader preparation.

This commit is contained in:
Thomas Andres Gomez 2025-03-12 22:23:20 +01:00
parent 16b2b49f03
commit a93bb9d3f5
18 changed files with 196 additions and 76 deletions

View file

@ -33,6 +33,8 @@ kotlin {
// composable component. // composable component.
implementation(libs.coil.compose) implementation(libs.coil.compose)
implementation(libs.coil.network.ktor) implementation(libs.coil.network.ktor)
// implementation("com.mikepenz.hypnoticcanvas:hypnoticcanvas:0.3.0")
// implementation("com.mikepenz.hypnoticcanvas:hypnoticcanvas-shaders:0.3.0")
// network // network
implementation(libs.kotlinx.serialization.json) implementation(libs.kotlinx.serialization.json)
implementation(libs.ktor.serialization.json) implementation(libs.ktor.serialization.json)

View file

@ -78,7 +78,10 @@ fun BlurContent(
Box( Box(
modifier = Modifier modifier = Modifier
.matchParentSize() .matchParentSize()
.blur(radius = animatedBlur.value, edgeTreatment = BlurredEdgeTreatment.Unbounded) .blur(
radius = animatedBlur.value,
edgeTreatment = BlurredEdgeTreatment.Unbounded,
)
.drawWithContent { .drawWithContent {
drawContent() drawContent()
drawRect(color = animatedBackground.value) drawRect(color = animatedBackground.value)

View file

@ -2,6 +2,7 @@ package com.pixelized.desktop.lwa.ui.composable.shapes
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
@ -9,38 +10,48 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.theme.lwa import com.pixelized.desktop.lwa.ui.theme.lwa
import com.pixelized.desktop.lwa.ui.theme.shapes.ArrowShape import com.pixelized.desktop.lwa.ui.theme.shapes.ArrowShape
@Composable @Composable
fun ArrowShape() { fun ArrowShape(
val colorScheme = MaterialTheme.lwa.colorScheme modifier: Modifier = Modifier,
val arrow = remember { size: Dp = 24.dp,
ArrowShape( core: Dp = 4.dp,
core = 4.dp, head: Dp = 4.dp,
head = 4.dp, thickness: Dp = 1.dp,
) paddingValues: PaddingValues = PaddingValues(horizontal = 2.dp, vertical = 3.dp),
color: Color = MaterialTheme.lwa.colorScheme.base.primary,
) {
val arrow = remember(core, head) {
ArrowShape(core = core, head = head)
} }
Box( Box(
modifier = Modifier modifier = Modifier
.size(size = 24.dp) .size(size = size)
.padding(horizontal = 2.dp) .padding(
.padding(top = 3.dp, bottom = 3.dp) paddingValues = paddingValues,
.border(
width = 1.dp,
color = colorScheme.portrait.levelUp,
shape = arrow,
) )
.shadow( .border(
elevation = 1.dp, width = thickness,
color = color,
shape = arrow, shape = arrow,
) )
.shadow( .shadow(
elevation = 2.dp, elevation = 2.dp,
shape = arrow, shape = arrow,
ambientColor = colorScheme.portrait.levelUp, ambientColor = MaterialTheme.lwa.colorScheme.portrait.levelUp,
spotColor = colorScheme.portrait.levelUp, spotColor = MaterialTheme.lwa.colorScheme.portrait.levelUp,
)
.shadow(
elevation = 1.dp,
shape = arrow,
)
.then(
other = modifier,
) )
) )
} }

View file

@ -153,6 +153,7 @@ fun RollPage(
.graphicsLayer { .graphicsLayer {
this.alpha = 0.8f this.alpha = 0.8f
this.rotationZ = viewModel.rollRotation.value this.rotationZ = viewModel.rollRotation.value
}, },
tint = MaterialTheme.colors.primary, tint = MaterialTheme.colors.primary,
painter = painterResource(Res.drawable.ic_d20_24dp), painter = painterResource(Res.drawable.ic_d20_24dp),

View file

@ -29,7 +29,6 @@ import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.LocalRollHostState import com.pixelized.desktop.lwa.LocalRollHostState
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.overlay.roll.RollViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeader import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeader
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeaderUio import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.header.CharacterDetailHeaderUio
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheet import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.sheet.CharacterDetailSheet
@ -120,7 +119,6 @@ fun CharacterDetailPanel(
} }
} }
) )
} }
@Composable @Composable

View file

@ -90,7 +90,9 @@ fun CharacterDetailHeader(
Column( Column(
modifier = modifier, modifier = modifier,
) { ) {
Row(modifier = Modifier.padding(start = 16.dp)) { Row(
modifier = Modifier.padding(start = 16.dp),
) {
Row( Row(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)

View file

@ -1,6 +1,7 @@
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.EaseOutCirc import androidx.compose.animation.core.EaseOutCirc
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
@ -62,17 +63,10 @@ data class PlayerPortraitUio(
val levelUp: Boolean, val levelUp: Boolean,
) )
object PlayerPortrait {
object Default {
val bloodColor = Color(0xbb0a1e).copy(alpha = 0.25f)
}
}
@Composable @Composable
fun PlayerPortrait( fun PlayerPortrait(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
size: DpSize, size: DpSize,
bloodColor: Color = PlayerPortrait.Default.bloodColor,
character: PlayerPortraitUio, character: PlayerPortraitUio,
onCharacter: (id: Campaign.CharacterInstance.Id) -> Unit, onCharacter: (id: Campaign.CharacterInstance.Id) -> Unit,
onLevelUp: (id: Campaign.CharacterInstance.Id) -> Unit, onLevelUp: (id: Campaign.CharacterInstance.Id) -> Unit,
@ -101,17 +95,22 @@ fun PlayerPortrait(
} }
BloodOverlay( BloodOverlay(
bloodColor = bloodColor,
maxHp = character.maxHp.toFloat(), maxHp = character.maxHp.toFloat(),
hp = character.hp.toFloat(), hp = character.hp.toFloat(),
) )
if (character.levelUp) { AnimatedVisibility(
modifier = Modifier.offset(x = (-8).dp, y = (-8).dp),
visible = character.levelUp,
enter = fadeIn(),
exit = fadeOut(),
) {
IconButton( IconButton(
modifier = Modifier.offset(x = (-8).dp, y = (-8).dp),
onClick = { onLevelUp(character.id) }, onClick = { onLevelUp(character.id) },
) { ) {
ArrowShape() ArrowShape(
color = MaterialTheme.lwa.colorScheme.portrait.levelUp,
)
} }
} }
@ -176,9 +175,9 @@ fun PlayerPortrait(
@Composable @Composable
private fun BloodOverlay( private fun BloodOverlay(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
bloodColor: Color = MaterialTheme.lwa.colorScheme.portrait.blood,
maxHp: Float, maxHp: Float,
hp: Float, hp: Float,
bloodColor: Color = PlayerPortrait.Default.bloodColor,
) { ) {
val animatedRatio = animateFloatAsState( val animatedRatio = animateFloatAsState(
targetValue = min(maxHp, max(0f, (maxHp - hp) / maxHp)), targetValue = min(maxHp, max(0f, (maxHp - hp) / maxHp)),

View file

@ -64,10 +64,10 @@ import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.LevelUpDestination import com.pixelized.desktop.lwa.ui.navigation.screen.destination.LevelUpDestination
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult
import com.pixelized.desktop.lwa.ui.screen.levelup.skill.LevelUpCharacteristic import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristic
import com.pixelized.desktop.lwa.ui.screen.levelup.skill.LevelUpCharacteristicUio import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristicUio
import com.pixelized.desktop.lwa.ui.screen.levelup.skill.LevelUpSkill import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkill
import com.pixelized.desktop.lwa.ui.screen.levelup.skill.LevelUpSkillUio import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkillUio
import com.pixelized.desktop.lwa.ui.theme.lwa import com.pixelized.desktop.lwa.ui.theme.lwa
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.Res
@ -224,7 +224,9 @@ private fun LevelUpContent(
Text( Text(
style = MaterialTheme.lwa.typography.base.body1, style = MaterialTheme.lwa.typography.base.body1,
text = (header.value?.level ?: 0).let { text = (header.value?.level ?: 0).let {
stringResource(Res.string.level_up__character_level_description, it, it + 1) stringResource(
Res.string.level_up__character_level_description, it, it + 1
)
}, },
) )
} }

View file

@ -3,8 +3,8 @@ package com.pixelized.desktop.lwa.ui.screen.levelup
import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio import com.pixelized.desktop.lwa.ui.overlay.roll.RollAction.RollActionUio
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult
import com.pixelized.desktop.lwa.ui.screen.levelup.skill.LevelUpCharacteristicUio import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpCharacteristicUio
import com.pixelized.desktop.lwa.ui.screen.levelup.skill.LevelUpSkillUio import com.pixelized.desktop.lwa.ui.screen.levelup.items.LevelUpSkillUio
import com.pixelized.shared.lwa.model.AlteredCharacterSheet import com.pixelized.shared.lwa.model.AlteredCharacterSheet
import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory
import com.pixelized.shared.lwa.model.campaign.Campaign import com.pixelized.shared.lwa.model.campaign.Campaign

View file

@ -1,4 +1,4 @@
package com.pixelized.desktop.lwa.ui.screen.levelup.skill package com.pixelized.desktop.lwa.ui.screen.levelup.items
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.SizeTransform import androidx.compose.animation.SizeTransform
@ -10,7 +10,6 @@ import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
@ -66,8 +65,8 @@ fun LevelUpCharacteristic(
.align(alignment = Alignment.Center), .align(alignment = Alignment.Center),
targetState = characteristic.selected, targetState = characteristic.selected,
transitionSpec = { transitionSpec = {
val enter = fadeIn() + slideInVertically { -32 } val enter = fadeIn() + slideInVertically { -16 }
val exit = fadeOut() + slideOutVertically { 32 } val exit = fadeOut() + slideOutVertically { 16 }
enter togetherWith exit using SizeTransform(clip = false) enter togetherWith exit using SizeTransform(clip = false)
}, },
) { ) {

View file

@ -1,4 +1,4 @@
package com.pixelized.desktop.lwa.ui.screen.levelup.skill package com.pixelized.desktop.lwa.ui.screen.levelup.items
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.SizeTransform import androidx.compose.animation.SizeTransform

View file

@ -2,8 +2,11 @@ package com.pixelized.desktop.lwa.ui.screen.network
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ScrollState import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -186,50 +189,57 @@ fun NetworkPage(
) { ) {
val snack = LocalSnackHost.current val snack = LocalSnackHost.current
Surface( Box(
modifier = modifier, modifier = modifier,
) { ) {
BlurContent( BlurContent(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
controller = viewModel.blurController, controller = viewModel.blurController,
) { ) {
NetworkContent( Surface {
modifier = Modifier.fillMaxSize(), NetworkContent(
network = viewModel.network.collectAsState(), modifier = Modifier.fillMaxSize(),
onPlayerChange = viewModel::onPlayerNameChange, network = viewModel.network.collectAsState(),
onHostChange = viewModel::onHostChange, onPlayerChange = viewModel::onPlayerNameChange,
onResetHostChange = viewModel::onResetHostChange, onHostChange = viewModel::onHostChange,
onPortChange = viewModel::onPortChange, onResetHostChange = viewModel::onResetHostChange,
onResetPortChange = viewModel::onResetPortChange, onPortChange = viewModel::onPortChange,
onConnect = viewModel::connect, onResetPortChange = viewModel::onResetPortChange,
onDisconnect = viewModel::disconnect, onConnect = viewModel::connect,
) onDisconnect = viewModel::disconnect,
)
}
} }
AnimatedContent( AnimatedContent(
modifier = Modifier.size(size = 64.dp), modifier = Modifier
.align(alignment = Alignment.Center)
.size(size = 32.dp),
targetState = viewModel.isLoading.value, targetState = viewModel.isLoading.value,
transitionSpec = { fadeIn() togetherWith fadeOut() }, transitionSpec = {
val enter = fadeIn() + slideInVertically { 64 }
val exit = fadeOut() + slideOutVertically { 64 }
enter togetherWith exit using SizeTransform(clip = false)
},
) { ) {
when (it) { when (it) {
true -> CircularProgressIndicator() true -> CircularProgressIndicator()
else -> Box(modifier = Modifier) else -> Box(modifier = Modifier)
} }
} }
LaunchedEffect(Unit) {
viewModel.message.collect {
snack.showSnackbar(
message = it,
duration = SnackbarDuration.Short,
)
}
}
ErrorSnack(
error = viewModel.networkError,
)
} }
LaunchedEffect(Unit) {
viewModel.message.collect {
snack.showSnackbar(
message = it,
duration = SnackbarDuration.Short,
)
}
}
ErrorSnack(
error = viewModel.networkError,
)
} }
@Composable @Composable

View file

@ -9,6 +9,7 @@ import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow

View file

@ -0,0 +1,67 @@
//package com.pixelized.desktop.lwa.ui.shaders
//
//import androidx.compose.animation.core.withInfiniteAnimationFrameMillis
//import androidx.compose.runtime.Composable
//import androidx.compose.runtime.getValue
//import androidx.compose.runtime.mutableStateOf
//import androidx.compose.runtime.produceState
//import androidx.compose.runtime.remember
//import androidx.compose.runtime.setValue
//import androidx.compose.ui.Modifier
//import androidx.compose.ui.draw.drawBehind
//import androidx.compose.ui.draw.drawWithContent
//import androidx.compose.ui.geometry.Size
//import androidx.compose.ui.graphics.BlendMode
//import androidx.compose.ui.graphics.Brush
//import androidx.compose.ui.graphics.Color
//import androidx.compose.ui.layout.onGloballyPositioned
//import com.mikepenz.hypnoticcanvas.NonAndroidRuntimeEffect
//import com.mikepenz.hypnoticcanvas.shaders.Shader
//import com.mikepenz.hypnoticcanvas.utils.round
//
//@Composable
//fun Modifier.shaderContent(
// shader: Shader,
// speed: Float = 1f,
// fallback: () -> Brush = {
// Brush.horizontalGradient(listOf(Color.Transparent, Color.Transparent))
// },
//): Modifier {
// val runtimeEffect = remember(shader) { NonAndroidRuntimeEffect(shader) }
// var size: Size by remember { mutableStateOf(Size(-1f, -1f)) }
// val speedModifier = shader.speedModifier
//
// val time by if (runtimeEffect.supported) {
// var startMillis = remember(shader) { -1L }
// produceState(0f, speedModifier) {
// while (true) {
// withInfiniteAnimationFrameMillis {
// if (startMillis < 0) startMillis = it
// value = ((it - startMillis) / 16.6f) / 10f
// }
// }
// }
// } else {
// mutableStateOf(-1f)
// }
//
// return this then Modifier
// .onGloballyPositioned {
// size = Size(it.size.width.toFloat(), it.size.height.toFloat())
// }
// .drawWithContent {
// drawContent()
// // set uniforms for the shaders
// runtimeEffect.update(
// shader = shader,
// time = (time * speed * speedModifier).round(3),
// width = size.width,
// height = size.height
// )
// if (runtimeEffect.ready) {
// drawRect(brush = runtimeEffect.build(), blendMode = BlendMode.SrcAtop)
// } else {
// drawRect(brush = fallback())
// }
// }
//}

View file

@ -0,0 +1,19 @@
//package com.pixelized.desktop.lwa.ui.shaders
//
//import androidx.compose.animation.core.withInfiniteAnimationFrameMillis
//import androidx.compose.runtime.Composable
//import androidx.compose.runtime.Stable
//import androidx.compose.runtime.State
//import androidx.compose.runtime.produceState
//
//@Composable
//@Stable
//fun rememberShaderTimeState(speed: Float = 1f): State<Float> {
// return produceState(0f) {
// while (true) {
// withInfiniteAnimationFrameMillis {
// value = it / 1000f * speed
// }
// }
// }
//}

View file

@ -4,6 +4,8 @@ import androidx.compose.ui.graphics.Color
object LwaColorPalette { object LwaColorPalette {
val DefaultScrimColor = Color.Black.copy(alpha = 0.4f) val DefaultScrimColor = Color.Black.copy(alpha = 0.4f)
val Blood = Color(0xFFBB0A1E)
val Gold = Color(0xFFffe900)
val Orange400 = Color(0xFFFFA726) val Orange400 = Color(0xFFFFA726)
val Red400 = Color(0xFFFF7043) val Red400 = Color(0xFFFF7043)

View file

@ -31,6 +31,7 @@ data class LwaColors(
@Stable @Stable
data class Portrait( data class Portrait(
val levelUp: Color, val levelUp: Color,
val blood: Color,
) )
@Stable @Stable
@ -81,7 +82,8 @@ fun darkLwaColorTheme(
) )
), ),
portrait: LwaColors.Portrait = LwaColors.Portrait( portrait: LwaColors.Portrait = LwaColors.Portrait(
levelUp = Color(0xFFffe900), levelUp = LwaColorPalette.Gold,
blood = LwaColorPalette.Blood.copy(alpha = .25f),
), ),
chat: LwaColors.Chat = LwaColors.Chat( chat: LwaColors.Chat = LwaColors.Chat(
timestamp = base.secondary, timestamp = base.secondary,

View file

@ -10,6 +10,7 @@ koin = "4.0.0"
turtle = "0.10.0" turtle = "0.10.0"
logback = "1.5.17" logback = "1.5.17"
coil = "3.1.0" coil = "3.1.0"
ui-graphics-android = "1.7.8"
[plugins] [plugins]
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" } composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
@ -50,4 +51,5 @@ ktor-server-negotiation = { group = 'io.ktor', name = 'ktor-server-content-negot
# Utilitary run command line. # Utilitary run command line.
turtle = { group = "com.lordcodes.turtle", name = "turtle", version.ref = "turtle" } turtle = { group = "com.lordcodes.turtle", name = "turtle", version.ref = "turtle" }
# Utilitary logging use by Ktor. # Utilitary logging use by Ktor.
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
androidx-ui-graphics-android = { group = "androidx.compose.ui", name = "ui-graphics-android", version.ref = "ui-graphics-android" }