@@ -13,6 +13,7 @@ import (
14 tea "charm.land/bubbletea/v2"
14 tea "charm.land/bubbletea/v2"
15 "github.com/charmbracelet/crush/internal/uiutil"
15 "github.com/charmbracelet/crush/internal/uiutil"
16+ uv "github.com/charmbracelet/ultraviolet"
16 "github.com/charmbracelet/x/ansi"
17 "github.com/charmbracelet/x/ansi"
17 "github.com/charmbracelet/x/ansi/kitty"
18 "github.com/charmbracelet/x/ansi/kitty"
18 "github.com/charmbracelet/x/mosaic"
19 "github.com/charmbracelet/x/mosaic"
@@ -33,6 +34,8 @@ type Capabilities struct {
33 // SupportsKittyGraphics indicates whether the terminal supports the Kitty
34 // SupportsKittyGraphics indicates whether the terminal supports the Kitty
34 // graphics protocol.
35 // graphics protocol.
35 SupportsKittyGraphics bool
36 SupportsKittyGraphics bool
37+ // Env is the terminal environment variables.
38// CellSize returns the size of a single terminal cell in pixels.
41// CellSize returns the size of a single terminal cell in pixels.
@@ -55,12 +58,15 @@ func CalculateCellSize(pixelWidth, pixelHeight, charWidth, charHeight int) CellS
56// RequestCapabilities is a [tea.Cmd] that requests the terminal to report
59// RequestCapabilities is a [tea.Cmd] that requests the terminal to report
57// its image related capabilities to the program.
60// its image related capabilities to the program.
58−func RequestCapabilities() tea.Cmd {
61+func RequestCapabilities(env uv.Environ) tea.Cmd {
60− ansi.WindowOp(14) + // Window size in pixels
62+ winOpReq := ansi.WindowOp(14) // Window size in pixels
61− // ID 31 is just a random ID used to detect Kitty graphics support.
63+ // ID 31 is just a random ID used to detect Kitty graphics support.
62− ansi.KittyGraphics([]byte("AAAA"), "i=31", "s=1", "v=1", "a=q", "t=d", "f=24"),
64+ kittyReq := ansi.KittyGraphics([]byte("AAAA"), "i=31", "s=1", "v=1", "a=q", "t=d", "f=24")
63− )
65+ if _, isTmux := env.LookupEnv("TMUX"); isTmux {
66+ kittyReq = ansi.TmuxPassthrough(kittyReq)
69+ return tea.Raw(winOpReq + kittyReq)
66// TransmittedMsg is a message indicating that an image has been transmitted to
72// TransmittedMsg is a message indicating that an image has been transmitted to
@@ -161,7 +167,7 @@ func HasTransmitted(id string, cols, rows int) bool {
162// Transmit transmits the image data to the terminal if needed. This is used to
168// Transmit transmits the image data to the terminal if needed. This is used to
163// cache the image on the terminal for later rendering.
169// cache the image on the terminal for later rendering.
164−func (e Encoding) Transmit(id string, img image.Image, cs CellSize, cols, rows int) tea.Cmd {
170+func (e Encoding) Transmit(id string, img image.Image, cs CellSize, cols, rows int, tmux bool) tea.Cmd {
165 if img == nil {
171 if img == nil {
166 return nil
172 return nil
@@ -192,7 +198,6 @@ func (e Encoding) Transmit(id string, img image.Image, cs CellSize, cols, rows i
192 bounds := img.Bounds()
198 bounds := img.Bounds()
193 imgWidth := bounds.Dx()
199 imgWidth := bounds.Dx()
194 imgHeight := bounds.Dy()
200 imgHeight := bounds.Dy()
196 imgID := int(key.Hash())
201 imgID := int(key.Hash())
197 if err := kitty.EncodeGraphics(&buf, img, &kitty.Options{
202 if err := kitty.EncodeGraphics(&buf, img, &kitty.Options{
198 ID: imgID,
203 ID: imgID,
@@ -205,9 +210,19 @@ func (e Encoding) Transmit(id string, img image.Image, cs CellSize, cols, rows i
205 Rows: rows,
210 Rows: rows,
206 VirtualPlacement: true,
211 VirtualPlacement: true,
207 Quite: 1,
212 Quite: 1,
214+ ChunkFormatter: func(chunk string) string {
216+ return ansi.TmuxPassthrough(chunk)
208 }); err != nil {
220 }); err != nil {
209 slog.Error("failed to encode image for kitty graphics", "err", err)
221 slog.Error("failed to encode image for kitty graphics", "err", err)
222+ return uiutil.InfoMsg{
223+ Type: uiutil.InfoTypeError,
210− return uiutil.ReportError(fmt.Errorf("failed to encode image"))
224+ Msg: "failed to encode image",
213 return tea.RawMsg{Msg: buf.String()}
228 return tea.RawMsg{Msg: buf.String()}