feat: 新增前端通用组件
- 新增FriendlyError友好错误提示组件 - 新增MarkdownText Markdown文本渲染组件
This commit is contained in:
@@ -0,0 +1,475 @@
|
|||||||
|
package com.huaga.life_echo.ui.components.common
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.core.*
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.scaleIn
|
||||||
|
import androidx.compose.animation.scaleOut
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.draw.scale
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.huaga.life_echo.config.AppConfig
|
||||||
|
import com.huaga.life_echo.ui.icons.AppIcons
|
||||||
|
import com.huaga.life_echo.ui.theme.LightPurple
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误类型枚举
|
||||||
|
* 用于分类不同的错误场景,展示对应的友好提示
|
||||||
|
*/
|
||||||
|
enum class ErrorType {
|
||||||
|
NETWORK, // 网络连接错误
|
||||||
|
SERVER, // 服务器错误
|
||||||
|
TIMEOUT, // 请求超时
|
||||||
|
AUTH, // 认证/授权错误
|
||||||
|
NOT_FOUND, // 资源不存在
|
||||||
|
VALIDATION, // 数据验证错误
|
||||||
|
UNKNOWN // 未知错误
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 友好错误信息数据类
|
||||||
|
*/
|
||||||
|
data class FriendlyErrorInfo(
|
||||||
|
val icon: ImageVector,
|
||||||
|
val title: String,
|
||||||
|
val message: String,
|
||||||
|
val actionText: String = "重试",
|
||||||
|
val iconTint: Color = LightPurple
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误信息处理工具
|
||||||
|
* 根据运行模式决定显示原始错误还是友好提示
|
||||||
|
*/
|
||||||
|
object ErrorHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取友好错误信息
|
||||||
|
* 生产模式下返回友好提示,开发模式下可选择显示原始错误
|
||||||
|
*/
|
||||||
|
fun getFriendlyError(
|
||||||
|
errorType: ErrorType,
|
||||||
|
originalMessage: String? = null
|
||||||
|
): FriendlyErrorInfo {
|
||||||
|
return when (errorType) {
|
||||||
|
ErrorType.NETWORK -> FriendlyErrorInfo(
|
||||||
|
icon = AppIcons.WifiOff,
|
||||||
|
title = "网络连接失败",
|
||||||
|
message = "请检查您的网络连接后重试",
|
||||||
|
iconTint = Color(0xFFFF9800)
|
||||||
|
)
|
||||||
|
ErrorType.SERVER -> FriendlyErrorInfo(
|
||||||
|
icon = AppIcons.CloudOff,
|
||||||
|
title = "服务暂时不可用",
|
||||||
|
message = "服务器正在维护中,请稍后再试",
|
||||||
|
iconTint = Color(0xFFF44336)
|
||||||
|
)
|
||||||
|
ErrorType.TIMEOUT -> FriendlyErrorInfo(
|
||||||
|
icon = AppIcons.AccessTime,
|
||||||
|
title = "请求超时",
|
||||||
|
message = "网络响应较慢,请稍后重试",
|
||||||
|
iconTint = Color(0xFFFF9800)
|
||||||
|
)
|
||||||
|
ErrorType.AUTH -> FriendlyErrorInfo(
|
||||||
|
icon = AppIcons.Lock,
|
||||||
|
title = "登录已过期",
|
||||||
|
message = "请重新登录后继续操作",
|
||||||
|
actionText = "去登录",
|
||||||
|
iconTint = Color(0xFF9C27B0)
|
||||||
|
)
|
||||||
|
ErrorType.NOT_FOUND -> FriendlyErrorInfo(
|
||||||
|
icon = AppIcons.SearchOff,
|
||||||
|
title = "内容不存在",
|
||||||
|
message = "您访问的内容可能已被删除或移动",
|
||||||
|
iconTint = Color(0xFF607D8B)
|
||||||
|
)
|
||||||
|
ErrorType.VALIDATION -> FriendlyErrorInfo(
|
||||||
|
icon = AppIcons.Warning,
|
||||||
|
title = "信息有误",
|
||||||
|
message = if (AppConfig.isDebugMode && originalMessage != null) {
|
||||||
|
originalMessage
|
||||||
|
} else {
|
||||||
|
"请检查输入的信息是否正确"
|
||||||
|
},
|
||||||
|
iconTint = Color(0xFFFF9800)
|
||||||
|
)
|
||||||
|
ErrorType.UNKNOWN -> FriendlyErrorInfo(
|
||||||
|
icon = AppIcons.Error,
|
||||||
|
title = "出了点小问题",
|
||||||
|
message = "请稍后重试,如问题持续请联系客服",
|
||||||
|
iconTint = Color(0xFFF44336)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据异常自动判断错误类型
|
||||||
|
*/
|
||||||
|
fun detectErrorType(exception: Throwable): ErrorType {
|
||||||
|
val message = exception.message?.lowercase() ?: ""
|
||||||
|
return when {
|
||||||
|
message.contains("timeout") || message.contains("timed out") -> ErrorType.TIMEOUT
|
||||||
|
message.contains("network") || message.contains("connect") ||
|
||||||
|
message.contains("socket") || message.contains("unreachable") -> ErrorType.NETWORK
|
||||||
|
message.contains("401") || message.contains("unauthorized") ||
|
||||||
|
message.contains("token") || message.contains("expired") -> ErrorType.AUTH
|
||||||
|
message.contains("404") || message.contains("not found") -> ErrorType.NOT_FOUND
|
||||||
|
message.contains("500") || message.contains("502") ||
|
||||||
|
message.contains("503") || message.contains("server") -> ErrorType.SERVER
|
||||||
|
message.contains("validation") || message.contains("invalid") -> ErrorType.VALIDATION
|
||||||
|
else -> ErrorType.UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据HTTP状态码判断错误类型
|
||||||
|
*/
|
||||||
|
fun detectErrorTypeByStatusCode(statusCode: Int): ErrorType {
|
||||||
|
return when (statusCode) {
|
||||||
|
401, 403 -> ErrorType.AUTH
|
||||||
|
404 -> ErrorType.NOT_FOUND
|
||||||
|
408, 504 -> ErrorType.TIMEOUT
|
||||||
|
in 500..599 -> ErrorType.SERVER
|
||||||
|
else -> ErrorType.UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用于显示的错误消息
|
||||||
|
* 生产模式下返回友好提示,开发模式下返回原始错误
|
||||||
|
*/
|
||||||
|
fun getDisplayMessage(
|
||||||
|
errorType: ErrorType,
|
||||||
|
originalMessage: String?
|
||||||
|
): String {
|
||||||
|
return if (AppConfig.isDebugMode && originalMessage != null) {
|
||||||
|
originalMessage
|
||||||
|
} else {
|
||||||
|
getFriendlyError(errorType, originalMessage).message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理异常并返回显示消息
|
||||||
|
*/
|
||||||
|
fun handleException(exception: Throwable): String {
|
||||||
|
val errorType = detectErrorType(exception)
|
||||||
|
return getDisplayMessage(errorType, exception.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 友好错误视图组件(带动画)
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun FriendlyErrorView(
|
||||||
|
errorType: ErrorType,
|
||||||
|
originalMessage: String? = null,
|
||||||
|
onRetry: (() -> Unit)? = null,
|
||||||
|
onAction: (() -> Unit)? = null,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
showAnimation: Boolean = true
|
||||||
|
) {
|
||||||
|
val errorInfo = ErrorHandler.getFriendlyError(errorType, originalMessage)
|
||||||
|
|
||||||
|
// 动画状态
|
||||||
|
var visible by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
visible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图标呼吸动画
|
||||||
|
val infiniteTransition = rememberInfiniteTransition(label = "error_animation")
|
||||||
|
val iconScale by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 1f,
|
||||||
|
targetValue = 1.1f,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(1000, easing = EaseInOutCubic),
|
||||||
|
repeatMode = RepeatMode.Reverse
|
||||||
|
),
|
||||||
|
label = "icon_scale"
|
||||||
|
)
|
||||||
|
|
||||||
|
val iconAlpha by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 0.7f,
|
||||||
|
targetValue = 1f,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(1000, easing = EaseInOutCubic),
|
||||||
|
repeatMode = RepeatMode.Reverse
|
||||||
|
),
|
||||||
|
label = "icon_alpha"
|
||||||
|
)
|
||||||
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = visible,
|
||||||
|
enter = fadeIn(animationSpec = tween(300)) + scaleIn(initialScale = 0.8f),
|
||||||
|
exit = fadeOut() + scaleOut()
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(32.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
// 图标容器(带动画)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(100.dp)
|
||||||
|
.scale(if (showAnimation) iconScale else 1f)
|
||||||
|
.alpha(if (showAnimation) iconAlpha else 1f)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(errorInfo.iconTint.copy(alpha = 0.1f)),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = errorInfo.icon,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = errorInfo.iconTint,
|
||||||
|
modifier = Modifier.size(48.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
|
// 标题
|
||||||
|
Text(
|
||||||
|
text = errorInfo.title,
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
// 描述信息
|
||||||
|
Text(
|
||||||
|
text = errorInfo.message,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
lineHeight = 22.sp,
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 开发模式下显示原始错误信息
|
||||||
|
if (AppConfig.isDebugMode && originalMessage != null && errorType != ErrorType.VALIDATION) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.3f)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "[DEBUG] $originalMessage",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
modifier = Modifier.padding(12.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
|
||||||
|
// 操作按钮
|
||||||
|
val actionHandler = onAction ?: onRetry
|
||||||
|
if (actionHandler != null) {
|
||||||
|
Button(
|
||||||
|
onClick = actionHandler,
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = errorInfo.iconTint
|
||||||
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(48.dp)
|
||||||
|
.padding(horizontal = 32.dp),
|
||||||
|
shape = RoundedCornerShape(24.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = errorInfo.actionText,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 友好错误对话框组件
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun FriendlyErrorDialog(
|
||||||
|
errorType: ErrorType,
|
||||||
|
originalMessage: String? = null,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onRetry: (() -> Unit)? = null
|
||||||
|
) {
|
||||||
|
val errorInfo = ErrorHandler.getFriendlyError(errorType, originalMessage)
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
icon = {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(56.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(errorInfo.iconTint.copy(alpha = 0.1f)),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = errorInfo.icon,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = errorInfo.iconTint,
|
||||||
|
modifier = Modifier.size(28.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = errorInfo.title,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
Text(
|
||||||
|
text = errorInfo.message,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
|
||||||
|
// 开发模式下显示原始错误
|
||||||
|
if (AppConfig.isDebugMode && originalMessage != null) {
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
Text(
|
||||||
|
text = "[DEBUG] $originalMessage",
|
||||||
|
fontSize = 11.sp,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
if (onRetry != null) {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
onDismiss()
|
||||||
|
onRetry()
|
||||||
|
},
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = errorInfo.iconTint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(errorInfo.actionText, color = Color.White)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TextButton(onClick = onDismiss) {
|
||||||
|
Text("我知道了", color = errorInfo.iconTint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = if (onRetry != null) {
|
||||||
|
{
|
||||||
|
TextButton(onClick = onDismiss) {
|
||||||
|
Text("取消", color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 友好错误 Snackbar 消息
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun rememberFriendlyErrorSnackbarHostState(): SnackbarHostState {
|
||||||
|
return remember { SnackbarHostState() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示友好错误 Snackbar
|
||||||
|
*/
|
||||||
|
suspend fun SnackbarHostState.showFriendlyError(
|
||||||
|
errorType: ErrorType,
|
||||||
|
originalMessage: String? = null,
|
||||||
|
actionLabel: String? = "重试"
|
||||||
|
): SnackbarResult {
|
||||||
|
val errorInfo = ErrorHandler.getFriendlyError(errorType, originalMessage)
|
||||||
|
return showSnackbar(
|
||||||
|
message = errorInfo.message,
|
||||||
|
actionLabel = actionLabel,
|
||||||
|
duration = SnackbarDuration.Long
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内联错误提示组件(用于表单等场景)
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun InlineErrorMessage(
|
||||||
|
message: String,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
// 生产模式下使用通用提示
|
||||||
|
val displayMessage = if (AppConfig.isDebugMode) {
|
||||||
|
message
|
||||||
|
} else {
|
||||||
|
// 检查是否是常见的技术性错误信息,如果是则替换为友好提示
|
||||||
|
when {
|
||||||
|
message.contains("timeout", ignoreCase = true) -> "请求超时,请重试"
|
||||||
|
message.contains("network", ignoreCase = true) -> "网络连接失败"
|
||||||
|
message.contains("server", ignoreCase = true) -> "服务暂时不可用"
|
||||||
|
message.contains("unauthorized", ignoreCase = true) -> "请重新登录"
|
||||||
|
message.contains("exception", ignoreCase = true) -> "操作失败,请重试"
|
||||||
|
message.contains("error", ignoreCase = true) && message.length > 50 -> "操作失败,请重试"
|
||||||
|
else -> message // 保留业务相关的错误信息
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(RoundedCornerShape(8.dp))
|
||||||
|
.background(MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.3f))
|
||||||
|
.padding(12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = AppIcons.Warning,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.error,
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = displayMessage,
|
||||||
|
fontSize = 13.sp,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
lineHeight = 18.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package com.huaga.life_echo.ui.components.common
|
||||||
|
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.mikepenz.markdown.m3.Markdown
|
||||||
|
import com.mikepenz.markdown.m3.markdownColor
|
||||||
|
import com.mikepenz.markdown.m3.markdownTypography
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Markdown 文本渲染组件
|
||||||
|
* 支持常见的 Markdown 语法:标题、粗体、斜体、列表、引用、代码块等
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun MarkdownText(
|
||||||
|
content: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
textColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
linkColor: Color = MaterialTheme.colorScheme.primary,
|
||||||
|
fontSize: Int = 16,
|
||||||
|
lineHeight: Int = 28
|
||||||
|
) {
|
||||||
|
val typography = markdownTypography(
|
||||||
|
h1 = MaterialTheme.typography.headlineLarge.copy(
|
||||||
|
color = textColor,
|
||||||
|
fontSize = (fontSize + 12).sp
|
||||||
|
),
|
||||||
|
h2 = MaterialTheme.typography.headlineMedium.copy(
|
||||||
|
color = textColor,
|
||||||
|
fontSize = (fontSize + 8).sp
|
||||||
|
),
|
||||||
|
h3 = MaterialTheme.typography.headlineSmall.copy(
|
||||||
|
color = textColor,
|
||||||
|
fontSize = (fontSize + 4).sp
|
||||||
|
),
|
||||||
|
h4 = MaterialTheme.typography.titleLarge.copy(
|
||||||
|
color = textColor,
|
||||||
|
fontSize = (fontSize + 2).sp
|
||||||
|
),
|
||||||
|
h5 = MaterialTheme.typography.titleMedium.copy(
|
||||||
|
color = textColor,
|
||||||
|
fontSize = (fontSize + 1).sp
|
||||||
|
),
|
||||||
|
h6 = MaterialTheme.typography.titleSmall.copy(
|
||||||
|
color = textColor,
|
||||||
|
fontSize = fontSize.sp
|
||||||
|
),
|
||||||
|
text = TextStyle(
|
||||||
|
color = textColor,
|
||||||
|
fontSize = fontSize.sp,
|
||||||
|
lineHeight = lineHeight.sp
|
||||||
|
),
|
||||||
|
paragraph = TextStyle(
|
||||||
|
color = textColor,
|
||||||
|
fontSize = fontSize.sp,
|
||||||
|
lineHeight = lineHeight.sp
|
||||||
|
),
|
||||||
|
ordered = TextStyle(
|
||||||
|
color = textColor,
|
||||||
|
fontSize = fontSize.sp,
|
||||||
|
lineHeight = lineHeight.sp
|
||||||
|
),
|
||||||
|
bullet = TextStyle(
|
||||||
|
color = textColor,
|
||||||
|
fontSize = fontSize.sp,
|
||||||
|
lineHeight = lineHeight.sp
|
||||||
|
),
|
||||||
|
quote = TextStyle(
|
||||||
|
color = textColor.copy(alpha = 0.8f),
|
||||||
|
fontSize = fontSize.sp,
|
||||||
|
lineHeight = lineHeight.sp
|
||||||
|
),
|
||||||
|
code = TextStyle(
|
||||||
|
color = textColor,
|
||||||
|
fontSize = (fontSize - 2).sp,
|
||||||
|
lineHeight = (lineHeight - 4).sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val colors = markdownColor(
|
||||||
|
text = textColor,
|
||||||
|
codeText = textColor,
|
||||||
|
linkText = linkColor,
|
||||||
|
codeBackground = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
inlineCodeBackground = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
dividerColor = MaterialTheme.colorScheme.outlineVariant
|
||||||
|
)
|
||||||
|
|
||||||
|
Markdown(
|
||||||
|
content = content,
|
||||||
|
modifier = modifier,
|
||||||
|
colors = colors,
|
||||||
|
typography = typography
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user