actions.go

  1package dialog
  2
  3import (
  4	"fmt"
  5	"net/http"
  6	"os"
  7	"path/filepath"
  8
  9	tea "charm.land/bubbletea/v2"
 10	"github.com/charmbracelet/catwalk/pkg/catwalk"
 11	"github.com/charmbracelet/crush/internal/commands"
 12	"github.com/charmbracelet/crush/internal/config"
 13	"github.com/charmbracelet/crush/internal/message"
 14	"github.com/charmbracelet/crush/internal/permission"
 15	"github.com/charmbracelet/crush/internal/session"
 16	"github.com/charmbracelet/crush/internal/ui/common"
 17	"github.com/charmbracelet/crush/internal/uiutil"
 18)
 19
 20// ActionClose is a message to close the current dialog.
 21type ActionClose struct{}
 22
 23// ActionQuit is a message to quit the application.
 24type ActionQuit = tea.QuitMsg
 25
 26// ActionOpenDialog is a message to open a dialog.
 27type ActionOpenDialog struct {
 28	DialogID string
 29}
 30
 31// ActionSelectSession is a message indicating a session has been selected.
 32type ActionSelectSession struct {
 33	Session session.Session
 34}
 35
 36// ActionSelectModel is a message indicating a model has been selected.
 37type ActionSelectModel struct {
 38	Provider  catwalk.Provider
 39	Model     config.SelectedModel
 40	ModelType config.SelectedModelType
 41}
 42
 43// Messages for commands
 44type (
 45	ActionNewSession        struct{}
 46	ActionToggleHelp        struct{}
 47	ActionToggleCompactMode struct{}
 48	ActionToggleThinking    struct{}
 49	ActionExternalEditor    struct{}
 50	ActionToggleYoloMode    struct{}
 51	// ActionInitializeProject is a message to initialize a project.
 52	ActionInitializeProject struct{}
 53	ActionSummarize         struct {
 54		SessionID string
 55	}
 56	ActionPermissionResponse struct {
 57		Permission permission.PermissionRequest
 58		Action     PermissionAction
 59	}
 60	// ActionRunCustomCommand is a message to run a custom command.
 61	ActionRunCustomCommand struct {
 62		Content   string
 63		Arguments []commands.Argument
 64		Args      map[string]string // Actual argument values
 65	}
 66	// ActionRunMCPPrompt is a message to run a custom command.
 67	ActionRunMCPPrompt struct {
 68		Title       string
 69		Description string
 70		PromptID    string
 71		ClientID    string
 72		Arguments   []commands.Argument
 73		Args        map[string]string // Actual argument values
 74	}
 75)
 76
 77// Messages for API key input dialog.
 78type (
 79	ActionChangeAPIKeyState struct {
 80		State APIKeyInputState
 81	}
 82)
 83
 84// ActionCmd represents an action that carries a [tea.Cmd] to be passed to the
 85// Bubble Tea program loop.
 86type ActionCmd struct {
 87	Cmd tea.Cmd
 88}
 89
 90// ActionFilePickerSelected is a message indicating a file has been selected in
 91// the file picker dialog.
 92type ActionFilePickerSelected struct {
 93	Path string
 94}
 95
 96// Cmd returns a command that reads the file at path and sends a
 97// [message.Attachement] to the program.
 98func (a ActionFilePickerSelected) Cmd() tea.Cmd {
 99	path := a.Path
100	if path == "" {
101		return nil
102	}
103	return func() tea.Msg {
104		isFileLarge, err := common.IsFileTooBig(path, common.MaxAttachmentSize)
105		if err != nil {
106			return uiutil.InfoMsg{
107				Type: uiutil.InfoTypeError,
108				Msg:  fmt.Sprintf("unable to read the image: %v", err),
109			}
110		}
111		if isFileLarge {
112			return uiutil.InfoMsg{
113				Type: uiutil.InfoTypeError,
114				Msg:  "file too large, max 5MB",
115			}
116		}
117
118		content, err := os.ReadFile(path)
119		if err != nil {
120			return uiutil.InfoMsg{
121				Type: uiutil.InfoTypeError,
122				Msg:  fmt.Sprintf("unable to read the image: %v", err),
123			}
124		}
125
126		mimeBufferSize := min(512, len(content))
127		mimeType := http.DetectContentType(content[:mimeBufferSize])
128		fileName := filepath.Base(path)
129
130		return message.Attachment{
131			FilePath: path,
132			FileName: fileName,
133			MimeType: mimeType,
134			Content:  content,
135		}
136	}
137}