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}