From a069e8a0dff3f1dffb520693182eacef4f49815b Mon Sep 17 00:00:00 2001 From: Kujtim Hoxha Date: Mon, 28 Jul 2025 21:12:34 +0200 Subject: [PATCH] chore: support clipboard past image --- internal/tui/components/chat/editor/editor.go | 41 +++++++++++++++++++ .../dialogs/filepicker/filepicker.go | 10 +++-- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/internal/tui/components/chat/editor/editor.go b/internal/tui/components/chat/editor/editor.go index fda718f290a2bf4eea089bb6f2804531aa224f00..fc4d26a431f1daac7e71ef4e772c6550b0750c68 100644 --- a/internal/tui/components/chat/editor/editor.go +++ b/internal/tui/components/chat/editor/editor.go @@ -2,8 +2,10 @@ package editor import ( "fmt" + "net/http" "os" "os/exec" + "path/filepath" "runtime" "slices" "strings" @@ -207,6 +209,45 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case OpenEditorMsg: m.textarea.SetValue(msg.Text) m.textarea.MoveToEnd() + case tea.PasteMsg: + path := strings.ReplaceAll(string(msg), "\\ ", " ") + // try to get an image + path, err := filepath.Abs(path) + if err != nil { + m.textarea, cmd = m.textarea.Update(msg) + return m, cmd + } + isAllowedType := false + for _, ext := range filepicker.AllowedTypes { + if strings.HasSuffix(path, ext) { + isAllowedType = true + break + } + } + if !isAllowedType { + + m.textarea, cmd = m.textarea.Update(msg) + return m, cmd + } + tooBig, _ := filepicker.IsFileTooBig(path, filepicker.MaxAttachmentSize) + if tooBig { + m.textarea, cmd = m.textarea.Update(msg) + return m, cmd + } + + content, err := os.ReadFile(path) + if err != nil { + m.textarea, cmd = m.textarea.Update(msg) + return m, cmd + } + mimeBufferSize := min(512, len(content)) + mimeType := http.DetectContentType(content[:mimeBufferSize]) + fileName := filepath.Base(path) + attachment := message.Attachment{FilePath: path, FileName: fileName, MimeType: mimeType, Content: content} + return m, util.CmdHandler(filepicker.FilePickedMsg{ + Attachment: attachment, + }) + case tea.KeyPressMsg: cur := m.textarea.Cursor() curIdx := m.textarea.Width()*cur.Y + cur.X diff --git a/internal/tui/components/dialogs/filepicker/filepicker.go b/internal/tui/components/dialogs/filepicker/filepicker.go index 3944da7665b6d400c091f4a1282360ed2c638163..274105bfef96b923e2fdef064af8b50dd45938f3 100644 --- a/internal/tui/components/dialogs/filepicker/filepicker.go +++ b/internal/tui/components/dialogs/filepicker/filepicker.go @@ -21,7 +21,7 @@ import ( ) const ( - maxAttachmentSize = int64(5 * 1024 * 1024) // 5MB + MaxAttachmentSize = int64(5 * 1024 * 1024) // 5MB FilePickerID = "filepicker" fileSelectionHight = 10 ) @@ -45,10 +45,12 @@ type model struct { help help.Model } +var AllowedTypes = []string{".jpg", ".jpeg", ".png"} + func NewFilePickerCmp(workingDir string) FilePicker { t := styles.CurrentTheme() fp := filepicker.New() - fp.AllowedTypes = []string{".jpg", ".jpeg", ".png"} + fp.AllowedTypes = AllowedTypes if workingDir != "" { fp.CurrentDirectory = workingDir @@ -127,7 +129,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Sequence( util.CmdHandler(dialogs.CloseDialogMsg{}), func() tea.Msg { - isFileLarge, err := ValidateFileSize(path, maxAttachmentSize) + isFileLarge, err := IsFileTooBig(path, MaxAttachmentSize) if err != nil { return util.ReportError(fmt.Errorf("unable to read the image: %w", err)) } @@ -222,7 +224,7 @@ func (m *model) Position() (int, int) { return row, col } -func ValidateFileSize(filePath string, sizeLimit int64) (bool, error) { +func IsFileTooBig(filePath string, sizeLimit int64) (bool, error) { fileInfo, err := os.Stat(filePath) if err != nil { return false, fmt.Errorf("error getting file info: %w", err)