Skip to content

Commit fa4e3c9

Browse files
Merge pull request #552 from Contextable/codex/address-github-issue-514
Address GitHub issue 514
1 parent d28f718 commit fa4e3c9

File tree

3 files changed

+37
-27
lines changed

3 files changed

+37
-27
lines changed

sdks/community/kotlin/examples/chatapp/gradle/libs.versions.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ runner = "1.6.2"
2121
slf4j = "2.0.9"
2222
ui-test-junit4 = "1.8.3"
2323
voyager-navigator = "1.0.0"
24+
compose-richtext = "1.0.0-alpha03"
2425

2526
[libraries]
2627
# Ktor
@@ -62,6 +63,9 @@ ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref =
6263
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager-navigator" }
6364
voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager-navigator" }
6465
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager-navigator" }
66+
richtext-commonmark = { module = "com.halilibo.compose-richtext:richtext-commonmark", version.ref = "compose-richtext" }
67+
richtext-ui = { module = "com.halilibo.compose-richtext:richtext-ui", version.ref = "compose-richtext" }
68+
richtext-ui-material3 = { module = "com.halilibo.compose-richtext:richtext-ui-material3", version.ref = "compose-richtext" }
6569

6670
[plugins]
6771
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }

sdks/community/kotlin/examples/chatapp/shared/build.gradle.kts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ kotlin {
4848
implementation(compose.ui)
4949
implementation(compose.components.resources)
5050
implementation(compose.components.uiToolingPreview)
51+
implementation(compose.materialIconsExtended)
5152

5253
// ag-ui library - consolidated client module includes agent functionality
5354
implementation(libs.agui.client)
@@ -79,6 +80,11 @@ kotlin {
7980

8081
// Base64 encoding/decoding
8182
implementation(libs.okio)
83+
84+
// Markdown rendering
85+
implementation(libs.richtext.commonmark)
86+
implementation(libs.richtext.ui)
87+
implementation(libs.richtext.ui.material3)
8288
}
8389
}
8490

@@ -261,4 +267,4 @@ compose.resources {
261267
// }
262268
// }
263269
// }
264-
//}
270+
//}

sdks/community/kotlin/examples/chatapp/shared/src/commonMain/kotlin/com/agui/example/chatapp/ui/screens/chat/components/MessageBubble.kt

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@ import androidx.compose.ui.geometry.Offset
1515
import androidx.compose.ui.graphics.BlendMode
1616
import androidx.compose.ui.graphics.Brush
1717
import androidx.compose.ui.graphics.Color
18+
import androidx.compose.ui.semantics.semantics
19+
import androidx.compose.ui.semantics.text
20+
import androidx.compose.ui.text.AnnotatedString
1821
import androidx.compose.ui.text.style.TextAlign
1922
import androidx.compose.ui.unit.dp
2023
import com.agui.example.chatapp.ui.screens.chat.DisplayMessage
2124
import com.agui.example.chatapp.ui.screens.chat.MessageRole
25+
import com.halilibo.richtext.commonmark.Markdown
26+
import com.halilibo.richtext.ui.material3.RichText
2227
import kotlinx.datetime.Instant
2328
import kotlinx.datetime.TimeZone
2429
import kotlinx.datetime.toLocalDateTime
@@ -34,6 +39,14 @@ fun MessageBubble(
3439
val isToolCall = message.role == MessageRole.TOOL_CALL
3540
val isStepInfo = message.role == MessageRole.STEP_INFO
3641
val isEphemeral = message.ephemeralGroupId != null
42+
val messageTextColor = when {
43+
isUser -> MaterialTheme.colorScheme.onPrimary
44+
isError -> MaterialTheme.colorScheme.onError
45+
isSystem -> MaterialTheme.colorScheme.onTertiary
46+
isToolCall -> MaterialTheme.colorScheme.onSecondaryContainer
47+
isStepInfo -> MaterialTheme.colorScheme.onTertiaryContainer
48+
else -> MaterialTheme.colorScheme.onSurfaceVariant
49+
}
3750

3851
// Enhanced fade-in animation
3952
val animatedAlpha = remember(message.id) { Animatable(0f) }
@@ -94,12 +107,7 @@ fun MessageBubble(
94107
Text(
95108
text = message.content,
96109
style = MaterialTheme.typography.bodyLarge,
97-
color = when {
98-
isUser -> MaterialTheme.colorScheme.onPrimary
99-
isError -> MaterialTheme.colorScheme.onError
100-
isSystem -> MaterialTheme.colorScheme.onTertiary
101-
else -> MaterialTheme.colorScheme.onSurfaceVariant
102-
}
110+
color = messageTextColor
103111
)
104112
CircularProgressIndicator(
105113
modifier = Modifier.size(12.dp),
@@ -126,14 +134,7 @@ fun MessageBubble(
126134
label = "textShimmer"
127135
)
128136

129-
val textColor = when {
130-
isUser -> MaterialTheme.colorScheme.onPrimary
131-
isError -> MaterialTheme.colorScheme.onError
132-
isSystem -> MaterialTheme.colorScheme.onTertiary
133-
isToolCall -> MaterialTheme.colorScheme.onSecondaryContainer
134-
isStepInfo -> MaterialTheme.colorScheme.onTertiaryContainer
135-
else -> MaterialTheme.colorScheme.onSurfaceVariant
136-
}
137+
val textColor = messageTextColor
137138

138139
Box {
139140
Text(
@@ -163,18 +164,17 @@ fun MessageBubble(
163164
}
164165
} else {
165166
// Regular text for non-ephemeral messages
166-
Text(
167-
text = message.content,
168-
style = MaterialTheme.typography.bodyLarge,
169-
color = when {
170-
isUser -> MaterialTheme.colorScheme.onPrimary
171-
isError -> MaterialTheme.colorScheme.onError
172-
isSystem -> MaterialTheme.colorScheme.onTertiary
173-
isToolCall -> MaterialTheme.colorScheme.onSecondaryContainer
174-
isStepInfo -> MaterialTheme.colorScheme.onTertiaryContainer
175-
else -> MaterialTheme.colorScheme.onSurfaceVariant
167+
ProvideTextStyle(MaterialTheme.typography.bodyLarge) {
168+
CompositionLocalProvider(LocalContentColor provides messageTextColor) {
169+
RichText(
170+
modifier = Modifier
171+
.fillMaxWidth()
172+
.semantics { text = AnnotatedString(message.content) }
173+
) {
174+
Markdown(message.content)
175+
}
176176
}
177-
)
177+
}
178178
}
179179
}
180180

@@ -203,4 +203,4 @@ private fun formatTimestamp(timestamp: Long): String {
203203
val instant = Instant.fromEpochMilliseconds(timestamp)
204204
val localDateTime = instant.toLocalDateTime(TimeZone.currentSystemDefault())
205205
return "${localDateTime.hour.toString().padStart(2, '0')}:${localDateTime.minute.toString().padStart(2, '0')}"
206-
}
206+
}

0 commit comments

Comments
 (0)