package com.peachpasswords.ui.components import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable 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.draw.drawBehind import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp import com.peachpasswords.ui.theme.peachColors import com.peachpasswords.ui.theme.PeachType /** * Initials chip used in place of a network favicon (offline-first). Ports the * browser titleInitials + titleBackground (deterministic HSL 32%/26%). The * corner radius tracks the chip size (≈6dp at 26dp, ≈3dp at 16dp) per spec §4.6. */ @Composable fun FaviconInitials( source: String, modifier: Modifier = Modifier, size: Dp = 26.dp, cornerRadius: Dp = (size.value * 0.23f).dp, ) { val bg = titleBackground(source) Box( modifier = modifier .size(size) .clip(RoundedCornerShape(cornerRadius)) .drawBehind { drawRect(bg) }, contentAlignment = Alignment.Center, ) { Text( text = titleInitials(source), color = peachColors.T0.copy(alpha = 0.92f), fontFamily = PeachType.Mono, fontWeight = FontWeight.SemiBold, letterSpacing = 0.04.em, fontSize = (size.value * 0.34f).sp, ) } } fun titleInitials(source: String): String { val cleaned = source.replace(Regex("^https?://"), "").removePrefix("www.").trim() if (cleaned.isEmpty()) return "·" val words = cleaned.split(Regex("[\\s.\\-_/]+")).filter { it.isNotEmpty() } if (words.size >= 2 && words[0].isNotEmpty() && words[1].isNotEmpty()) { return (words[0][0].toString() + words[1][0].toString()).uppercase() } val single = words.firstOrNull() ?: cleaned return single.take(2).uppercase() } /** Deterministic muted HSL(hue, 32%, 26%) background, matching the browser. */ fun titleBackground(source: String): Color { var hash = 0 for (ch in source) hash = ((hash shl 5) - hash + ch.code) val hue = (((hash % 360) + 360) % 360).toFloat() return hsl(hue, 0.32f, 0.26f) } private fun hsl(h: Float, s: Float, l: Float): Color { val c = (1f - kotlin.math.abs(2 * l - 1f)) * s val x = c * (1f - kotlin.math.abs((h / 60f) % 2f - 1f)) val m = l - c / 2f val (r, g, b) = when { h < 60 -> Triple(c, x, 0f) h < 120 -> Triple(x, c, 0f) h < 180 -> Triple(0f, c, x) h < 240 -> Triple(0f, x, c) h < 300 -> Triple(x, 0f, c) else -> Triple(c, 0f, x) } return Color(r + m, g + m, b + m) }