44 "fmt"
55 "io"
66 "strings"
7+ "time"
78
89 "charm.land/bubbles/v2/key"
910 "charm.land/bubbles/v2/list"
@@ -22,12 +23,15 @@ var (
2223 tabBarStyle = lipgloss .NewStyle ().BorderStyle (lipgloss .NormalBorder ()).BorderBottom (true ).PaddingBottom (1 ).MarginBottom (1 )
2324)
2425
26+ var dateStyle = lipgloss .NewStyle ().Foreground (lipgloss .Color ("243" ))
27+
2528type item struct {
2629 title , desc string
2730 originalIndex int
2831 uid uint32
2932 accountID string
3033 accountEmail string
34+ date time.Time
3135}
3236
3337func (i item ) Title () string { return i .title }
@@ -52,14 +56,82 @@ func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list
5256 str = fmt .Sprintf ("%d. [%s] %s" , index + 1 , truncateEmail (i .accountEmail ), i .title )
5357 }
5458
59+ // Format and right-align date
60+ dateStr := formatRelativeDate (i .date )
61+ styledDate := dateStyle .Render (dateStr )
62+ dateWidth := lipgloss .Width (styledDate )
63+
64+ // Truncate the left part to fit within the available width
65+ listWidth := m .Width ()
66+ isSelected := index == m .Index ()
67+ cursorWidth := 0
68+ if isSelected {
69+ cursorWidth = 2 // "> " prefix
70+ }
71+ maxLeft := listWidth - dateWidth - 2 - cursorWidth // 2 for spacing
72+ if maxLeft < 10 {
73+ maxLeft = 10
74+ }
75+ if lipgloss .Width (str ) > maxLeft {
76+ // Truncate with ellipsis
77+ for lipgloss .Width (str ) > maxLeft - 1 && len (str ) > 0 {
78+ str = str [:len (str )- 1 ]
79+ }
80+ str += "…"
81+ }
82+
83+ // Pad to push date to the right
84+ padding := listWidth - lipgloss .Width (str ) - dateWidth - cursorWidth
85+ if padding < 1 {
86+ padding = 1
87+ }
88+
5589 fn := itemStyle .Render
5690 if index == m .Index () {
5791 fn = func (s ... string ) string {
5892 return selectedItemStyle .Render ("> " + s [0 ])
5993 }
6094 }
6195
62- fmt .Fprint (w , fn (str ))
96+ fmt .Fprint (w , fn (str + strings .Repeat (" " , padding )+ styledDate ))
97+ }
98+
99+ // formatRelativeDate formats a time as relative if within the last week,
100+ // otherwise as an absolute date.
101+ func formatRelativeDate (t time.Time ) string {
102+ if t .IsZero () {
103+ return ""
104+ }
105+ now := time .Now ()
106+ d := now .Sub (t )
107+
108+ switch {
109+ case d < time .Minute :
110+ return "just now"
111+ case d < time .Hour :
112+ mins := int (d .Minutes ())
113+ if mins == 1 {
114+ return "1 min ago"
115+ }
116+ return fmt .Sprintf ("%d min ago" , mins )
117+ case d < 24 * time .Hour :
118+ hours := int (d .Hours ())
119+ if hours == 1 {
120+ return "1 hour ago"
121+ }
122+ return fmt .Sprintf ("%d hours ago" , hours )
123+ case d < 7 * 24 * time .Hour :
124+ days := int (d .Hours () / 24 )
125+ if days == 1 {
126+ return "1 day ago"
127+ }
128+ return fmt .Sprintf ("%d days ago" , days )
129+ default :
130+ if t .Year () == now .Year () {
131+ return t .Format ("Jan 02" )
132+ }
133+ return t .Format ("Jan 02, 2006" )
134+ }
63135}
64136
65137// truncateEmail shortens an email for display
@@ -205,6 +277,7 @@ func (m *Inbox) updateList() {
205277 uid : email .UID ,
206278 accountID : email .AccountID ,
207279 accountEmail : accountEmail ,
280+ date : email .Date ,
208281 }
209282 }
210283
0 commit comments