Pith - shim
package com.vgmlr.shim

import android.content.Intent
import android.graphics.Color as AndroidColor
import android.net.Uri
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.gestures.detectVerticalDragGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.outlined.Archive
import androidx.compose.material.icons.outlined.InvertColors
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.graphics.toColorInt
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import java.util.Locale
import kotlin.math.roundToInt
import kotlinx.coroutines.delay
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.res.colorResource

private val CBg = Color(0xFF0E0F11)
private val CCard = Color(0xFF272A30).copy(alpha = 0.8f)
private val CCardNo = Color(0xFF272A30)
private val CText = Color(0xFFE8EAED)
private val CMuted = Color(0xFF868D97)
private val CChip = Color(0xFF3A4048)
private val CDanger = Color(0xFF8B0000)
private val CHead = Color(0xFF33373E).copy(alpha = 0.8f)
private val COutline = CMuted.copy(alpha = 0.30f)
private val CTitle = Color(0xFF272A30)

private val ROW_H = 46.dp
private val HEAD_H = 46.dp
private val FS = 15.sp

private val HEAD_INK = CText.copy(alpha = 0.75f)
private val HEAD_ICON = 16.dp
private val HEAD_ICON_GAP = 10.dp
private val HEAD_TEXT_DY = 1.dp
private val HEAD_FS = 12.sp
private val SETTINGS_TOP_GAP = 22.dp

class ShimSettings : ComponentActivity() {
    private lateinit var themeManager: ShimThemeManager
    private lateinit var db: ShimDatabase

    private val fontSizeOptions = (22..36).map { "%.1f".format(Locale.US, it * 0.5) }
    private val lineHeightOptions = (10..20).map { "%.1f".format(Locale.US, it * 0.1) }
    private val textSpacingOptions = (0..10).map { "%.1f".format(Locale.US, it * 0.1) }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge(
            statusBarStyle = SystemBarStyle.dark(AndroidColor.TRANSPARENT),
            navigationBarStyle = SystemBarStyle.dark(AndroidColor.TRANSPARENT)
        )
        themeManager = ShimThemeManager(this)
        db = ShimDatabase(this)

        setContent {
            ShimTheme(themeManager) {
                SettingsScreen()
            }
        }
    }

    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun SettingsScreen() {
        val context = LocalContext.current

        val defBg = hexOf(colorResource(id = R.color.background_color).toArgb())
        val defText = hexOf(colorResource(id = R.color.text_color).toArgb())

        var bgColor by remember { mutableStateOf(themeManager.hexBgColor) }
        var textColor by remember { mutableStateOf(themeManager.hexTextColor) }
        var tagBg by remember { mutableStateOf(themeManager.hexTagBgColor) }
        var fontSize by remember { mutableStateOf(themeManager.fontSize.floatValue.toString()) }
        var lineHeight by remember { mutableStateOf(themeManager.lineHeight.floatValue.toString()) }
        var lineSpacing by remember { mutableStateOf(themeManager.lineSpacing.floatValue.toString()) }
        var isMonospace by remember { mutableStateOf(themeManager.isMonospace.value) }
        var showDates by remember { mutableStateOf(themeManager.showDates.value) }
        var shimSpacing by remember { mutableStateOf(themeManager.shimSpacing.intValue.toString()) }

        var bkLocation by remember { mutableStateOf(ShimSave.location(context)) }
        var bkFormat by remember { mutableStateOf(ShimSave.format(context)) }
        var armPurge by remember { mutableStateOf(false) }

        val folderPicker = rememberLauncherForActivityResult(
            ActivityResultContracts.OpenDocumentTree()
        ) { uri: Uri? ->
            uri?.let {
                try {
                    contentResolver.takePersistableUriPermission(
                        it,
                        Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                    )
                    bkLocation = it.toString()
                } catch (_: Exception) {
                }
            }
        }

        LaunchedEffect(
            bgColor,
            textColor,
            tagBg,
            fontSize,
            lineHeight,
            lineSpacing,
            isMonospace,
            shimSpacing,
            showDates
        ) {
            delay(500)
            if (!isHex(bgColor) || !isHex(textColor) || !isHex(tagBg)) return@LaunchedEffect
            themeManager.saveTheme(
                bgColor,
                textColor,
                tagBg,
                isMonospace,
                fontSize,
                lineHeight,
                lineSpacing,
                shimSpacing,
                showDates
            )
        }

        LaunchedEffect(bkLocation, bkFormat) {
            ShimSave.saveSettings(context, bkLocation, bkFormat)
        }

        LaunchedEffect(armPurge) {
            if (!armPurge) return@LaunchedEffect
            delay(ARM_HOLD)
            armPurge = false
        }

        Scaffold(
            contentWindowInsets = WindowInsets(0, 0, 0, 0),
            containerColor = CBg,
            topBar = { SettingsTopBar("Settings") { finish() } }
        ) { padding ->

            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(bottom = padding.calculateBottomPadding())
                    .background(CBg)
                    .imePadding()
                    .navigationBarsPadding()
                    .padding(start = LIST_PAD_START, end = LIST_PAD_END)
                    .verticalScroll(rememberScrollState())
            ) {

                Spacer(Modifier.height(padding.calculateTopPadding() + SETTINGS_TOP_GAP))

                // archive

                WgSection("Archive", icon = Icons.Outlined.Archive) {
                    WgRow("Location") {
                        val locationName = remember(bkLocation) {
                            if (bkLocation.isEmpty()) "" else try {
                                DocumentFile.fromTreeUri(context, bkLocation.toUri())?.name
                                    ?: bkLocation
                            } catch (_: Exception) {
                                bkLocation
                            }
                        }
                        val truncated =
                            if (locationName.length > 25) locationName.take(22) + "..." else locationName
                        WgPick(truncated) { folderPicker.launch(null) }
                    }

                    WgDivider()

                    WgRow("Format") {
                        WgDropdown(
                            value = ShimSave.formatLabel(bkFormat),
                            options = ShimSave.FORMATS.map { ShimSave.formatLabel(it) }
                        ) { picked ->
                            bkFormat = ShimSave.FORMATS
                                .firstOrNull { ShimSave.formatLabel(it) == picked } ?: bkFormat
                        }
                    }
                }

                // aesthetic

                WgSection("Aesthetic", icon = Icons.Outlined.InvertColors) {
                    WgColorRow("Text", textColor, defText) { textColor = it }
                    WgDivider()
                    WgColorRow("Tags", tagBg, TAG_BG_DEFAULT) { tagBg = it }
                    WgDivider()
                    WgColorRow("Ground", bgColor, defBg) { bgColor = it }
                    WgDivider()
                    WgRow("Font Size") {
                        WgDropdown(fontSize, fontSizeOptions) { fontSize = it }
                    }
                    WgDivider()
                    WgRow("Text Spacing") {
                        WgDropdown(lineSpacing, textSpacingOptions) { lineSpacing = it }
                    }
                    WgDivider()
                    WgRow("Line Height") {
                        WgDropdown(lineHeight, lineHeightOptions) { lineHeight = it }
                    }
                    WgDivider()
                    WgRow("Monospace") {
                        WgRadio(
                            value = if (isMonospace) "True" else "False",
                            options = listOf("True", "False")
                        ) { isMonospace = it == "True" }
                    }
                    WgDivider()
                    WgRow("Show Time-Stamps") {
                        WgRadio(
                            value = if (showDates) "True" else "False",
                            options = listOf("True", "False")
                        ) { showDates = it == "True" }
                    }
                    WgDivider()
                    WgRow("List Spacing") {
                        WgField(
                            value = shimSpacing,
                            onValueChange = {
                                if (it.length <= 2 && it.all(Char::isDigit)) shimSpacing = it
                            },
                            placeholder = SPACING_DEFAULT.toString(),
                            modifier = Modifier.weight(1f),
                            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
                        )
                    }
                }

                // delete

                WgSection("Delete", icon = Icons.Filled.Clear) {
                    WgRow("Obliterate Shims") {
                        WgAction(
                            if (armPurge) "Confirm" else "Purge",
                            danger = true,
                            icon = if (armPurge) Icons.Outlined.WarningAmber else null
                        ) {
                            if (armPurge) {
                                db.deleteAllShims()
                                armPurge = false
                            } else armPurge = true
                        }
                    }
                }
                Spacer(Modifier.height(8.dp))
            }
        }
    }

    private fun isHex(value: String): Boolean =
        runCatching { (if (value.startsWith("#")) value else "#$value").toColorInt() }.isSuccess

    private fun hexOf(argb: Int): String = "#%06X".format(0xFFFFFF and argb)

    private fun parseColor(value: String): Color = runCatching {
        Color((if (value.startsWith("#")) value else "#$value").toColorInt())
    }.getOrDefault(Color.Gray)

    @Composable
    private fun SettingsTopBar(title: String, onBack: () -> Unit) {
        val topInset = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = topInset + BAR_GAP, start = BAR_EDGE, end = BAR_EDGE_ACTIONS),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.spacedBy(BAR_GAP)
        ) {
            ShimButton(Icons.AutoMirrored.Filled.ArrowBack, "Back", onClick = onBack)
            ShimTitle(title, Modifier.weight(1f), color = CTitle)
        }
    }

    @Composable
    private fun WgSection(
        title: String,
        icon: ImageVector? = null,
        content: @Composable ColumnScope.() -> Unit
    ) {
        val shape = RoundedCornerShape(ROUND_END)
        Column(
            Modifier
                .fillMaxWidth()
                .clip(shape)
                .border(EDGE, shape)
        ) {
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(min = HEAD_H)
                    .background(CHead)
                    .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                if (icon != null) {
                    Icon(
                        icon,
                        contentDescription = null,
                        tint = HEAD_INK,
                        modifier = Modifier.size(HEAD_ICON)
                    )
                    Spacer(Modifier.width(HEAD_ICON_GAP))
                }
                Text(
                    text = title,
                    color = HEAD_INK,
                    fontWeight = FontWeight.Medium,
                    letterSpacing = 1.4.sp,
                    style = TextStyle(
                        fontSize = HEAD_FS,
                        lineHeight = HEAD_FS,
                        platformStyle = PlatformTextStyle(includeFontPadding = false),
                        lineHeightStyle = LineHeightStyle(
                            alignment = LineHeightStyle.Alignment.Center,
                            trim = LineHeightStyle.Trim.Both
                        )
                    ),
                    modifier = Modifier.offset(y = HEAD_TEXT_DY)
                )
            }
            WgDivider()
            WgCard(content)
        }
        Spacer(Modifier.height(24.dp))
    }

    @Composable
    private fun WgCard(content: @Composable ColumnScope.() -> Unit) {
        Column(
            Modifier
                .fillMaxWidth()
                .background(CCard),
            content = content
        )
    }

    @Composable
    private fun WgRow(label: String, content: @Composable RowScope.() -> Unit) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .heightIn(min = ROW_H)
                .padding(horizontal = 16.dp, vertical = 8.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                text = label,
                color = CText.copy(alpha = 0.95f),
                fontSize = FS,
                maxLines = 1,
                modifier = Modifier.widthIn(min = 92.dp)
            )
            Spacer(Modifier.width(12.dp))
            Row(
                modifier = Modifier.weight(1f),
                horizontalArrangement = Arrangement.End,
                verticalAlignment = Alignment.CenterVertically,
                content = content
            )
        }
    }

    @Composable
    private fun WgDivider() {
        HorizontalDivider(thickness = 1.dp, color = CBg)
    }

    @Composable
    private fun WgPick(value: String, onClick: () -> Unit) {
        Text(
            text = value.ifBlank { "app/" },
            color = if (value.isBlank()) CMuted else CText,
            fontSize = FS,
            fontFamily = FontFamily.Monospace,
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
            modifier = Modifier.clickable(
                interactionSource = remember { MutableInteractionSource() },
                indication = null,
                onClick = onClick
            )
        )
    }

    @Composable
    private fun WgField(
        value: String,
        onValueChange: (String) -> Unit,
        placeholder: String,
        modifier: Modifier = Modifier,
        visualTransformation: VisualTransformation = VisualTransformation.None,
        keyboardOptions: KeyboardOptions = KeyboardOptions.Default
    ) {
        val style = TextStyle(
            color = CText,
            fontSize = FS,
            textAlign = TextAlign.End,
            fontFamily = FontFamily.Monospace
        )
        CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides Dp.Unspecified) {
            BasicTextField(
                value = value,
                onValueChange = onValueChange,
                modifier = modifier,
                singleLine = true,
                textStyle = style,
                cursorBrush = SolidColor(CText),
                visualTransformation = visualTransformation,
                keyboardOptions = keyboardOptions,
                decorationBox = { inner ->
                    Box(contentAlignment = Alignment.CenterEnd) {
                        if (value.isEmpty() && placeholder.isNotEmpty()) {
                            Text(placeholder, style = style.copy(color = CMuted), maxLines = 1)
                        }
                        inner()
                    }
                }
            )
        }
    }

    @Composable
    private fun WgColorRow(
        label: String,
        value: String,
        default: String,
        onValueChange: (String) -> Unit
    ) {
        var showPicker by remember { mutableStateOf(false) }

        WgRow(label) {
            WgField(
                value = value,
                onValueChange = onValueChange,
                placeholder = default,
                modifier = Modifier.weight(1f)
            )
            Spacer(Modifier.width(12.dp))
            Box(
                Modifier
                    .size(20.dp)
                    .clip(CircleShape)
                    .background(parseColor(value.ifBlank { default }))
                    .clickable(
                        interactionSource = remember { MutableInteractionSource() },
                        indication = null
                    ) { showPicker = true }
            )
        }

        if (showPicker) {
            ColorPickerDialog(
                value.ifBlank { default },
                { onValueChange(it); showPicker = false },
                { showPicker = false }
            )
        }
    }

    @Composable
    private fun WgDropdown(
        value: String,
        options: List<String>,
        onSelect: (String) -> Unit
    ) {
        var expanded by remember { mutableStateOf(false) }

        Box {
            Row(
                modifier = Modifier.clickable(
                    interactionSource = remember { MutableInteractionSource() },
                    indication = null
                ) { expanded = true },
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text(
                    text = value,
                    color = CText,
                    fontSize = FS,
                    fontFamily = FontFamily.Monospace,
                    maxLines = 1
                )
                Icon(
                    imageVector = Icons.Default.KeyboardArrowDown,
                    contentDescription = null,
                    tint = CMuted,
                    modifier = Modifier.size(18.dp)
                )
            }
            DropdownMenu(
                expanded = expanded,
                onDismissRequest = { expanded = false },
                containerColor = Color(0xFF1F2226),
                shape = RoundedCornerShape(ROUND_END),
                tonalElevation = 0.dp,
                shadowElevation = 0.dp
            ) {
                options.forEach { opt ->
                    DropdownMenuItem(
                        text = {
                            Text(
                                text = opt,
                                color = if (opt == value) CMuted else CText,
                                fontSize = FS,
                                fontFamily = FontFamily.Monospace
                            )
                        },
                        onClick = { onSelect(opt); expanded = false },
                        contentPadding = PaddingValues(horizontal = 14.dp)
                    )
                }
            }
        }
    }

    @Composable
    private fun WgChip(
        label: String,
        selected: Boolean,
        enabled: Boolean = true,
        danger: Boolean = false,
        shape: Shape = RoundedCornerShape(6.dp),
        border: Boolean = true,
        leftLine: Color? = null,
        rightLine: Color? = null,
        icon: ImageVector? = null,
        onClick: () -> Unit
    ) {
        val filled = selected && enabled
        val fill = if (danger) CDanger else CChip
        val ink = when {
            !enabled -> CMuted.copy(alpha = 0.4f)
            selected -> CText
            else -> CMuted
        }

        Box(
            modifier = Modifier
                .height(28.dp)
                .clip(shape)
                .background(if (filled) fill else Color.Transparent)
                .then(
                    if (leftLine != null || rightLine != null) Modifier.drawBehind {
                        val w = 1.dp.toPx()
                        leftLine?.let { drawLine(it, Offset(w / 2f, 0f), Offset(w / 2f, size.height), w) }
                        rightLine?.let {
                            val x = size.width - w / 2f
                            drawLine(it, Offset(x, 0f), Offset(x, size.height), w)
                        }
                    } else Modifier
                )
                .then(
                    if (border) Modifier.border(
                        width = 1.dp,
                        color = COutline,
                        shape = shape
                    ) else Modifier
                )
                .clickable(
                    enabled = enabled,
                    interactionSource = remember { MutableInteractionSource() },
                    indication = null,
                    onClick = onClick
                )
                .padding(horizontal = 16.dp),
            contentAlignment = Alignment.Center
        ) {
            Row(verticalAlignment = Alignment.CenterVertically) {
                if (icon != null) {
                    Icon(icon, contentDescription = null, tint = ink, modifier = Modifier.size(16.dp))
                    Spacer(Modifier.width(6.dp))
                }
                Text(
                    text = label,
                    color = ink,
                    fontSize = 13.sp,
                    fontFamily = FontFamily.Monospace,
                    maxLines = 1
                )
            }
        }
    }

    @Composable
    private fun WgAction(
        text: String,
        enabled: Boolean = true,
        danger: Boolean = false,
        icon: ImageVector? = null,
        onClick: () -> Unit
    ) {
        WgChip(
            label = text,
            selected = true,
            enabled = enabled,
            danger = danger,
            icon = icon,
            onClick = onClick
        )
    }

    @Composable
    private fun WgRadio(
        value: String,
        options: List<String>,
        onSelect: (String) -> Unit
    ) {
        val shape = RoundedCornerShape(6.dp)
        val outline = COutline

        Row(
            modifier = Modifier
                .height(28.dp)
                .clip(shape)
                .border(1.dp, outline, shape),
            verticalAlignment = Alignment.CenterVertically
        ) {
            options.forEachIndexed { i, opt ->
                WgChip(
                    label = opt,
                    selected = opt == value,
                    shape = RectangleShape,
                    border = false,
                    leftLine = if (i > 0 && options[i - 1] != value) outline else null,
                    rightLine = if (i < options.lastIndex && opt == value) outline else null,
                    onClick = { onSelect(opt) }
                )
            }
        }
    }

    @Composable
    private fun ColorPickerDialog(
        initial: String,
        onPick: (String) -> Unit,
        onDismiss: () -> Unit
    ) {
        val start = parseColor(initial)
        var r by remember { mutableFloatStateOf(start.red) }
        var g by remember { mutableFloatStateOf(start.green) }
        var b by remember { mutableFloatStateOf(start.blue) }
        val hex = "#%02X%02X%02X".format((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt())

        val btnShape = RoundedCornerShape(ROUND_END)
        val btnColors = ButtonDefaults.buttonColors(
            containerColor = CChip,
            contentColor = CText
        )
        val sliderColor = Color(0xFF9AA1AA)
        val sliderBg = Color(0xFF23262A)

        AlertDialog(
            onDismissRequest = onDismiss,
            modifier = Modifier.border(EDGE, RoundedCornerShape(ROUND_END)),
            shape = RoundedCornerShape(ROUND_END),
            containerColor = CCardNo,
            confirmButton = {
                Button(
                    onClick = { onPick(hex) },
                    modifier = Modifier.height(44.dp),
                    colors = btnColors,
                    shape = btnShape
                ) { Text("This", color = CText, fontSize = 15.sp) }
            },
            dismissButton = {
                Button(
                    onClick = onDismiss,
                    modifier = Modifier.height(44.dp),
                    colors = btnColors,
                    shape = btnShape
                ) { Text("Abort", color = CText, fontSize = 15.sp) }
            },
            text = {
                Column {
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.Center,
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        Box(
                            Modifier
                                .size(52.dp)
                                .background(Color(r, g, b), RoundedCornerShape(ROUND_END))
                        )
                        Spacer(Modifier.width(15.dp))
                        Text(
                            hex,
                            color = CText,
                            fontFamily = FontFamily.Monospace,
                            fontSize = 17.sp
                        )
                    }
                    Spacer(Modifier.height(22.dp))
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.spacedBy(
                            62.dp,
                            Alignment.CenterHorizontally
                        )
                    ) {
                        VerticalRgbSlider(r, { r = it }, sliderColor, trackColor = sliderBg)
                        VerticalRgbSlider(g, { g = it }, sliderColor, trackColor = sliderBg)
                        VerticalRgbSlider(b, { b = it }, sliderColor, trackColor = sliderBg)
                    }
                }
            }
        )
    }

    @Composable
    private fun VerticalRgbSlider(
        value: Float,
        onValueChange: (Float) -> Unit,
        color: Color,
        modifier: Modifier = Modifier,
        trackColor: Color = color,
        height: Dp = 130.dp,
        trackWidth: Dp = 10.dp,
        thumbSize: Dp = 22.dp
    ) {
        val thumbPx = with(LocalDensity.current) { thumbSize.toPx() }
        var heightPx by remember { mutableFloatStateOf(0f) }
        val travel = (heightPx - thumbPx).coerceAtLeast(1f)

        val setFromY: (Float) -> Unit = { y ->
            val pos = (y - thumbPx / 2f).coerceIn(0f, travel)
            onValueChange(1f - pos / travel)
        }

        Box(
            modifier = modifier
                .height(height)
                .width(thumbSize)
                .onGloballyPositioned { heightPx = it.size.height.toFloat() }
                .pointerInput(Unit) { detectTapGestures { setFromY(it.y) } }
                .pointerInput(Unit) {
                    detectVerticalDragGestures { change, _ -> setFromY(change.position.y) }
                }
        ) {
            Box(
                Modifier
                    .width(trackWidth)
                    .fillMaxHeight()
                    .align(Alignment.Center)
                    .background(trackColor, CircleShape)
                    .border(EDGE, CircleShape)
            )
            Box(
                Modifier
                    .size(thumbSize)
                    .offset { IntOffset(0, ((1f - value) * travel).roundToInt()) }
                    .background(color, CircleShape)
            )
        }
    }
}