From b254b4b3f83c7d8b697f9355f320fa92efca1b06 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Sun, 21 Dec 2025 23:14:08 -0800 Subject: [PATCH] Add clickable mail preview in tmux status bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New `gt mail peek` command shows compact preview of first unread message - Left-click on status-right (mail icon area) opens popup with mail preview - Popup shows subject, sender, body preview (truncated to 500 chars) - Shows count of additional unread messages if any - Silent exit (code 1) when no unread mail 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- internal/cmd/mail.go | 73 +++++++++++++++++++++++++++++++++++++++++++ internal/tmux/tmux.go | 13 ++++++++ 2 files changed, 86 insertions(+) diff --git a/internal/cmd/mail.go b/internal/cmd/mail.go index 3031a47e..92e8de20 100644 --- a/internal/cmd/mail.go +++ b/internal/cmd/mail.go @@ -110,6 +110,16 @@ The message ID can be found from 'gt mail inbox'.`, RunE: runMailRead, } +var mailPeekCmd = &cobra.Command{ + Use: "peek", + Short: "Show preview of first unread message", + Long: `Display a compact preview of the first unread message. + +Useful for status bar popups - shows subject, sender, and body preview. +Exits silently with code 1 if no unread messages.`, + RunE: runMailPeek, +} + var mailDeleteCmd = &cobra.Command{ Use: "delete ", Short: "Delete a message", @@ -221,6 +231,7 @@ func init() { mailCmd.AddCommand(mailSendCmd) mailCmd.AddCommand(mailInboxCmd) mailCmd.AddCommand(mailReadCmd) + mailCmd.AddCommand(mailPeekCmd) mailCmd.AddCommand(mailDeleteCmd) mailCmd.AddCommand(mailArchiveCmd) mailCmd.AddCommand(mailCheckCmd) @@ -448,6 +459,68 @@ func runMailRead(cmd *cobra.Command, args []string) error { return nil } +func runMailPeek(cmd *cobra.Command, args []string) error { + // Determine which inbox + address := detectSender() + + // All mail uses town beads (two-level architecture) + workDir, err := findMailWorkDir() + if err != nil { + os.Exit(1) // Silent exit - no workspace + return nil + } + + // Get mailbox + router := mail.NewRouter(workDir) + mailbox, err := router.GetMailbox(address) + if err != nil { + os.Exit(1) // Silent exit - can't access mailbox + return nil + } + + // Get unread messages + messages, err := mailbox.ListUnread() + if err != nil || len(messages) == 0 { + os.Exit(1) // Silent exit - no unread + return nil + } + + // Show first unread message + msg := messages[0] + + // Header with priority indicator + priorityStr := "" + if msg.Priority == mail.PriorityUrgent { + priorityStr = " [URGENT]" + } else if msg.Priority == mail.PriorityHigh { + priorityStr = " [!]" + } + + fmt.Printf("📬 %s%s\n", msg.Subject, priorityStr) + fmt.Printf("From: %s\n", msg.From) + fmt.Printf("ID: %s\n\n", msg.ID) + + // Body preview (truncate long bodies) + if msg.Body != "" { + body := msg.Body + // Truncate to ~500 chars for popup display + if len(body) > 500 { + body = body[:500] + "\n..." + } + fmt.Print(body) + if !strings.HasSuffix(body, "\n") { + fmt.Println() + } + } + + // Show count if more messages + if len(messages) > 1 { + fmt.Printf("\n%s\n", style.Dim.Render(fmt.Sprintf("(+%d more unread)", len(messages)-1))) + } + + return nil +} + func runMailDelete(cmd *cobra.Command, args []string) error { msgID := args[0] diff --git a/internal/tmux/tmux.go b/internal/tmux/tmux.go index f308a4f3..3a766ea2 100644 --- a/internal/tmux/tmux.go +++ b/internal/tmux/tmux.go @@ -498,5 +498,18 @@ func (t *Tmux) ConfigureGasTownSession(session string, theme Theme, rig, worker, if err := t.SetDynamicStatus(session); err != nil { return fmt.Errorf("setting dynamic status: %w", err) } + if err := t.SetMailClickBinding(session); err != nil { + return fmt.Errorf("setting mail click binding: %w", err) + } return nil } + +// SetMailClickBinding configures left-click on status-right to show mail preview. +// This creates a popup showing the first unread message when clicking the mail icon area. +func (t *Tmux) SetMailClickBinding(session string) error { + // Bind left-click on status-right to show mail popup + // The popup runs gt mail peek and closes on any key + _, err := t.run("bind-key", "-T", "root", "MouseDown1StatusRight", + "display-popup", "-E", "-w", "60", "-h", "15", "gt mail peek || echo 'No unread mail'") + return err +}