on main help sign in
chore: support clipboard past image
a069e8aKujtim Hoxha
internal/tui/components/chat/editor/editor.go+41
@@ -2,8 +2,10 @@ package editor
2
2
3import (
3import (
4 "fmt"
4 "fmt"
5+ "net/http"
5 "os"
6 "os"
6 "os/exec"
7 "os/exec"
8+ "path/filepath"
7 "runtime"
9 "runtime"
8 "slices"
10 "slices"
9 "strings"
11 "strings"
@@ -207,6 +209,45 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
207 case OpenEditorMsg:
209 case OpenEditorMsg:
208 m.textarea.SetValue(msg.Text)
210 m.textarea.SetValue(msg.Text)
209 m.textarea.MoveToEnd()
211 m.textarea.MoveToEnd()
212+ case tea.PasteMsg:
213+ path := strings.ReplaceAll(string(msg), "\\ ", " ")
214+ // try to get an image
215+ path, err := filepath.Abs(path)
216+ if err != nil {
217+ m.textarea, cmd = m.textarea.Update(msg)
218+ return m, cmd
219+ }
220+ isAllowedType := false
221+ for _, ext := range filepicker.AllowedTypes {
222+ if strings.HasSuffix(path, ext) {
223+ isAllowedType = true
224+ break
225+ }
226+ }
227+ if !isAllowedType {
228+
229+ m.textarea, cmd = m.textarea.Update(msg)
230+ return m, cmd
231+ }
232+ tooBig, _ := filepicker.IsFileTooBig(path, filepicker.MaxAttachmentSize)
233+ if tooBig {
234+ m.textarea, cmd = m.textarea.Update(msg)
235+ return m, cmd
236+ }
237+
238+ content, err := os.ReadFile(path)
239+ if err != nil {
240+ m.textarea, cmd = m.textarea.Update(msg)
241+ return m, cmd
242+ }
243+ mimeBufferSize := min(512, len(content))
244+ mimeType := http.DetectContentType(content[:mimeBufferSize])
245+ fileName := filepath.Base(path)
246+ attachment := message.Attachment{FilePath: path, FileName: fileName, MimeType: mimeType, Content: content}
247+ return m, util.CmdHandler(filepicker.FilePickedMsg{
248+ Attachment: attachment,
249+ })
250+
210 case tea.KeyPressMsg:
251 case tea.KeyPressMsg:
211 cur := m.textarea.Cursor()
252 cur := m.textarea.Cursor()
212 curIdx := m.textarea.Width()*cur.Y + cur.X
253 curIdx := m.textarea.Width()*cur.Y + cur.X
internal/tui/components/dialogs/filepicker/filepicker.go+6−4
@@ -21,7 +21,7 @@ import (
21)
21)
22
22
23const (
23const (
24− maxAttachmentSize = int64(5 * 1024 * 1024) // 5MB
24+ MaxAttachmentSize = int64(5 * 1024 * 1024) // 5MB
25 FilePickerID = "filepicker"
25 FilePickerID = "filepicker"
26 fileSelectionHight = 10
26 fileSelectionHight = 10
27)
27)
@@ -45,10 +45,12 @@ type model struct {
45 help help.Model
45 help help.Model
46}
46}
47
47
48+var AllowedTypes = []string{".jpg", ".jpeg", ".png"}
49+
48func NewFilePickerCmp(workingDir string) FilePicker {
50func NewFilePickerCmp(workingDir string) FilePicker {
49 t := styles.CurrentTheme()
51 t := styles.CurrentTheme()
50 fp := filepicker.New()
52 fp := filepicker.New()
51− fp.AllowedTypes = []string{".jpg", ".jpeg", ".png"}
53+ fp.AllowedTypes = AllowedTypes
52
54
53 if workingDir != "" {
55 if workingDir != "" {
54 fp.CurrentDirectory = workingDir
56 fp.CurrentDirectory = workingDir
@@ -127,7 +129,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
127 return m, tea.Sequence(
129 return m, tea.Sequence(
128 util.CmdHandler(dialogs.CloseDialogMsg{}),
130 util.CmdHandler(dialogs.CloseDialogMsg{}),
129 func() tea.Msg {
131 func() tea.Msg {
130− isFileLarge, err := ValidateFileSize(path, maxAttachmentSize)
132+ isFileLarge, err := IsFileTooBig(path, MaxAttachmentSize)
131 if err != nil {
133 if err != nil {
132 return util.ReportError(fmt.Errorf("unable to read the image: %w", err))
134 return util.ReportError(fmt.Errorf("unable to read the image: %w", err))
133 }
135 }
@@ -222,7 +224,7 @@ func (m *model) Position() (int, int) {
222 return row, col
224 return row, col
223}
225}
224
226
225−func ValidateFileSize(filePath string, sizeLimit int64) (bool, error) {
227+func IsFileTooBig(filePath string, sizeLimit int64) (bool, error) {
226 fileInfo, err := os.Stat(filePath)
228 fileInfo, err := os.Stat(filePath)
227 if err != nil {
229 if err != nil {
228 return false, fmt.Errorf("error getting file info: %w", err)
230 return false, fmt.Errorf("error getting file info: %w", err)