common.go

  1package common
  2
  3import (
  4	"fmt"
  5	"image"
  6	"os"
  7
  8	tea "charm.land/bubbletea/v2"
  9	"github.com/atotto/clipboard"
 10	"github.com/charmbracelet/crush/internal/app"
 11	"github.com/charmbracelet/crush/internal/config"
 12	"github.com/charmbracelet/crush/internal/ui/styles"
 13	"github.com/charmbracelet/crush/internal/ui/util"
 14	uv "github.com/charmbracelet/ultraviolet"
 15)
 16
 17// MaxAttachmentSize defines the maximum allowed size for file attachments (5 MB).
 18const MaxAttachmentSize = int64(5 * 1024 * 1024)
 19
 20// AllowedImageTypes defines the permitted image file types.
 21var AllowedImageTypes = []string{".jpg", ".jpeg", ".png"}
 22
 23// Common defines common UI options and configurations.
 24type Common struct {
 25	App    *app.App
 26	Styles *styles.Styles
 27}
 28
 29// Config returns the pure-data configuration associated with this [Common] instance.
 30func (c *Common) Config() *config.Config {
 31	return c.App.Config()
 32}
 33
 34// Store returns the config store associated with this [Common] instance.
 35func (c *Common) Store() *config.ConfigStore {
 36	return c.App.Store()
 37}
 38
 39// DefaultCommon returns the default common UI configurations.
 40func DefaultCommon(app *app.App) *Common {
 41	s := styles.DefaultStyles()
 42	return &Common{
 43		App:    app,
 44		Styles: &s,
 45	}
 46}
 47
 48// CenterRect returns a new [Rectangle] centered within the given area with the
 49// specified width and height.
 50func CenterRect(area uv.Rectangle, width, height int) uv.Rectangle {
 51	centerX := area.Min.X + area.Dx()/2
 52	centerY := area.Min.Y + area.Dy()/2
 53	minX := centerX - width/2
 54	minY := centerY - height/2
 55	maxX := minX + width
 56	maxY := minY + height
 57	return image.Rect(minX, minY, maxX, maxY)
 58}
 59
 60// BottomLeftRect returns a new [Rectangle] positioned at the bottom-left within the given area with the
 61// specified width and height.
 62func BottomLeftRect(area uv.Rectangle, width, height int) uv.Rectangle {
 63	minX := area.Min.X
 64	maxX := minX + width
 65	maxY := area.Max.Y
 66	minY := maxY - height
 67	return image.Rect(minX, minY, maxX, maxY)
 68}
 69
 70// IsFileTooBig checks if the file at the given path exceeds the specified size
 71// limit.
 72func IsFileTooBig(filePath string, sizeLimit int64) (bool, error) {
 73	fileInfo, err := os.Stat(filePath)
 74	if err != nil {
 75		return false, fmt.Errorf("error getting file info: %w", err)
 76	}
 77
 78	if fileInfo.Size() > sizeLimit {
 79		return true, nil
 80	}
 81
 82	return false, nil
 83}
 84
 85// CopyToClipboard copies the given text to the clipboard using both OSC 52
 86// (terminal escape sequence) and native clipboard for maximum compatibility.
 87// Returns a command that reports success to the user with the given message.
 88func CopyToClipboard(text, successMessage string) tea.Cmd {
 89	return CopyToClipboardWithCallback(text, successMessage, nil)
 90}
 91
 92// CopyToClipboardWithCallback copies text to clipboard and executes a callback
 93// before showing the success message.
 94// This is useful when you need to perform additional actions like clearing UI state.
 95func CopyToClipboardWithCallback(text, successMessage string, callback tea.Cmd) tea.Cmd {
 96	return tea.Sequence(
 97		tea.SetClipboard(text),
 98		func() tea.Msg {
 99			_ = clipboard.WriteAll(text)
100			return nil
101		},
102		callback,
103		util.ReportInfo(successMessage),
104	)
105}