Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/changes/unreleased/1072-bare-number-reply-draws.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
kind: fixed
title: a reply that is only a number and a full stop draws instead of vanishing
pr: 1072
surface: [chat]
invalidates:
- "A model reply whose whole text was a number and a full stop (`32.`, `1024.`) parsed as a Markdown ordered list with one empty item, so the list renderer drew the marker column, found no body, and discarded the marker too. The reply rendered to zero rows and the deck skipped it: the fold showed only the thought row though the transcript held the text. An ordered-list item with no body now draws its marker as the literal text the reader sent, so `32.` stands under the chip; `1. one` and multi-item lists still draw as lists."
---

Found while running the number-answer journey against `dev`: asking the chat for
"only the number followed by a period" produced `32.`, which the surface dropped.
`renderer.list` in `internal/tui2/prose` pushed the marker, rendered the empty
item, and popped it as undrawn, taking the marker with it. The fix draws the
marker when the item body produced nothing, which is the only case a bare
`<number>.` reaches.
22 changes: 22 additions & 0 deletions internal/tui2/prose/bare_number_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package prose

import (
"strings"
"testing"
)

// TestABareNumberedReplyStillDraws guards #1072: a reply that is only a number
// and a full stop (`32.`, `1024.`) is parsed by Markdown as an ordered list
// with one empty item. The list renderer must draw the marker as the literal
// text the reader sent, never discard it as an empty list and render nothing.
func TestABareNumberedReplyStillDraws(t *testing.T) {
for _, in := range []string{"32.", "1024."} {
if got := strings.TrimSpace(strings.Join(Render(in, Options{Width: 80}), "\n")); got == "" {
t.Errorf("%q rendered to nothing", in)
}
}
// A genuine ordered list with a body still draws as a list.
if got := strings.TrimSpace(strings.Join(Render("1. one", Options{Width: 80}), "\n")); got == "" {
t.Errorf("%q rendered to nothing", "1. one")
}
}
10 changes: 9 additions & 1 deletion internal/tui2/prose/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,15 @@ func (r *renderer) list(n *ast.List) {
saved := r.push(first, indent)
before := len(r.out)
r.container(c, !n.IsTight)
r.pop(saved, len(r.out) > before)
drew := len(r.out) > before
r.pop(saved, drew)
// An item with no body - a line that was only a marker, like the whole
// reply `32.` - would otherwise render to nothing and the deck would
// drop the reply. Draw the marker as the literal text the reader sent so
// the answer still stands.
if !drew {
r.emit(first)
}
}
}

Expand Down