package com.suno.android.extensions import androidx.compose.ui.graphics.Color private const val THREE_DIGIT_HEX = 3 private const val SIX_DIGIT_HEX = 6 private const val EIGHT_DIGIT_HEX = 8 private const val HEX_RADIX = 16 /** * Converts a string representation of a color to a Compose Color object. * * Supported formats: * - 6-digit hex: "FF5733" or "#FF5733" * - 8-digit hex with alpha: "FF5733AA" or "#FF5733AA" (alpha repositioned from end to start) * - 3-digit hex shorthand: "F73" or "#F73" (expanded to "FF7733") * - Keywords (case-sensitive): { transparent } * * @return Compose Color object if valid web hex color code, otherwise null. */ fun String.xAsColor(): Color? { // Handle special keywords (case-sensitive) if (this.equals("transparent", ignoreCase = false)) { return Color.Transparent } return try { parseHexColor(this) } catch (_: NumberFormatException) { null } catch (_: IllegalArgumentException) { null } } /** * Parses a hex color string and returns a Compose Color object. * Expects normalized hex format validation to happen in the caller. */ private fun parseHexColor( hexString: String, ): Color? { val normalized = hexString.trim().uppercase().removePrefix("#") // Validate that all characters are valid hex digits if (!normalized.all { it in '0'..'9' || it in 'A'..'F' }) { return null } // Parse based on length and convert to ARGB Int format val argbInt = when (normalized.length) { THREE_DIGIT_HEX -> { // Expand shorthand: "F73" -> "FF7733" val expanded = normalized.map { "$it$it" }.joinToString("") // 6-digit hex gets full opacity (0xFF prefix for alpha) "FF$expanded".toULong(HEX_RADIX).toInt() } SIX_DIGIT_HEX -> { // 6-digit hex gets full opacity (0xFF prefix for alpha) "FF$normalized".toULong(HEX_RADIX).toInt() } EIGHT_DIGIT_HEX -> { // Backend sends alpha at the end of the string in web format (RRGGBBAA) // Android Color(Int) expects alpha at the front (AARRGGBB) val alpha = normalized.substring(SIX_DIGIT_HEX, EIGHT_DIGIT_HEX) val rgb = normalized.substring(0, SIX_DIGIT_HEX) "$alpha$rgb".toULong(HEX_RADIX).toInt() } else -> null } return argbInt?.let { Color(it) } }