@@ -16,6 +16,7 @@ import (
16 "charm.land/bubbles/v2/textarea"
16 "charm.land/bubbles/v2/textarea"
17 tea "charm.land/bubbletea/v2"
17 tea "charm.land/bubbletea/v2"
18 "charm.land/lipgloss/v2"
18 "charm.land/lipgloss/v2"
19+ nativeclipboard "github.com/aymanbagabas/go-nativeclipboard"
19 "github.com/charmbracelet/crush/internal/app"
20 "github.com/charmbracelet/crush/internal/app"
20 "github.com/charmbracelet/crush/internal/filetracker"
21 "github.com/charmbracelet/crush/internal/filetracker"
21 "github.com/charmbracelet/crush/internal/fsext"
22 "github.com/charmbracelet/crush/internal/fsext"
@@ -338,6 +339,84 @@ func (m *editorCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
338 m.textarea.InsertRune('\n')
339 m.textarea.InsertRune('\n')
339 cmds = append(cmds, util.CmdHandler(completions.CloseCompletionsMsg{}))
340 cmds = append(cmds, util.CmdHandler(completions.CloseCompletionsMsg{}))
342+ // Handle image paste from clipboard
343+ if key.Matches(msg, m.keyMap.PasteImage) {
344+ imageData, err := nativeclipboard.Image.Read()
346+ if err != nil || len(imageData) == 0 {
347+ // If no image data found, try to get text data (could be file path)
349+ textData, err = nativeclipboard.Text.Read()
350+ if err != nil || len(textData) == 0 {
351+ // If clipboard is empty, show a warning
352+ return m, util.ReportWarn("No data found in clipboard. Note: Some terminals may not support reading image data from clipboard directly.")
355+ // Check if the text data is a file path
356+ textStr := string(textData)
357+ // First, try to interpret as a file path (existing functionality)
358+ path := strings.ReplaceAll(textStr, "\\ ", " ")
359+ path, err = filepath.Abs(strings.TrimSpace(path))
361+ isAllowedType := false
362+ for _, ext := range filepicker.AllowedTypes {
363+ if strings.HasSuffix(path, ext) {
364+ isAllowedType = true
369+ tooBig, _ := filepicker.IsFileTooBig(path, filepicker.MaxAttachmentSize)
371+ content, err := os.ReadFile(path)
373+ mimeBufferSize := min(512, len(content))
374+ mimeType := http.DetectContentType(content[:mimeBufferSize])
375+ fileName := filepath.Base(path)
376+ attachment := message.Attachment{FilePath: path, FileName: fileName, MimeType: mimeType, Content: content}
377+ return m, util.CmdHandler(filepicker.FilePickedMsg{
378+ Attachment: attachment,
385+ // If not a valid file path, show a warning
386+ return m, util.ReportWarn("No image found in clipboard")
388+ // We have image data from the clipboard
389+ // Create a temporary file to store the clipboard image data
390+ tempFile, err := os.CreateTemp("", "clipboard_image_crush_*")
392+ return m, util.ReportError(err)
394+ defer tempFile.Close()
396+ // Write clipboard content to the temporary file
397+ _, err = tempFile.Write(imageData)
399+ return m, util.ReportError(err)
402+ // Determine the file extension based on the image data
403+ mimeBufferSize := min(512, len(imageData))
404+ mimeType := http.DetectContentType(imageData[:mimeBufferSize])
406+ // Create an attachment from the temporary file
407+ fileName := filepath.Base(tempFile.Name())
408+ attachment := message.Attachment{
409+ FilePath: tempFile.Name(),
415+ return m, util.CmdHandler(filepicker.FilePickedMsg{
416+ Attachment: attachment,
341 // Handle Enter key
420 // Handle Enter key
342 if m.textarea.Focused() && key.Matches(msg, m.keyMap.SendMessage) {
421 if m.textarea.Focused() && key.Matches(msg, m.keyMap.SendMessage) {
343 value := m.textarea.Value()
422 value := m.textarea.Value()