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/uiutil"
 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 configuration associated with this [Common] instance.
 30func (c *Common) Config() *config.Config {
 31	return c.App.Config()
 32}
 33
 34// DefaultCommon returns the default common UI configurations.
 35func DefaultCommon(app *app.App) *Common {
 36	s := styles.DefaultStyles()
 37	return &Common{
 38		App:    app,
 39		Styles: &s,
 40	}
 41}
 42
 43// CenterRect returns a new [Rectangle] centered within the given area with the
 44// specified width and height.
 45func CenterRect(area uv.Rectangle, width, height int) uv.Rectangle {
 46	centerX := area.Min.X + area.Dx()/2
 47	centerY := area.Min.Y + area.Dy()/2
 48	minX := centerX - width/2
 49	minY := centerY - height/2
 50	maxX := minX + width
 51	maxY := minY + height
 52	return image.Rect(minX, minY, maxX, maxY)
 53}
 54
 55// BottomLeftRect returns a new [Rectangle] positioned at the bottom-left within the given area with the
 56// specified width and height.
 57func BottomLeftRect(area uv.Rectangle, width, height int) uv.Rectangle {
 58	minX := area.Min.X
 59	maxX := minX + width
 60	maxY := area.Max.Y
 61	minY := maxY - height
 62	return image.Rect(minX, minY, maxX, maxY)
 63}
 64
 65// IsFileTooBig checks if the file at the given path exceeds the specified size
 66// limit.
 67func IsFileTooBig(filePath string, sizeLimit int64) (bool, error) {
 68	fileInfo, err := os.Stat(filePath)
 69	if err != nil {
 70		return false, fmt.Errorf("error getting file info: %w", err)
 71	}
 72
 73	if fileInfo.Size() > sizeLimit {
 74		return true, nil
 75	}
 76
 77	return false, nil
 78}
 79
 80// CopyToClipboard copies the given text to the clipboard using both OSC 52
 81// (terminal escape sequence) and native clipboard for maximum compatibility.
 82// Returns a command that reports success to the user with the given message.
 83func CopyToClipboard(text, successMessage string) tea.Cmd {
 84	return CopyToClipboardWithCallback(text, successMessage, nil)
 85}
 86
 87// CopyToClipboardWithCallback copies text to clipboard and executes a callback
 88// before showing the success message.
 89// This is useful when you need to perform additional actions like clearing UI state.
 90func CopyToClipboardWithCallback(text, successMessage string, callback tea.Cmd) tea.Cmd {
 91	return tea.Sequence(
 92		tea.SetClipboard(text),
 93		func() tea.Msg {
 94			_ = clipboard.WriteAll(text)
 95			return nil
 96		},
 97		callback,
 98		uiutil.ReportInfo(successMessage),
 99	)
100}