@@ -15,7 +15,7 @@ import (
1515 "time"
1616)
1717
18- const version = "0.2.1 "
18+ const version = "0.3.0 "
1919
2020// Message represents a conversation message
2121type Message struct {
@@ -29,6 +29,7 @@ type Conversation struct {
2929 SessionID string `json:"session_id"`
3030 Cwd string `json:"cwd"`
3131 FirstTimestamp string `json:"first_timestamp"`
32+ LastTimestamp string `json:"last_timestamp"`
3233 Messages []Message `json:"messages"`
3334}
3435
@@ -145,6 +146,9 @@ func parseConversationFile(path string) (*Conversation, error) {
145146 return nil , nil
146147 }
147148
149+ // Set LastTimestamp from the last message
150+ conv .LastTimestamp = conv .Messages [len (conv .Messages )- 1 ].Ts
151+
148152 if conv .Cwd == "" {
149153 conv .Cwd = "unknown"
150154 }
@@ -202,7 +206,7 @@ func getConversations() ([]Conversation, error) {
202206 }
203207
204208 sort .Slice (conversations , func (i , j int ) bool {
205- return conversations [i ].FirstTimestamp > conversations [j ].FirstTimestamp
209+ return conversations [i ].LastTimestamp > conversations [j ].LastTimestamp
206210 })
207211
208212 return conversations , nil
@@ -249,21 +253,33 @@ func buildSearchLines(conversations []Conversation) ([]string, map[string]Conver
249253 project = conv .Cwd [idx + 1 :]
250254 }
251255
252- for i , msg := range conv .Messages {
253- if msg .Role != "user" {
254- continue
256+ // Collect all user messages for search, display first one
257+ var firstUserMsg * Message
258+ var allUserText []string
259+ for i := range conv .Messages {
260+ if conv .Messages [i ].Role == "user" {
261+ if firstUserMsg == nil {
262+ firstUserMsg = & conv .Messages [i ]
263+ }
264+ allUserText = append (allUserText , conv .Messages [i ].Text )
255265 }
266+ }
256267
257- text := truncate (msg .Text , 100 )
258- ts := formatTimestamp (msg .Ts )
259- projectPad := padOrTruncate (project , 25 )
260-
261- // Format: id \t date \t project \t message
262- // Colors: date=dim, project=yellow/bold, message=white
263- line := fmt .Sprintf ("%s:%d\t \033 [90m%s\033 [0m\t \033 [1;33m%s\033 [0m\t %s" ,
264- conv .SessionID , i , ts , projectPad , text )
265- lines = append (lines , line )
268+ if firstUserMsg == nil {
269+ continue
266270 }
271+
272+ displayText := truncate (firstUserMsg .Text , 100 )
273+ searchText := strings .Join (strings .Fields (strings .Join (allUserText , " " )), " " )
274+ ts := formatTimestamp (conv .LastTimestamp )
275+ projectPad := padOrTruncate (project , 25 )
276+
277+ // Format: id \t date \t project \t display_message \t search_text
278+ // Colors: date=dim, project=yellow/bold, message=white
279+ // search_text is hidden (column 5) but used for matching
280+ line := fmt .Sprintf ("%s\t \033 [90m%s\033 [0m\t \033 [1;33m%s\033 [0m\t %s\t %s" ,
281+ conv .SessionID , ts , projectPad , displayText , searchText )
282+ lines = append (lines , line )
267283 }
268284
269285 return lines , convMap
@@ -344,15 +360,7 @@ func showPreview(line, query string) {
344360 return
345361 }
346362
347- sessionMsg := parts [0 ]
348- lastColon := strings .LastIndex (sessionMsg , ":" )
349- if lastColon < 0 {
350- return
351- }
352-
353- sessionID := sessionMsg [:lastColon ]
354- var msgIdx int
355- fmt .Sscanf (sessionMsg [lastColon + 1 :], "%d" , & msgIdx )
363+ sessionID := parts [0 ]
356364
357365 conv , ok := convMap [sessionID ]
358366 if ! ok {
@@ -364,19 +372,56 @@ func showPreview(line, query string) {
364372 fmt .Printf ("\033 [1;33mSession:\033 [0m %s\n " , sessionID )
365373 fmt .Printf ("\033 [1;33mTotal messages:\033 [0m %d\n \n " , len (conv .Messages ))
366374
367- start := msgIdx - 2
368- if start < 0 {
369- start = 0
375+ // Find all messages containing the query
376+ var matchIndices []int
377+ matchSet := make (map [int ]bool )
378+ if query != "" {
379+ queryLower := strings .ToLower (query )
380+ for i , msg := range conv .Messages {
381+ if strings .Contains (strings .ToLower (msg .Text ), queryLower ) {
382+ matchIndices = append (matchIndices , i )
383+ matchSet [i ] = true
384+ }
385+ }
370386 }
371- end := msgIdx + 4
372- if end > len (conv .Messages ) {
373- end = len (conv .Messages )
387+
388+ // Build set of indices to show (matches + 1 context on each side)
389+ showSet := make (map [int ]bool )
390+ if len (matchIndices ) > 0 {
391+ for _ , idx := range matchIndices {
392+ if idx > 0 {
393+ showSet [idx - 1 ] = true
394+ }
395+ showSet [idx ] = true
396+ if idx < len (conv .Messages )- 1 {
397+ showSet [idx + 1 ] = true
398+ }
399+ }
400+ } else {
401+ // No matches, show first 6 messages
402+ for i := 0 ; i < 6 && i < len (conv .Messages ); i ++ {
403+ showSet [i ] = true
404+ }
374405 }
375406
376- for i := start ; i < end ; i ++ {
407+ // Display messages with gaps
408+ lastShown := - 1
409+ for i := 0 ; i < len (conv .Messages ); i ++ {
410+ if ! showSet [i ] {
411+ continue
412+ }
413+
414+ // Show gap indicator if we skipped messages
415+ if lastShown >= 0 && i > lastShown + 1 {
416+ skipped := i - lastShown - 1
417+ fmt .Printf ("\033 [90m ... %d messages ...\033 [0m\n \n " , skipped )
418+ } else if lastShown == - 1 && i > 0 {
419+ fmt .Printf ("\033 [90m ... %d earlier messages\033 [0m\n \n " , i )
420+ }
421+
377422 msg := conv .Messages [i ]
378423 var prefix string
379- if i == msgIdx {
424+ if matchSet [ i ] {
380425 if msg .Role == "user" {
381426 prefix = "\033 [1;32m>>> User:\033 [0m"
382427 } else {
@@ -398,10 +443,12 @@ func showPreview(line, query string) {
398443 fmt .Println (prefix )
399444 fmt .Println (formatCodeBlock (text , query , " " ))
400445 fmt .Println ()
446+
447+ lastShown = i
401448 }
402449
403- remaining := len (conv .Messages ) - end
404- if remaining > 0 {
450+ if lastShown < len (conv .Messages )- 1 {
451+ remaining := len ( conv . Messages ) - lastShown - 1
405452 fmt .Printf ("\033 [90m ... %d more messages\033 [0m\n " , remaining )
406453 }
407454}
@@ -504,6 +551,8 @@ func main() {
504551 "--ansi" ,
505552 "--delimiter=\t " ,
506553 "--with-nth=2,3,4" ,
554+ "--nth=2.." ,
555+ "--no-sort" ,
507556 "--tabstop=4" ,
508557 "--preview" , fmt .Sprintf ("%s --preview {} {q}" , self ),
509558 "--preview-window=bottom:70%:wrap:+5" ,
@@ -529,9 +578,7 @@ func main() {
529578 }
530579
531580 parts := strings .Split (selected , "\t " )
532- sessionMsg := parts [0 ]
533- lastColon := strings .LastIndex (sessionMsg , ":" )
534- sessionID := sessionMsg [:lastColon ]
581+ sessionID := parts [0 ]
535582
536583 conv , ok := convMap [sessionID ]
537584 if ! ok {
0 commit comments