Chat bubbles: preserve single newlines in agent messages too — the hard-break rule from user bubbles now applies to agent replies as well, so status lines and model-wrapped text render one line per field instead of collapsing into a wall of text; unit tests for preserveNewlinesAsHardBreaks
This commit is contained in:
1 parent
1bcadcf950
commit
ec53769325
3 files changed
+34
-8
No files matched your search
@@ -2137,8 +2137,11 @@ private fun MessageBubble(
|
|||||||
// M8: render the agent's reply as markdown (bold / italic /
|
// M8: render the agent's reply as markdown (bold / italic /
|
||||||
// underscore, tables, highlighted code blocks). Leading
|
// underscore, tables, highlighted code blocks). Leading
|
||||||
// newlines are stripped so the text hugs the top of the
|
// newlines are stripped so the text hugs the top of the
|
||||||
// bubble; the ▉ cursor is kept while streaming.
|
// bubble; single newlines become hard breaks (models write
|
||||||
val displayText = msg.text.prepareForMarkdown() + if (msg.streaming) " ▉" else ""
|
// status lines and wrapped text expecting a break per line,
|
||||||
|
// same as user input); the ▉ cursor is kept while streaming.
|
||||||
|
val displayText = msg.text.prepareForMarkdown().preserveNewlinesAsHardBreaks() +
|
||||||
|
if (msg.streaming) " ▉" else ""
|
||||||
MarkdownText(
|
MarkdownText(
|
||||||
text = displayText,
|
text = displayText,
|
||||||
color = textColor,
|
color = textColor,
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ fun String.prepareForMarkdown(): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the single newlines a user typed into markdown hard breaks (two
|
* Converts single newlines into markdown hard breaks (two trailing spaces) so
|
||||||
* trailing spaces) so they render as line breaks instead of being collapsed
|
* they render as line breaks instead of being collapsed into spaces by the
|
||||||
* into spaces by the parser (GFM treats a lone newline inside a paragraph as a
|
* parser (GFM treats a lone newline inside a paragraph as a space). Applied to
|
||||||
* space). Lines inside fenced code blocks (``` / ~~~) are left untouched so the
|
* both user input (lazy line breaks) and agent replies (models write status
|
||||||
* code content is not altered; blank lines are kept as-is since they already
|
* lines and wrapped text expecting a break per line). Lines inside fenced code
|
||||||
* produce paragraph breaks.
|
* blocks (``` / ~~~) are left untouched so the code content is not altered;
|
||||||
|
* blank lines are kept as-is since they already produce paragraph breaks.
|
||||||
*/
|
*/
|
||||||
fun String.preserveNewlinesAsHardBreaks(): String {
|
fun String.preserveNewlinesAsHardBreaks(): String {
|
||||||
val lines = split("\n")
|
val lines = split("\n")
|
||||||
|
|||||||
@@ -39,4 +39,26 @@ class MarkdownTest {
|
|||||||
fun allBlankBecomesEmpty() {
|
fun allBlankBecomesEmpty() {
|
||||||
assertEquals("", "\n\n \n".prepareForMarkdown())
|
assertEquals("", "\n\n \n".prepareForMarkdown())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun convertsSingleNewlinesToHardBreaks() {
|
||||||
|
assertEquals("a \nb ", "a\nb".preserveNewlinesAsHardBreaks())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun keepsBlankLinesAsParagraphBreaks() {
|
||||||
|
assertEquals("a \n\nb ", "a\n\nb".preserveNewlinesAsHardBreaks())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun leavesFencedCodeBlocksUntouched() {
|
||||||
|
val md = "```kotlin\nval a = 1\nval b = 2\n```"
|
||||||
|
assertEquals(md, md.preserveNewlinesAsHardBreaks())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun statusStyleLinesBecomeHardBreaks() {
|
||||||
|
val md = "**Title:** x\n**Model:** y"
|
||||||
|
assertEquals("**Title:** x \n**Model:** y ", md.preserveNewlinesAsHardBreaks())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user