Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion config/default_keybinds.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"rsvp_accept": "1",
"rsvp_decline": "2",
"rsvp_tentative": "3",
"focus_attachments": "tab"
"focus_attachments": "tab",
"open_html_browser": "o"
},
"composer": {
"external_editor": "ctrl+e",
Expand Down
1 change: 1 addition & 0 deletions config/keybinds.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type EmailKeys struct {
RsvpDecline string `json:"rsvp_decline"`
RsvpTentative string `json:"rsvp_tentative"`
FocusAttachments string `json:"focus_attachments"`
OpenHTMLBrowser string `json:"open_html_browser"`
}

type ComposerKeys struct {
Expand Down
53 changes: 53 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -1706,6 +1706,8 @@ func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo
m.syncPluginKeyBindings()
return m, m.current.Init()

case tui.OpenHTMLEmailMsg:
return m, openHTMLInBrowser(msg.Email.Body)
case tui.OpenEditorMsg:
composer, ok := m.current.(*tui.Composer)
if !ok {
Expand Down Expand Up @@ -3165,6 +3167,57 @@ func openExternalEditor(body string) tea.Cmd {
})
}

// openHTMLInBrowser writes the HTML body to a temp file and opens it in the default browser.
func openHTMLInBrowser(htmlBody string) tea.Cmd {
return func() tea.Msg {
tmpFile, err := os.CreateTemp("", "matcha-*.html")
if err != nil {
return nil
}
tmpPath := tmpFile.Name()

if _, err := tmpFile.WriteString(htmlBody); err != nil {
_ = tmpFile.Close()
_ = os.Remove(tmpPath)
return nil
}
if err := tmpFile.Close(); err != nil {
_ = os.Remove(tmpPath)
return nil
}

// Determine the command to open the browser based on the OS
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var cmd *exec.Cmd
switch runtime.GOOS {
case goosDarwin: // macOS
cmd = exec.CommandContext(ctx, "open", tmpPath)
case "linux":
cmd = exec.CommandContext(ctx, "xdg-open", tmpPath)
case "windows":
cmd = exec.CommandContext(ctx, "cmd", "/c", "start", tmpPath)
default:
_ = os.Remove(tmpPath)
return nil
}

// Run the command asynchronously without waiting for it to complete
// so the temp file isn't deleted before the browser opens it
if err := cmd.Start(); err != nil {
_ = os.Remove(tmpPath)
return nil
}

// Schedule cleanup of the temp file after a delay to allow the browser to read it
time.AfterFunc(30*time.Second, func() {
_ = os.Remove(tmpPath)
})

return nil
}
}

// --- IDLE command ---

// listenForIdleUpdates blocks until an IDLE update arrives, then returns it as a tea.Msg.
Expand Down
13 changes: 11 additions & 2 deletions tui/email_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func (m *EmailView) Init() tea.Cmd {
return nil
}

func (m *EmailView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m *EmailView) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:gocyclo
var cmd tea.Cmd
cmds := make([]tea.Cmd, 0, 1)

Expand Down Expand Up @@ -312,6 +312,12 @@ func (m *EmailView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if len(m.email.Attachments) > 0 {
m.focusOnAttachments = true
}
case kb.Email.OpenHTMLBrowser:
if m.email.BodyMIMEType == "text/html" && len(m.email.Body) > 0 {
// Clear Kitty graphics before opening browser
ClearKittyGraphics()
return m, func() tea.Msg { return OpenHTMLEmailMsg{Email: m.email} }
}
}
}
case tea.WindowSizeMsg:
Expand Down Expand Up @@ -386,8 +392,11 @@ func (m *EmailView) View() tea.View {
} else {
var shortcuts strings.Builder
shortcuts.WriteString("\uf112 r: reply • \uf064 f: forward • \uea81 d: delete • \uea98 a: archive • \uf435 tab: focus attachments • \ueb06 esc: back to inbox")
if m.email.BodyMIMEType == "text/html" && len(m.email.Body) > 0 {
shortcuts.WriteString(" • \uf303 o: open in browser")
}
if view.ImageProtocolSupported() {
shortcuts.WriteString("• \uf03e i: toggle images")
shortcuts.WriteString(" • \uf03e i: toggle images")
}
for _, pk := range m.pluginKeyBindings {
shortcuts.WriteString(" • ")
Expand Down
4 changes: 4 additions & 0 deletions tui/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ type ForwardEmailMsg struct {
Email fetcher.Email
}

type OpenHTMLEmailMsg struct {
Email fetcher.Email
}

type SetComposerCursorToStartMsg struct{}

type GoToFilePickerMsg struct{}
Expand Down