Add clickable mail preview in tmux status bar
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -110,6 +110,16 @@ The message ID can be found from 'gt mail inbox'.`,
|
|||||||
RunE: runMailRead,
|
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{
|
var mailDeleteCmd = &cobra.Command{
|
||||||
Use: "delete <message-id>",
|
Use: "delete <message-id>",
|
||||||
Short: "Delete a message",
|
Short: "Delete a message",
|
||||||
@@ -221,6 +231,7 @@ func init() {
|
|||||||
mailCmd.AddCommand(mailSendCmd)
|
mailCmd.AddCommand(mailSendCmd)
|
||||||
mailCmd.AddCommand(mailInboxCmd)
|
mailCmd.AddCommand(mailInboxCmd)
|
||||||
mailCmd.AddCommand(mailReadCmd)
|
mailCmd.AddCommand(mailReadCmd)
|
||||||
|
mailCmd.AddCommand(mailPeekCmd)
|
||||||
mailCmd.AddCommand(mailDeleteCmd)
|
mailCmd.AddCommand(mailDeleteCmd)
|
||||||
mailCmd.AddCommand(mailArchiveCmd)
|
mailCmd.AddCommand(mailArchiveCmd)
|
||||||
mailCmd.AddCommand(mailCheckCmd)
|
mailCmd.AddCommand(mailCheckCmd)
|
||||||
@@ -448,6 +459,68 @@ func runMailRead(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
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 {
|
func runMailDelete(cmd *cobra.Command, args []string) error {
|
||||||
msgID := args[0]
|
msgID := args[0]
|
||||||
|
|
||||||
|
|||||||
@@ -498,5 +498,18 @@ func (t *Tmux) ConfigureGasTownSession(session string, theme Theme, rig, worker,
|
|||||||
if err := t.SetDynamicStatus(session); err != nil {
|
if err := t.SetDynamicStatus(session); err != nil {
|
||||||
return fmt.Errorf("setting dynamic status: %w", err)
|
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
|
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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user