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}