diff --git a/demo/build.2.0.ohos.gradle.kts b/demo/build.2.0.ohos.gradle.kts index 6af5991d9..899abab65 100644 --- a/demo/build.2.0.ohos.gradle.kts +++ b/demo/build.2.0.ohos.gradle.kts @@ -45,7 +45,7 @@ kotlin { implementation(project(":core-annotations")) implementation(project(":compose")) // Chat Demo 相关依赖 -// implementation("com.tencent.kuiklybase:markdown:0.2.0-ohos") + implementation("com.tencent.kuiklybase:markdown:0.2.0-ohos") } } diff --git a/demo/build.gradle.kts b/demo/build.gradle.kts index c50141ce5..9193bafd8 100644 --- a/demo/build.gradle.kts +++ b/demo/build.gradle.kts @@ -61,7 +61,7 @@ kotlin { implementation(project(":core-annotations")) // compileOnly(project(":core-annotations")) // Chat Demo 相关依赖 -// implementation("com.tencent.kuiklybase:markdown:0.2.0") + implementation("com.tencent.kuiklybase:markdown:0.2.0") implementation("io.ktor:ktor-client-core:2.3.10") } } diff --git a/demo/src/androidMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.android.kt b/demo/src/androidMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.android.kt index bb96f664e..1ee3d36d5 100644 --- a/demo/src/androidMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.android.kt +++ b/demo/src/androidMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.android.kt @@ -1,9 +1,9 @@ -//package com.tencent.kuikly.demo.pages.compose.chatDemo -// -//import io.ktor.client.HttpClient -//import io.ktor.client.engine.okhttp.OkHttp -// -//internal actual object NetworkClient { -// actual val client: Any? -// get() = HttpClient(OkHttp) -//} \ No newline at end of file +package com.tencent.kuikly.demo.pages.compose.chatDemo + +import io.ktor.client.HttpClient +import io.ktor.client.engine.okhttp.OkHttp + +internal actual object NetworkClient { + actual val client: Any? + get() = HttpClient(OkHttp) +} \ No newline at end of file diff --git a/demo/src/commonMain/assets/ChatDemo/kuikly_logo.png b/demo/src/commonMain/assets/ChatDemo/kuikly_logo.png new file mode 100644 index 000000000..acf7d67c5 Binary files /dev/null and b/demo/src/commonMain/assets/ChatDemo/kuikly_logo.png differ diff --git a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.kt b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.kt index 577ad3b0e..b193726f1 100644 --- a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.kt +++ b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.kt @@ -1,686 +1,501 @@ -//package com.tencent.kuikly.demo.pages.compose.chatDemo -// -//import androidx.compose.runtime.Composable -//import androidx.compose.runtime.LaunchedEffect -//import androidx.compose.runtime.mutableStateOf -//import androidx.compose.runtime.remember -//import androidx.compose.runtime.getValue -//import androidx.compose.runtime.mutableStateListOf -//import androidx.compose.runtime.setValue -//import com.tencent.kuikly.compose.ComposeContainer -//import com.tencent.kuikly.compose.extension.keyboardHeightChange -//import com.tencent.kuikly.compose.foundation.Canvas -//import com.tencent.kuikly.compose.foundation.Image -//import com.tencent.kuikly.compose.foundation.background -//import com.tencent.kuikly.compose.foundation.clickable -//import com.tencent.kuikly.compose.foundation.layout.Arrangement -//import com.tencent.kuikly.compose.foundation.layout.Box -//import com.tencent.kuikly.compose.foundation.layout.Column -//import com.tencent.kuikly.compose.foundation.layout.Row -//import com.tencent.kuikly.compose.foundation.layout.Spacer -//import com.tencent.kuikly.compose.foundation.layout.fillMaxSize -//import com.tencent.kuikly.compose.foundation.layout.fillMaxWidth -//import com.tencent.kuikly.compose.foundation.layout.height -//import com.tencent.kuikly.compose.foundation.layout.padding -//import com.tencent.kuikly.compose.foundation.layout.size -//import com.tencent.kuikly.compose.foundation.layout.width -//import com.tencent.kuikly.compose.foundation.layout.widthIn -//import com.tencent.kuikly.compose.foundation.lazy.LazyColumn -//import com.tencent.kuikly.compose.foundation.lazy.LazyRow -//import com.tencent.kuikly.compose.foundation.lazy.items -//import com.tencent.kuikly.compose.foundation.lazy.itemsIndexed -//import com.tencent.kuikly.compose.foundation.lazy.rememberLazyListState -//import com.tencent.kuikly.compose.foundation.shape.CircleShape -//import com.tencent.kuikly.compose.foundation.shape.RoundedCornerShape -//import com.tencent.kuikly.compose.material3.Text -//import com.tencent.kuikly.compose.material3.TextField -//import com.tencent.kuikly.compose.material3.TextFieldDefaults -//import com.tencent.kuikly.compose.resources.DrawableResource -//import com.tencent.kuikly.compose.resources.InternalResourceApi -//import com.tencent.kuikly.compose.resources.painterResource -//import com.tencent.kuikly.compose.setContent -//import com.tencent.kuikly.compose.ui.Alignment -//import com.tencent.kuikly.compose.ui.Modifier -//import com.tencent.kuikly.compose.ui.draw.clip -//import com.tencent.kuikly.compose.ui.graphics.Color -//import com.tencent.kuikly.compose.ui.graphics.Path -//import com.tencent.kuikly.compose.ui.text.font.FontWeight -//import com.tencent.kuikly.compose.ui.unit.Dp -//import com.tencent.kuikly.compose.ui.unit.dp -//import com.tencent.kuikly.compose.ui.unit.sp -//import com.tencent.kuikly.core.annotations.Page -//import com.tencent.kuikly.core.base.attr.ImageUri -//import com.tencent.kuikly.core.coroutines.GlobalScope -//import com.tencent.kuikly.core.coroutines.launch -//import com.tencent.kuikly.core.module.RouterModule -//import com.tencent.kuikly.core.nvi.serialization.json.JSONObject -//import com.tencent.kuiklybase.markdown.compose.Markdown -//import com.tencent.kuiklybase.markdown.model.rememberMarkdownState -//import kotlinx.coroutines.delay -// -//internal expect object NetworkClient { -// val client: Any? -//} -// -//@Page("ChatDemo") -//internal class ChatDemo : ComposeContainer() { -// -// override fun willInit() { -// super.willInit() -// setContent { -// ChatScreen() -// } -// } -// -// @Composable -// internal fun ChatScreen() { -// var inputText by remember { mutableStateOf("") } -// val chatList = remember { mutableStateListOf() } -// var keyboardHeight by remember { mutableStateOf(0f) } -// val promptBoxes = listOf( -// PromptBox("\uD83C\uDF93 高考指南", "请帮我分析高考志愿填报方案,结合我的成绩和兴趣给出建议"), -// PromptBox("\u2600\uFE0F 健康助手", "请告诉我高考期间如何保持身体健康和心理状态"), -// PromptBox("\uD83D\uDCFA 文娱节目单", "请推荐一些适合学生放松的文娱节目或电影"), -// PromptBox("\uD83D\uDCB0 今日金价", "请告诉我今天的黄金价格走势和投资建议"), -// ) -// -// // 聊天列表滚动状态 -// val listState = rememberLazyListState() -// -// Box( -// modifier = Modifier -// .fillMaxSize() -// .background(Color(0xFFF4F4FE)) -// ) { -// // 顶部导航栏区(固定) -// Column( -// modifier = Modifier -// .fillMaxWidth() -// .align(Alignment.TopStart) -// ) { -// // 状态栏占位 -// Spacer(modifier = Modifier.height(pagerData.statusBarHeight.dp)) -// -// // 导航栏 -// NavBar(onBack = { -// getPager().acquireModule(RouterModule.MODULE_NAME).closePage() -// }) -// -// // 聊天列表 -// if (chatList.isNotEmpty()) { -// LazyColumn( -// modifier = Modifier -// .weight(1f) -// .fillMaxWidth(), -// state = listState -// ) { -// itemsIndexed(chatList) { index, message -> -// Row( -// modifier = Modifier -// .fillMaxWidth() -// .padding(horizontal = 6.dp, vertical = 6.dp), -// horizontalArrangement = if (index % 2 == 0) Arrangement.End else Arrangement.Start -// ) { -// ChatMessageItem( -// message = message, -// isUser = (index % 2 == 0), -// maxWidth = (0.7f * pagerData.pageViewWidth).dp -// ) -// } -// } -// item { -// Spacer(modifier = Modifier.height(1.dp)) -// } -// } -// LaunchedEffect(chatList.size) { -// if (chatList.isNotEmpty()) { -// listState.animateScrollToItem(chatList.size) -// } -// } -// } else { -// welcome( -// onInputTextChange = { inputText = it }, -// modifier = Modifier.weight(1f) -// ) -// } -// -// Column( -// modifier = Modifier -// .padding(bottom = keyboardHeight.dp) -// ) { -// LazyRow( -// modifier = Modifier -// .fillMaxWidth() -// .padding(vertical = 8.dp, horizontal = 10.dp) -// ) { -// items(promptBoxes) { box -> -// Box( -// modifier = Modifier -// .padding(end = 8.dp) -// .clip(RoundedCornerShape(8.dp)) -// .background(Color.White) -// .clickable { -// inputText = box.prompt -// } -// .padding(horizontal = 16.dp, vertical = 8.dp) -// ) { -// Text( -// text = box.title, -// color = Color.Black, -// fontSize = 15.sp -// ) -// } -// } -// } -// -// // 输入栏 -// Row( -// modifier = Modifier -// .fillMaxWidth() -// .padding(horizontal = 10.dp) -// .padding(bottom = 10.dp), -// verticalAlignment = Alignment.CenterVertically, -// ) { -// Box(modifier = Modifier.weight(1f)) { -// TextField( -// value = inputText, -// onValueChange = { inputText = it }, -// modifier = Modifier -// .padding(end = 40.dp) // 给右侧按钮留出空间 -// .fillMaxWidth() -// .keyboardHeightChange { -// keyboardHeight = it.height -// }, -// placeholder = { Text(PLACEHOLDER) }, -// shape = RoundedCornerShape(16.dp), -// colors = TextFieldDefaults.colors( -// unfocusedContainerColor = Color.White, -// focusedContainerColor = Color.White -// ) -// ) -// } -// -// -// Spacer(modifier = Modifier.width(10.dp)) -// -// @OptIn(InternalResourceApi::class) -// val sendDrawable = -// DrawableResource(ImageUri.pageAssets(SEND_ICON).toUrl("ChatDemo")) -// -// Image( -// painter = painterResource(sendDrawable), -// contentDescription = "Send", -// modifier = Modifier -// .size(30.dp) -// .clickable(enabled = inputText.isNotBlank()) { -// val messageToSend = inputText -// inputText = "" -// -// chatList.add(messageToSend) -// -// /* -// GlobalScope.launch { -// // Android 和 iOS 通过ktor接口发送并处理接收消息 -// sendStreamMessage( -// client = NetworkClient.client as HttpClient, -// url = CHAT_URL, -// model = CHAT_MODEL, -// apiKey = CHAT_API_KEY, -// prompt = messageToSend, -// chatList = chatList -// ) -// -// // OHOS 通过原生的桥接模块发送并处理接收消息 -// sendOhosMessage( -// url = CHAT_URL, -// model = CHAT_MODEL, -// apiKey = CHAT_API_KEY, -// prompt = messageToSend, -// chatList = chatList -// ) -// } -// */ -// -// GlobalScope.launch { -// chatList.add("") -// markdown.forEachIndexed { index, _ -> -// delay(16) -// chatList[chatList.lastIndex] = -// markdown.substring(0, index + 1) -// } -// } -// } -// ) -// } -// } -// } -// } -// } -// -// @Composable -// fun NavBar(onBack: () -> Unit) { -// // 顶部导航栏 -// Row( -// modifier = Modifier -// .fillMaxWidth() -// .height(44.dp) -// .padding(horizontal = 12.dp), -// verticalAlignment = Alignment.CenterVertically -// ) { -// @OptIn(InternalResourceApi::class) -// val drawable = DrawableResource(ImageUri.pageAssets(BACK_ICON).toUrl("ChatDemo")) -// Image( -// painter = painterResource(drawable), -// contentDescription = "Back", -// modifier = Modifier -// .size(16.dp) -// .clickable { onBack() } -// ) -// Spacer(modifier = Modifier.weight(1f)) -// Text( -// text = "新闻弟", -// fontSize = 17.sp, -// fontWeight = FontWeight.Bold, -// color = Color.Black, -// modifier = Modifier.align(Alignment.CenterVertically) -// ) -// Spacer(modifier = Modifier.weight(1f)) -// Box(modifier = Modifier.width(20.dp)) -// } -// -// // 横线分割线 -// Box( -// modifier = Modifier -// .fillMaxWidth() -// .height(1.dp) -// .background(Color(0xFFE3E3E3)) -// ) -// } -// -// @Composable -// fun welcome(onInputTextChange: (String) -> Unit, -// modifier: Modifier = Modifier) { -// Column( -// modifier = modifier, -// horizontalAlignment = Alignment.CenterHorizontally -// ) { -// Spacer(modifier = Modifier.height(32.dp)) -// @OptIn(InternalResourceApi::class) -// val sendDrawable = DrawableResource(ImageUri.pageAssets(AVATAR).toUrl("ChatDemo")) -// // 第一行:居中的图片 -// Image( -// painter = painterResource(sendDrawable), -// contentDescription = "Welcome", -// modifier = Modifier -// .size(140.dp) -// .clip(CircleShape) -// ) -// Spacer(modifier = Modifier.height(32.dp)) -// -// Text( -// text = "了解热点 向我提问", -// fontSize = 24.sp, -// color = Color.Black, -// fontWeight = FontWeight.Bold -// ) -// Spacer(modifier = Modifier.height(32.dp)) -// -// // 第二行:两个并列的 Box -// Row( -// modifier = Modifier -// .fillMaxWidth() -// .padding(horizontal = 32.dp), -// horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally), -// verticalAlignment = Alignment.CenterVertically -// ) { -// Box( -// modifier = Modifier -// .weight(1f) -// .clip(RoundedCornerShape(12.dp)) -// .background(Color.White) -// .clickable { onInputTextChange("请帮我分析高考志愿填报方案,结合我的成绩和兴趣给出建议") } -// .padding(vertical = 16.dp, horizontal = 2.dp), -// contentAlignment = Alignment.Center -// ) { -// Column( -// horizontalAlignment = Alignment.Start -// ) { -// Text("\uD83C\uDF93 高考志愿分析", fontSize = 16.sp, color = Color.Black) -// Spacer(modifier = Modifier.height(10.dp)) -// Text("高考之路,有我护航", fontSize = 14.sp, color = Color(0xFF888888)) -// } -// } -// Box( -// modifier = Modifier -// .weight(1f) -// .clip(RoundedCornerShape(12.dp)) -// .background(Color.White) -// .clickable { onInputTextChange("请帮我分析高考志愿填报方案,结合我的成绩和兴趣给出建议") } -// .padding(vertical = 16.dp, horizontal = 2.dp), -// contentAlignment = Alignment.Center -// ) { -// Column( -// horizontalAlignment = Alignment.Start -// ) { -// Text("\u26BD 世界杯观赛助手", fontSize = 16.sp, color = Color.Black) -// Spacer(modifier = Modifier.height(10.dp)) -// Text(" ", fontSize = 14.sp, color = Color(0xFF888888)) -// } -// } -// } -// Spacer(modifier = Modifier.height(16.dp)) -// -// // 第三行:两个并列的 Box -// Row( -// modifier = Modifier -// .fillMaxWidth() -// .padding(horizontal = 32.dp), -// horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally), -// verticalAlignment = Alignment.CenterVertically -// ) { -// Box( -// modifier = Modifier -// .weight(1f) -// .clip(RoundedCornerShape(12.dp)) -// .background(Color.White) -// .clickable { onInputTextChange("健康助手prompt") } -// .padding(vertical = 16.dp, horizontal = 2.dp), -// contentAlignment = Alignment.Center -// ) { -// Column( -// horizontalAlignment = Alignment.Start -// ) { -// Text("\u2600\uFE0F 医学健康助手", fontSize = 16.sp, color = Color.Black) -// Spacer(modifier = Modifier.height(10.dp)) -// Text("专业、科学", fontSize = 14.sp, color = Color(0xFF888888)) -// } -// } -// Box( -// modifier = Modifier -// .weight(1f) -// .clip(RoundedCornerShape(12.dp)) -// .background(Color.White) -// .clickable { onInputTextChange("新闻妹高考送祝福prompt") } -// .padding(vertical = 16.dp, horizontal = 2.dp), -// contentAlignment = Alignment.Center -// ) { -// Column( -// horizontalAlignment = Alignment.Start -// ) { -// Text("新闻妹高考送祝福", fontSize = 16.sp, color = Color.Black) -// Spacer(modifier = Modifier.height(10.dp)) -// Text("祝各位考生金榜题名", fontSize = 14.sp, color = Color(0xFF888888)) -// } -// } -// } -// Spacer(modifier = Modifier.height(16.dp)) -// -// // 第四行:两个并列的 Box -// Row( -// modifier = Modifier -// .fillMaxWidth() -// .padding(horizontal = 32.dp), -// horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally), -// verticalAlignment = Alignment.CenterVertically -// ) { -// Box( -// modifier = Modifier -// .weight(1f) -// .clip(RoundedCornerShape(12.dp)) -// .background(Color.White) -// .padding(vertical = 16.dp), -// contentAlignment = Alignment.Center -// ) { -// Row( -// verticalAlignment = Alignment.CenterVertically, -// horizontalArrangement = Arrangement.Center -// ) { -// @OptIn(InternalResourceApi::class) -// val explore = DrawableResource(ImageUri.pageAssets(EXPL_ICON).toUrl("ChatDemo")) -// Image( -// painter = painterResource(explore), -// contentDescription = null, -// modifier = Modifier -// .size(16.dp) // 小图标大小 -// ) -// Spacer(modifier = Modifier.width(6.dp)) -// Text("发现更多", fontSize = 16.sp, color = Color.Black) -// } -// } -// Box( -// modifier = Modifier -// .weight(1f) -// .clip(RoundedCornerShape(12.dp)) -// .background(Color.White) -// .padding(vertical = 16.dp), -// contentAlignment = Alignment.Center -// ) { -// @OptIn(InternalResourceApi::class) -// val fresh = DrawableResource(ImageUri.pageAssets(FRSH_ICON).toUrl("ChatDemo")) -// Row( -// verticalAlignment = Alignment.CenterVertically, -// horizontalArrangement = Arrangement.Center -// ) { -// Image( -// painter = painterResource(fresh), -// contentDescription = null, -// modifier = Modifier -// .size(16.dp) // 小图标大小 -// ) -// Spacer(modifier = Modifier.width(6.dp)) -// Text("换一批", fontSize = 16.sp, color = Color.Black) -// } -// } -// } -// } -// } -// -// -// @Composable -// fun ChatMessageItem( -// message: String, -// isUser: Boolean, -// maxWidth: Dp -// ) { -// if (isUser) { -// Box( -// modifier = Modifier -// .widthIn(max = maxWidth) -// .padding(bottom = 4.dp, end = 8.dp) -// ) { -// Row( -// verticalAlignment = Alignment.CenterVertically -// ) { -// Box( -// modifier = Modifier -// .background( -// color = Color(0xFFE9E9EB), -// shape = RoundedCornerShape(8.dp) -// ) -// .padding(horizontal = 10.dp, vertical = 10.dp) -// ) { -// Text( -// text = message, -// fontSize = 14.sp, -// color = Color.Black -// ) -// } -// // 右侧三角 -// Canvas( -// modifier = Modifier -// .size(6.dp, 12.dp) -// .align(Alignment.CenterVertically) -// ) { -// val width = size.width -// val height = size.height -// val path = Path().apply { -// moveTo(0f, 0f) // Box 右侧边的上点 -// lineTo(0f, height) // Box 右侧边的下点 -// lineTo(width, height / 2f) // 三角顶点 -// close() -// } -// drawPath( -// path = path, -// color = Color(0xFFE9E9EB) -// ) -// } -// } -// } -// } else { -// val markdownState = rememberMarkdownState() -// LaunchedEffect(message) { -// markdownState.parse(message, false) -// } -// Markdown( -// state = markdownState, -// colors = markdownColor(text = Color.Black), -// typography = markdownTypography(), -// modifier = Modifier -// .widthIn(max = pagerData.pageViewWidth.dp) -// .padding(horizontal = 24.dp) -// ) -// } -// } -// -// /* 安卓和iOS使用(ktor请求方式) -// private suspend fun sendStreamMessage( -// client: HttpClient, -// url: String, -// model: String, -// apiKey: String, -// prompt: String, -// chatList: MutableList, -// ) { -// try { -// withContext(Dispatchers.Main) { -// chatList.add("") -// } -// val msgIndex = chatList.lastIndex -// var streamingMsg = "" -// -// withContext(Dispatchers.IO) { -// val response: HttpResponse = client.post(url) { -// headers { -// append("Authorization", "Bearer $apiKey") -// append("Content-Type", "application/json") -// append("Accept", "text/event-stream") -// } -// setBody( -// """ -// { -// "model": "$model", -// "messages": [{"role": "user", "content": "$prompt"}], -// "stream": true -// } -// """.trimIndent() -// ) -// } -// -// val channel: ByteReadChannel = response.bodyAsChannel() -// -// while (!channel.isClosedForRead) { -// val line = channel.readUTF8Line() ?: break -// if (line.startsWith("data:")) { -// val data = line.removePrefix("data: ").trim() -// if (data == "[DONE]") break -// val delta = extractContentFromDelta(data) -// if (delta.isNotEmpty()) { -// streamingMsg += delta -// withContext(Dispatchers.Main) { -// chatList[msgIndex] = streamingMsg -// } -// } -// } -// } -// } -// } catch (e: Exception) { -// withContext(Dispatchers.Main) { -// chatList.add("[出错:${e.message}]") -// } -// } -// } -// */ -// -// // ohos使用(原生模块方式) -// private fun sendOhosMessage( -// url: String, -// model: String, -// apiKey: String, -// prompt: String, -// chatList: MutableList -// ) { -// chatList.add(prompt) -// chatList.add("") -// val msgIndex = chatList.lastIndex -// println("prompt: $prompt") -// -// getPager().acquireModule(OhosStreamRequestModule.MODULE_NAME) -// .request(url, model, apiKey, prompt) { event -> -// -// when (event?.optString("event")) { -// "data" -> { -// // ArkTS端每次推送一段流式内容 -// val delta = extractContentFromDelta(event.optString("data")) -// if (delta.isNotEmpty()) { -// chatList[msgIndex] = chatList[msgIndex] + delta -// println(chatList[msgIndex]) -// } -// } -// "error" -> { -// chatList.add("[出错:${event.optString("data")}]") -// } -// } -// } -// } -// -// private fun extractContentFromDelta(delta: String): String { -// val json = JSONObject(delta) -// val choices = json.optJSONArray("choices") -// if (choices != null && choices.length() > 0) { -// val firstChoice = choices.optJSONObject(0) -// val deltaObj = firstChoice?.optJSONObject("delta") -// if (deltaObj != null) { -// return deltaObj.optString("content", "") -// } -// } -// return "" -// } -// -// -// companion object { -// private const val BACK_ICON = "ic_back.png" -// private const val SEND_ICON = "ic_send.png" -// private const val FRSH_ICON = "ic_fresh.png" -// private const val EXPL_ICON = "ic_explore.png" -// private const val AVATAR = "avatar.jpg" -// private const val PLACEHOLDER = "Type something..." -// private const val CHAT_URL = "https://api.hunyuan.cloud.tencent.com/v1/chat/completions" -// private const val CHAT_MODEL = "hunyuan-turbos-latest" -// private const val CHAT_API_KEY = "" -// private val markdown = """ -// # 一级标题 -// ## 二级标题 -// 这是一段模拟AI回复的markdown文本,**这是一段AI回复的加粗markdown文本** -// *这是一段模拟AI回复的斜体markdown文本* -// -// ~~这是一段模拟AI回复的删除线markdown文本~~ -// > 这是一段AI引用的markdown文本 -// -// | 列1 | 列2 | -// |------------|-----------| -// | 数据1 [1](@ref) | 数据2 | -// | 示例A | 示例B | -// | 测试1 | 测试2 | -// | 临时A | 临时B | -// -// 这是一段AI回复的无序列表: -// - 项目1 -// """.trimIndent() -// } -// -//} -// -//internal data class PromptBox( -// val title: String, -// val prompt: String -//) +package com.tencent.kuikly.demo.pages.compose.chatDemo + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.setValue +import com.tencent.kuikly.compose.ComposeContainer +import com.tencent.kuikly.compose.extension.keyboardHeightChange +import com.tencent.kuikly.compose.foundation.Canvas +import com.tencent.kuikly.compose.foundation.Image +import com.tencent.kuikly.compose.foundation.background +import com.tencent.kuikly.compose.foundation.clickable +import com.tencent.kuikly.compose.foundation.layout.Arrangement +import com.tencent.kuikly.compose.foundation.layout.Box +import com.tencent.kuikly.compose.foundation.layout.Column +import com.tencent.kuikly.compose.foundation.layout.Row +import com.tencent.kuikly.compose.foundation.layout.Spacer +import com.tencent.kuikly.compose.foundation.layout.fillMaxSize +import com.tencent.kuikly.compose.foundation.layout.fillMaxWidth +import com.tencent.kuikly.compose.foundation.layout.height +import com.tencent.kuikly.compose.foundation.layout.padding +import com.tencent.kuikly.compose.foundation.layout.size +import com.tencent.kuikly.compose.foundation.layout.width +import com.tencent.kuikly.compose.foundation.layout.widthIn +import com.tencent.kuikly.compose.foundation.lazy.LazyColumn +import com.tencent.kuikly.compose.foundation.lazy.LazyRow +import com.tencent.kuikly.compose.foundation.lazy.items +import com.tencent.kuikly.compose.foundation.lazy.itemsIndexed +import com.tencent.kuikly.compose.foundation.lazy.rememberLazyListState +import com.tencent.kuikly.compose.foundation.shape.CircleShape +import com.tencent.kuikly.compose.foundation.shape.RoundedCornerShape +import com.tencent.kuikly.compose.material3.Text +import com.tencent.kuikly.compose.material3.TextField +import com.tencent.kuikly.compose.material3.TextFieldDefaults +import com.tencent.kuikly.compose.resources.DrawableResource +import com.tencent.kuikly.compose.resources.InternalResourceApi +import com.tencent.kuikly.compose.resources.painterResource +import com.tencent.kuikly.compose.setContent +import com.tencent.kuikly.compose.ui.Alignment +import com.tencent.kuikly.compose.ui.Modifier +import com.tencent.kuikly.compose.ui.draw.clip +import com.tencent.kuikly.compose.ui.graphics.Color +import com.tencent.kuikly.compose.ui.graphics.Path +import com.tencent.kuikly.compose.ui.text.font.FontWeight +import com.tencent.kuikly.compose.ui.unit.Dp +import com.tencent.kuikly.compose.ui.unit.dp +import com.tencent.kuikly.compose.ui.unit.sp +import com.tencent.kuikly.core.annotations.Page +import com.tencent.kuikly.core.base.attr.ImageUri +import com.tencent.kuikly.core.coroutines.GlobalScope +import com.tencent.kuikly.core.coroutines.launch +import com.tencent.kuikly.core.module.RouterModule +import com.tencent.kuikly.core.nvi.serialization.json.JSONObject +import com.tencent.kuiklybase.markdown.compose.Markdown +import com.tencent.kuiklybase.markdown.model.rememberMarkdownState +import kotlinx.coroutines.delay + +internal expect object NetworkClient { + val client: Any? +} + +@Page("ChatDemo") +internal class ChatDemo : ComposeContainer() { + + override fun willInit() { + super.willInit() + setContent { + ChatScreen() + } + } + + @Composable + internal fun ChatScreen() { + var inputText by remember { mutableStateOf("") } + val chatList = remember { mutableStateListOf() } + var keyboardHeight by remember { mutableStateOf(0f) } + + // 聊天列表滚动状态 + val listState = rememberLazyListState() + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF4F4FE)) + ) { + // 顶部导航栏区(固定) + Column( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.TopStart) + ) { + // 状态栏占位 + Spacer(modifier = Modifier.height(pagerData.statusBarHeight.dp)) + + // 导航栏 + NavBar(onBack = { + getPager().acquireModule(RouterModule.MODULE_NAME).closePage() + }) + + // 聊天列表 + if (chatList.isNotEmpty()) { + LazyColumn( + modifier = Modifier + .weight(1f) + .fillMaxWidth(), + state = listState + ) { + itemsIndexed(chatList) { index, message -> + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 6.dp, vertical = 6.dp), + horizontalArrangement = if (index % 2 == 0) Arrangement.End else Arrangement.Start + ) { + ChatMessageItem( + message = message, + isUser = (index % 2 == 0), + maxWidth = (0.7f * pagerData.pageViewWidth).dp + ) + } + } + item { + Spacer(modifier = Modifier.height(1.dp)) + } + } + LaunchedEffect(chatList.size) { + if (chatList.isNotEmpty()) { + listState.animateScrollToItem(chatList.size) + } + } + } else { + welcome( + onInputTextChange = { inputText = it }, + modifier = Modifier.weight(1f) + ) + } + + Column( + modifier = Modifier + .padding(bottom = keyboardHeight.dp) + ) { + + // 输入栏 + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 10.dp) + .padding(bottom = 10.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Box(modifier = Modifier.weight(1f)) { + TextField( + value = inputText, + onValueChange = { inputText = it }, + modifier = Modifier + .padding(end = 40.dp) // 给右侧按钮留出空间 + .fillMaxWidth() + .keyboardHeightChange { + keyboardHeight = it.height + }, + placeholder = { Text(PLACEHOLDER) }, + shape = RoundedCornerShape(16.dp), + colors = TextFieldDefaults.colors( + unfocusedContainerColor = Color.White, + focusedContainerColor = Color.White + ) + ) + } + + + Spacer(modifier = Modifier.width(10.dp)) + + @OptIn(InternalResourceApi::class) + val sendDrawable = + DrawableResource(ImageUri.pageAssets(SEND_ICON).toUrl("ChatDemo")) + + Image( + painter = painterResource(sendDrawable), + contentDescription = "Send", + modifier = Modifier + .size(30.dp) + .clickable(enabled = inputText.isNotBlank()) { + val messageToSend = inputText + inputText = "" + chatList.add(messageToSend) + GlobalScope.launch { + chatList.add("") + markdown.forEachIndexed { index, _ -> + delay(16) + chatList[chatList.lastIndex] = + markdown.substring(0, index + 1) + } + } + } + ) + } + } + } + } + } + + @Composable + fun NavBar(onBack: () -> Unit) { + // 顶部导航栏 + Row( + modifier = Modifier + .fillMaxWidth() + .height(44.dp) + .padding(horizontal = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + @OptIn(InternalResourceApi::class) + val drawable = DrawableResource(ImageUri.pageAssets(BACK_ICON).toUrl("ChatDemo")) + Image( + painter = painterResource(drawable), + contentDescription = "Back", + modifier = Modifier + .size(16.dp) + .clickable { onBack() } + ) + Spacer(modifier = Modifier.weight(1f)) + Text( + text = "AI Chat", + fontSize = 17.sp, + fontWeight = FontWeight.Bold, + color = Color.Black, + modifier = Modifier.align(Alignment.CenterVertically) + ) + Spacer(modifier = Modifier.weight(1f)) + Box(modifier = Modifier.width(20.dp)) + } + + // 横线分割线 + Box( + modifier = Modifier + .fillMaxWidth() + .height(1.dp) + .background(Color(0xFFE3E3E3)) + ) + } + + @Composable + fun welcome(onInputTextChange: (String) -> Unit, + modifier: Modifier = Modifier) { + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(32.dp)) + + // Kuikly logo + @OptIn(InternalResourceApi::class) + val logoDrawable = DrawableResource(ImageUri.pageAssets(LOGO_ICON).toUrl("ChatDemo")) + Image( + painter = painterResource(logoDrawable), + contentDescription = "Kuikly Logo", + modifier = Modifier + .width(240.dp) + .height(70.dp) + ) + + Spacer(modifier = Modifier.height(40.dp)) + + // 使用循环生成卡片 - 艺术化设计 + val promptBoxes = listOf( + PromptBox( + "\uD83C\uDF93 高考志愿分析", + "请帮我分析高考志愿填报方案,结合我的成绩和兴趣给出建议", + "高考之路,有我护航", + Color(0xFFCDC4BB) + ), + PromptBox( + "\u26BD 世界杯观赛助手", + "分析今天的世界杯战况如何", + "分析比赛战况", + Color(0xFFFEE1D3) + ), + PromptBox( + "\u2600\uFE0F 医学健康助手", + "请给出健康生活建议", + "专业、科学", + Color(0xFFF6BEBD) + ), + PromptBox( + "\uD83C\uDF89 高考送祝福", + "请写一段高考祝福语,祝考生金榜题名", + "祝各位考生金榜题名", + Color(0xFFCFAAA1) + ), + PromptBox( + "\uD83D\uDCDA 学习计划助手", + "帮我制定一个高效的学习计划,提升学习效率", + "科学规划,高效学习", + Color(0xFFD4E4F7) + ), + PromptBox( + "\uD83C\uDFA8 创意写作助手", + "帮我写一篇富有创意的短文或故事", + "激发灵感,妙笔生花", + Color(0xFFE8D5F2) + ) + ) + // kuikly logo + + promptBoxes.forEachIndexed { _, box -> + + Box( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + contentAlignment = Alignment.Center + ) { + Box( + modifier = Modifier + .fillMaxWidth(0.92f) + .clip(RoundedCornerShape(16.dp)) + .background( + com.tencent.kuikly.compose.ui.graphics.Brush.Companion.horizontalGradient( + colors = listOf(box.startColor, box.endColor) + ) + ) + .clickable { onInputTextChange(box.prompt) } + .padding(vertical = 16.dp, horizontal = 18.dp) + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = box.title, + fontSize = 20.sp, + color = Color.Black, + fontWeight = FontWeight.Bold + ) + Spacer(modifier = Modifier.height(6.dp)) + Text( + text = box.subtitle, + fontSize = 15.sp, + color = Color.Black.copy(alpha = 0.9f) + ) + } + } + } + Spacer(modifier = Modifier.height(10.dp)) + } + + } + } + + + @Composable + fun ChatMessageItem( + message: String, + isUser: Boolean, + maxWidth: Dp + ) { + if (isUser) { + Box( + modifier = Modifier + .widthIn(max = maxWidth) + .padding(bottom = 4.dp, end = 8.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .background( + color = Color(0xFFE9E9EB), + shape = RoundedCornerShape(8.dp) + ) + .padding(horizontal = 10.dp, vertical = 10.dp) + ) { + Text( + text = message, + fontSize = 14.sp, + color = Color.Black + ) + } + // 右侧三角 + Canvas( + modifier = Modifier + .size(6.dp, 12.dp) + .align(Alignment.CenterVertically) + ) { + val width = size.width + val height = size.height + val path = Path().apply { + moveTo(0f, 0f) // Box 右侧边的上点 + lineTo(0f, height) // Box 右侧边的下点 + lineTo(width, height / 2f) // 三角顶点 + close() + } + drawPath( + path = path, + color = Color(0xFFE9E9EB) + ) + } + } + } + } else { + val markdownState = rememberMarkdownState() + LaunchedEffect(message) { + markdownState.parse(message, false) + } + Markdown( + state = markdownState, + colors = markdownColor(text = Color.Black), + typography = markdownTypography(), + modifier = Modifier + .widthIn(max = pagerData.pageViewWidth.dp) + .padding(horizontal = 24.dp) + ) + } + } + + // ohos使用(原生模块方式) + private fun sendOhosMessage( + url: String, + model: String, + apiKey: String, + prompt: String, + chatList: MutableList + ) { + chatList.add(prompt) + chatList.add("") + val msgIndex = chatList.lastIndex + println("prompt: $prompt") + + getPager().acquireModule(OhosStreamRequestModule.MODULE_NAME) + .request(url, model, apiKey, prompt) { event -> + + when (event?.optString("event")) { + "data" -> { + // ArkTS端每次推送一段流式内容 + val delta = extractContentFromDelta(event.optString("data")) + if (delta.isNotEmpty()) { + chatList[msgIndex] = chatList[msgIndex] + delta + println(chatList[msgIndex]) + } + } + "error" -> { + chatList.add("[出错:${event.optString("data")}]") + } + } + } + } + + private fun extractContentFromDelta(delta: String): String { + val json = JSONObject(delta) + val choices = json.optJSONArray("choices") + if (choices != null && choices.length() > 0) { + val firstChoice = choices.optJSONObject(0) + val deltaObj = firstChoice?.optJSONObject("delta") + if (deltaObj != null) { + return deltaObj.optString("content", "") + } + } + return "" + } + + + companion object { + private const val BACK_ICON = "ic_back.png" + private const val SEND_ICON = "ic_send.png" + private const val LOGO_ICON = "kuikly_logo.png" + + private const val PLACEHOLDER = "Type something..." + private val markdown = """ + # 一级标题 + ## 二级标题 + 这是一段模拟AI回复的markdown文本,**这是一段AI回复的加粗markdown文本** + *这是一段模拟AI回复的斜体markdown文本* + + ~~这是一段模拟AI回复的删除线markdown文本~~ + > 这是一段AI引用的markdown文本 + + | 列1 | 列2 | + |------------|-----------| + | 数据1 [1](@ref) | 数据2 | + | 示例A | 示例B | + | 测试1 | 测试2 | + | 临时A | 临时B | + + 这是一段AI回复的无序列表: + - 项目1 + """.trimIndent() + } + +} + +internal data class PromptBox( + val title: String, + val prompt: String, + val subtitle: String = "", + val startColor: Color = Color.White, + val endColor: Color = Color.White +) diff --git a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/Colors.kt b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/Colors.kt index 4e3f57507..4725a58d6 100644 --- a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/Colors.kt +++ b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/Colors.kt @@ -1,44 +1,44 @@ -//package com.tencent.kuikly.demo.pages.compose.chatDemo -// -//import androidx.compose.runtime.Immutable -//import androidx.compose.runtime.staticCompositionLocalOf -//import com.tencent.kuikly.compose.ui.graphics.Color -// -//internal val LocalColorScheme = staticCompositionLocalOf { LightColoScheme } -// -//@Immutable -//internal class ColorScheme( -// -// // 字体颜色 -// val t1: Color, -// val t2: Color, -// val t3: Color, -// val aigcPrompt: Color, -// val bgBlock: Color, -// val newBgBlock: Color, -// // 分割线 -// val lineFine: Color, -// val lineStroke: Color, -//) -// -//internal val LightColoScheme = ColorScheme( -// t1 = Color(0xFF333333), -// t2 = Color(0xFF5C5C5C), -// t3 = Color(0xFF999999), -// aigcPrompt = Color(0xFF505DE5), -// bgBlock = Color(0xFFF5F5F5), -// newBgBlock = Color(0xFFF7F7F7), -// lineFine = Color(0XFFF0F0F0), -// lineStroke = Color(0XFFE6E6E6), -//) -// -//internal val DarkColorScheme = ColorScheme( -// t1 = Color(0xFFD9D9D9), -// t2 = Color(0xFFA9A9A9), -// t3 = Color(0xFF696969), -// aigcPrompt = Color(0xFF7780D9), -// bgBlock = Color(0xFF262626), -// newBgBlock = Color(0xFF262626), -// lineFine = Color(0xFF292929), -// lineStroke = Color(0xFF303030), -//) +package com.tencent.kuikly.demo.pages.compose.chatDemo + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import com.tencent.kuikly.compose.ui.graphics.Color + +internal val LocalColorScheme = staticCompositionLocalOf { LightColoScheme } + +@Immutable +internal class ColorScheme( + + // 字体颜色 + val t1: Color, + val t2: Color, + val t3: Color, + val aigcPrompt: Color, + val bgBlock: Color, + val newBgBlock: Color, + // 分割线 + val lineFine: Color, + val lineStroke: Color, +) + +internal val LightColoScheme = ColorScheme( + t1 = Color(0xFF333333), + t2 = Color(0xFF5C5C5C), + t3 = Color(0xFF999999), + aigcPrompt = Color(0xFF505DE5), + bgBlock = Color(0xFFF5F5F5), + newBgBlock = Color(0xFFF7F7F7), + lineFine = Color(0XFFF0F0F0), + lineStroke = Color(0XFFE6E6E6), +) + +internal val DarkColorScheme = ColorScheme( + t1 = Color(0xFFD9D9D9), + t2 = Color(0xFFA9A9A9), + t3 = Color(0xFF696969), + aigcPrompt = Color(0xFF7780D9), + bgBlock = Color(0xFF262626), + newBgBlock = Color(0xFF262626), + lineFine = Color(0xFF292929), + lineStroke = Color(0xFF303030), +) diff --git a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MarkdownColors.kt b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MarkdownColors.kt index e5abeddeb..fae04772a 100644 --- a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MarkdownColors.kt +++ b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MarkdownColors.kt @@ -1,29 +1,29 @@ -//package com.tencent.kuikly.demo.pages.compose.chatDemo -// -//import androidx.compose.runtime.Composable -//import com.tencent.kuikly.compose.ui.graphics.Color -//import com.tencent.kuiklybase.markdown.model.DefaultMarkdownColors -//import com.tencent.kuiklybase.markdown.model.MarkdownColors -// -//@Composable -//internal fun markdownColor( -// text: Color = MdTheme.colorScheme.t1, -// codeBackground: Color = MdTheme.colorScheme.bgBlock, -// inlineCodeBackground: Color = codeBackground, -// dividerColor: Color = MdTheme.colorScheme.lineFine, -// tableBackground: Color = MdTheme.colorScheme.bgBlock, -// tableStroke: Color = MdTheme.colorScheme.lineStroke, -// tableHeaderBackground: Color = MdTheme.colorScheme.newBgBlock, -//): MarkdownColors = DefaultMarkdownColors( -// text = text, -// codeText = Color.Unspecified, -// inlineCodeText = Color.Unspecified, -// linkText = Color.Unspecified, -// codeBackground = codeBackground, -// inlineCodeBackground = inlineCodeBackground, -// dividerColor = dividerColor, -// tableText = Color.Unspecified, -// tableBackground = tableBackground, -// tableHeaderBackground = tableHeaderBackground, -// tableStroke = tableStroke -//) +package com.tencent.kuikly.demo.pages.compose.chatDemo + +import androidx.compose.runtime.Composable +import com.tencent.kuikly.compose.ui.graphics.Color +import com.tencent.kuiklybase.markdown.model.DefaultMarkdownColors +import com.tencent.kuiklybase.markdown.model.MarkdownColors + +@Composable +internal fun markdownColor( + text: Color = MdTheme.colorScheme.t1, + codeBackground: Color = MdTheme.colorScheme.bgBlock, + inlineCodeBackground: Color = codeBackground, + dividerColor: Color = MdTheme.colorScheme.lineFine, + tableBackground: Color = MdTheme.colorScheme.bgBlock, + tableStroke: Color = MdTheme.colorScheme.lineStroke, + tableHeaderBackground: Color = MdTheme.colorScheme.newBgBlock, +): MarkdownColors = DefaultMarkdownColors( + text = text, + codeText = Color.Unspecified, + inlineCodeText = Color.Unspecified, + linkText = Color.Unspecified, + codeBackground = codeBackground, + inlineCodeBackground = inlineCodeBackground, + dividerColor = dividerColor, + tableText = Color.Unspecified, + tableBackground = tableBackground, + tableHeaderBackground = tableHeaderBackground, + tableStroke = tableStroke +) diff --git a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MarkdownTypography.kt b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MarkdownTypography.kt index 0000a0d66..5c7882298 100644 --- a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MarkdownTypography.kt +++ b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MarkdownTypography.kt @@ -1,114 +1,114 @@ -//package com.tencent.kuikly.demo.pages.compose.chatDemo -// -//import androidx.compose.runtime.Composable -//import com.tencent.kuikly.compose.ui.text.TextLinkStyles -//import com.tencent.kuikly.compose.ui.text.TextStyle -//import com.tencent.kuikly.compose.ui.text.font.FontWeight -//import com.tencent.kuikly.compose.ui.unit.sp -//import com.tencent.kuiklybase.markdown.model.DefaultMarkdownTypography -//import com.tencent.kuiklybase.markdown.model.MarkdownTypography -// -//@Composable -//internal fun markdownTypography( -// h1: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t1, -// fontSize = 20.sp, -// fontWeight = FontWeight.Bold, -// lineHeight = 30.sp, -// ), -// h2: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t1, -// fontSize = 19.sp, -// fontWeight = FontWeight.Bold, -// lineHeight = 28.sp, -// ), -// h3: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t1, -// fontSize = 19.sp, -// fontWeight = FontWeight.Bold, -// lineHeight = 28.sp, -// ), -// h4: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t1, -// fontSize = 18.sp, -// fontWeight = FontWeight.Bold, -// lineHeight = 27.sp, -// ), -// h5: TextStyle = h4, -// h6: TextStyle = h4, -// -// text: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t1, -// fontSize = 16.sp, -// lineHeight = 28.sp -// ), -// -// code: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t1, -// fontSize = 14.sp, -// lineHeight = 22.sp -// ), -// -// inlineCode: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t1, -// fontSize = 14.sp, -// lineHeight = 28.sp -// ), -// -// quote: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t3, -// fontSize = 16.sp, -// lineHeight = 28.sp -// ), -// -// paragraph: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t1, -// fontSize = 16.sp, -// lineHeight = 28.sp -// ), -// -// ordered: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t1, -// fontSize = 16.sp, -// lineHeight = 28.sp -// ), -// bullet: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t1, -// fontSize = 16.sp, -// lineHeight = 28.sp -// ), -// list: TextStyle = TextStyle( -// color = MdTheme.colorScheme.t1, -// fontSize = 16.sp, -// lineHeight = 28.sp -// ), -// -// link: TextStyle = TextStyle( -// color = MdTheme.colorScheme.aigcPrompt, -// fontSize = 16.sp, -// ), -// textLink: TextLinkStyles = TextLinkStyles( -// style = link.toSpanStyle() -// ), -// -// table: TextStyle = text -// -//): MarkdownTypography = DefaultMarkdownTypography( -// h1 = h1, -// h2 = h2, -// h3 = h3, -// h4 = h4, -// h5 = h5, -// h6 = h6, -// text = text, -// quote = quote, -// code = code, -// inlineCode = inlineCode, -// paragraph = paragraph, -// ordered = ordered, -// bullet = bullet, -// list = list, -// link = link, -// textLink = textLink, -// table = table, -//) +package com.tencent.kuikly.demo.pages.compose.chatDemo + +import androidx.compose.runtime.Composable +import com.tencent.kuikly.compose.ui.text.TextLinkStyles +import com.tencent.kuikly.compose.ui.text.TextStyle +import com.tencent.kuikly.compose.ui.text.font.FontWeight +import com.tencent.kuikly.compose.ui.unit.sp +import com.tencent.kuiklybase.markdown.model.DefaultMarkdownTypography +import com.tencent.kuiklybase.markdown.model.MarkdownTypography + +@Composable +internal fun markdownTypography( + h1: TextStyle = TextStyle( + color = MdTheme.colorScheme.t1, + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + lineHeight = 30.sp, + ), + h2: TextStyle = TextStyle( + color = MdTheme.colorScheme.t1, + fontSize = 19.sp, + fontWeight = FontWeight.Bold, + lineHeight = 28.sp, + ), + h3: TextStyle = TextStyle( + color = MdTheme.colorScheme.t1, + fontSize = 19.sp, + fontWeight = FontWeight.Bold, + lineHeight = 28.sp, + ), + h4: TextStyle = TextStyle( + color = MdTheme.colorScheme.t1, + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + lineHeight = 27.sp, + ), + h5: TextStyle = h4, + h6: TextStyle = h4, + + text: TextStyle = TextStyle( + color = MdTheme.colorScheme.t1, + fontSize = 16.sp, + lineHeight = 28.sp + ), + + code: TextStyle = TextStyle( + color = MdTheme.colorScheme.t1, + fontSize = 14.sp, + lineHeight = 22.sp + ), + + inlineCode: TextStyle = TextStyle( + color = MdTheme.colorScheme.t1, + fontSize = 14.sp, + lineHeight = 28.sp + ), + + quote: TextStyle = TextStyle( + color = MdTheme.colorScheme.t3, + fontSize = 16.sp, + lineHeight = 28.sp + ), + + paragraph: TextStyle = TextStyle( + color = MdTheme.colorScheme.t1, + fontSize = 16.sp, + lineHeight = 28.sp + ), + + ordered: TextStyle = TextStyle( + color = MdTheme.colorScheme.t1, + fontSize = 16.sp, + lineHeight = 28.sp + ), + bullet: TextStyle = TextStyle( + color = MdTheme.colorScheme.t1, + fontSize = 16.sp, + lineHeight = 28.sp + ), + list: TextStyle = TextStyle( + color = MdTheme.colorScheme.t1, + fontSize = 16.sp, + lineHeight = 28.sp + ), + + link: TextStyle = TextStyle( + color = MdTheme.colorScheme.aigcPrompt, + fontSize = 16.sp, + ), + textLink: TextLinkStyles = TextLinkStyles( + style = link.toSpanStyle() + ), + + table: TextStyle = text + +): MarkdownTypography = DefaultMarkdownTypography( + h1 = h1, + h2 = h2, + h3 = h3, + h4 = h4, + h5 = h5, + h6 = h6, + text = text, + quote = quote, + code = code, + inlineCode = inlineCode, + paragraph = paragraph, + ordered = ordered, + bullet = bullet, + list = list, + link = link, + textLink = textLink, + table = table, +) diff --git a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MdTheme.kt b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MdTheme.kt index ff539ad11..7f171f266 100644 --- a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MdTheme.kt +++ b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/MdTheme.kt @@ -1,13 +1,13 @@ -//package com.tencent.kuikly.demo.pages.compose.chatDemo -// -//import androidx.compose.runtime.Composable -//import androidx.compose.runtime.ReadOnlyComposable -// -//internal object MdTheme { -// -// val colorScheme: ColorScheme -// @Composable -// @ReadOnlyComposable -// get() = LocalColorScheme.current -// -//} +package com.tencent.kuikly.demo.pages.compose.chatDemo + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable + +internal object MdTheme { + + val colorScheme: ColorScheme + @Composable + @ReadOnlyComposable + get() = LocalColorScheme.current + +} diff --git a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/OhosStreamRequestModule.kt b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/OhosStreamRequestModule.kt index f100f3c5e..891291de4 100644 --- a/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/OhosStreamRequestModule.kt +++ b/demo/src/commonMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/OhosStreamRequestModule.kt @@ -1,52 +1,52 @@ -//package com.tencent.kuikly.demo.pages.compose.chatDemo -// -//import com.tencent.kuikly.core.module.CallbackFn -//import com.tencent.kuikly.core.module.Module -//import com.tencent.kuikly.core.nvi.serialization.json.JSONObject -// -//class OhosStreamRequestModule : Module() { -// -// companion object { -// const val MODULE_NAME = "KROhosStreamRequestModule" -// } -// override fun moduleName(): String = MODULE_NAME -// -// fun request(url: String, -// model: String, -// apiKey: String, -// prompt: String, -// callbackFn: CallbackFn) { -// val params = JSONObject().apply { -// put("url", url) -// put("model", model) -// put("apiKey", apiKey) -// put("prompt", prompt) -// }.toString() -// -// toNative(true, -// "request", -// params, -// { retEvent -> -// println(retEvent) -// val jsonObj = JSONObject(retEvent.toString()) -// val dataStr = jsonObj.optString("data", "") -// -// val sseLines = dataStr.split("\n\n") -// for (line in sseLines) { -// if (line.startsWith("data: ")) { -// val jsonPart = line.removePrefix("data: ").trim() -// if (jsonPart == "[DONE]") continue -// try { -// val eventObj = JSONObject() -// eventObj.put("event", "data") -// eventObj.put("data", jsonPart) -// callbackFn.invoke(eventObj) -// } catch (e: Exception) { -// // 解析失败,忽略 -// } -// } -// } -// }, -// false) -// } -//} +package com.tencent.kuikly.demo.pages.compose.chatDemo + +import com.tencent.kuikly.core.module.CallbackFn +import com.tencent.kuikly.core.module.Module +import com.tencent.kuikly.core.nvi.serialization.json.JSONObject + +class OhosStreamRequestModule : Module() { + + companion object { + const val MODULE_NAME = "KROhosStreamRequestModule" + } + override fun moduleName(): String = MODULE_NAME + + fun request(url: String, + model: String, + apiKey: String, + prompt: String, + callbackFn: CallbackFn) { + val params = JSONObject().apply { + put("url", url) + put("model", model) + put("apiKey", apiKey) + put("prompt", prompt) + }.toString() + + toNative(true, + "request", + params, + { retEvent -> + println(retEvent) + val jsonObj = JSONObject(retEvent.toString()) + val dataStr = jsonObj.optString("data", "") + + val sseLines = dataStr.split("\n\n") + for (line in sseLines) { + if (line.startsWith("data: ")) { + val jsonPart = line.removePrefix("data: ").trim() + if (jsonPart == "[DONE]") continue + try { + val eventObj = JSONObject() + eventObj.put("event", "data") + eventObj.put("data", jsonPart) + callbackFn.invoke(eventObj) + } catch (e: Exception) { + // 解析失败,忽略 + } + } + } + }, + false) + } +} diff --git a/demo/src/iosMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.ios.kt b/demo/src/iosMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.ios.kt index 41bbfa448..e37363fd0 100644 --- a/demo/src/iosMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.ios.kt +++ b/demo/src/iosMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.ios.kt @@ -1,9 +1,9 @@ -//package com.tencent.kuikly.demo.pages.compose.chatDemo -// -//import io.ktor.client.HttpClient -//import io.ktor.client.engine.darwin.Darwin -// -//internal actual object NetworkClient { -// actual val client: Any? -// get() = HttpClient(Darwin) -//} \ No newline at end of file +package com.tencent.kuikly.demo.pages.compose.chatDemo + +import io.ktor.client.HttpClient +import io.ktor.client.engine.darwin.Darwin + +internal actual object NetworkClient { + actual val client: Any? + get() = HttpClient(Darwin) +} \ No newline at end of file diff --git a/demo/src/jsMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatdemo/ChatDemo.js.kt b/demo/src/jsMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatdemo/ChatDemo.js.kt index fadab8652..fc4477576 100644 --- a/demo/src/jsMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatdemo/ChatDemo.js.kt +++ b/demo/src/jsMain/kotlin/com/tencent/kuikly/demo/pages/compose/chatdemo/ChatDemo.js.kt @@ -1,7 +1,7 @@ -//package com.tencent.kuikly.demo.pages.compose.chatDemo -// -//// todo 后续迁移module -//internal actual object NetworkClient { -// actual val client: Any? -// get() = null -//} \ No newline at end of file +package com.tencent.kuikly.demo.pages.compose.chatDemo + +// todo 后续迁移module +internal actual object NetworkClient { + actual val client: Any? + get() = null +} \ No newline at end of file diff --git a/demo/src/ohosArm64Main/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.ohos.kt b/demo/src/ohosArm64Main/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.ohos.kt index 35ebd0b3a..d94821c3d 100644 --- a/demo/src/ohosArm64Main/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.ohos.kt +++ b/demo/src/ohosArm64Main/kotlin/com/tencent/kuikly/demo/pages/compose/chatDemo/ChatDemo.ohos.kt @@ -1,6 +1,6 @@ -//package com.tencent.kuikly.demo.pages.compose.chatDemo -// -//internal actual object NetworkClient { -// actual val client: Any? -// get() = null -//} \ No newline at end of file +package com.tencent.kuikly.demo.pages.compose.chatDemo + +internal actual object NetworkClient { + actual val client: Any? + get() = null +} \ No newline at end of file