commit 3805112bc376460f6da1ff6eaac97f399ec94a1d
parent 0d8c68c1cc2088aefbb213dd59fb2d8a2d2fd1dd
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 25 Aug 2026 22:38:31 -0700
Render reply parents in latest feed
Diffstat:
2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/internal/tui/model_test.go b/internal/tui/model_test.go
@@ -318,6 +318,26 @@ func TestPostListUsesDenseRowsAndShowsActivityContext(t *testing.T) {
t.Fatalf("activity context is missing:\n%s", view)
}
}
+func TestLatestReplyRendersItsParentAboveIt(t *testing.T) {
+ parent := textlog.Post{ID: 10, Body: "parent body", CreatedAt: time.Now().Add(-time.Minute), Author: textlog.UserReference{Handle: "parent"}}
+ reply := textlog.Post{ID: 11, ParentID: intPtr(parent.ID), Parent: &parent, Body: "reply body", CreatedAt: time.Now(), Author: textlog.UserReference{Handle: "child"}}
+ model := New(&acceptanceBackend{}, config.Config{NoColor: true}, Options{})
+ model.width, model.height = 80, 40
+ s := model.current()
+ s.loading = false
+ s.feed = string(config.FeedLatest)
+ s.posts = []textlog.Post{reply}
+
+ view := model.View()
+ parentAt, replyAt := strings.Index(view, "parent body"), strings.Index(view, "reply body")
+ if parentAt < 0 || replyAt < 0 || parentAt >= replyAt {
+ t.Fatalf("latest reply did not render parent above reply:\n%s", view)
+ }
+ if !strings.Contains(view[:replyAt], "· parent") || !strings.Contains(view[parentAt:replyAt], "│") {
+ t.Fatalf("latest reply did not show parent context and reply indentation:\n%s", view)
+ }
+}
+
func TestPostListFillsAvailableHeight(t *testing.T) {
model := New(&acceptanceBackend{}, config.Config{BaseURL: "https://example.test", Theme: config.ThemeLight, NoColor: true}, Options{})
model.width, model.height = 120, 40
diff --git a/internal/tui/view.go b/internal/tui/view.go
@@ -206,11 +206,11 @@ func (m *Model) postsView(s *screen, p palette) string {
}
note := renderNote(s.posts[i], i == s.selected, focused, context, max(20, m.width-depth*2), bodyLines, p)
if depth > 0 {
- lines := strings.Split(note, "\n")
- for j := range lines {
- lines[j] = strings.Repeat("│ ", depth) + lines[j]
- }
- note = strings.Join(lines, "\n")
+ note = indentNote(note, depth)
+ }
+ if s.kind == screenFeed && s.feed == string(config.FeedLatest) && s.posts[i].Parent != nil {
+ parent := renderNote(*s.posts[i].Parent, false, -1, "parent", m.width, 3, p)
+ note = parent + "\n" + indentNote(note, 1)
}
if i < len(s.more) && s.more[i] > 0 {
label := fmt.Sprintf("more · %d replies", s.more[i])
@@ -279,6 +279,15 @@ func (m *Model) postsView(s *screen, p palette) string {
return strings.Join(parts, "\n")
}
+func indentNote(note string, depth int) string {
+ lines := strings.Split(note, "\n")
+ prefix := strings.Repeat("│ ", depth)
+ for i := range lines {
+ lines[i] = prefix + lines[i]
+ }
+ return strings.Join(lines, "\n")
+}
+
func renderNote(post textlog.Post, selected bool, focused int, context string, width, maxBodyLines int, p palette) string {
if post.Body == "↑ top" && post.Author.Handle == "" && post.CreatedAt.IsZero() {
style := lipgloss.NewStyle().Foreground(p.accent).Width(max(10, width-2))