@@ -42,7 +42,6 @@ import (
4242 "github.com/charmbracelet/crush/internal/ui/common"
4343 "github.com/charmbracelet/crush/internal/ui/completions"
4444 "github.com/charmbracelet/crush/internal/ui/dialog"
45− timage "github.com/charmbracelet/crush/internal/ui/image"
4645 "github.com/charmbracelet/crush/internal/ui/logo"
4746 "github.com/charmbracelet/crush/internal/ui/styles"
4847 "github.com/charmbracelet/crush/internal/uiutil"
@@ -145,9 +144,8 @@ type UI struct {
145144 // terminal.
146145 sendProgressBar bool
147146
148− // QueryCapabilities instructs the TUI to query for the terminal version when it
149− // starts.
150− QueryCapabilities bool
147+ // caps hold different terminal capabilities that we query for.
148+ caps common.Capabilities
151149
152150 // Editor components
153151 textarea textarea.Model
@@ -182,9 +180,6 @@ type UI struct {
182180 // sidebarLogo keeps a cached version of the sidebar sidebarLogo.
183181 sidebarLogo string
184182
185− // imgCaps stores the terminal image capabilities.
186− imgCaps timage.Capabilities
187−
188183 // custom commands & mcp commands
189184 customCommands []commands.CustomCommand
190185 mcpPrompts []commands.MCPPrompt
@@ -304,9 +299,6 @@ func New(com *common.Common) *UI {
304299// Init initializes the UI model.
305300func (m *UI) Init() tea.Cmd {
306301 var cmds []tea.Cmd
307− if m.QueryCapabilities {
308− cmds = append(cmds, tea.RequestTerminalVersion)
309− }
310302 if m.state == uiOnboarding {
311303 if cmd := m.openModelsDialog(); cmd != nil {
312304 cmds = append(cmds, cmd)
@@ -363,19 +355,15 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
363355 m.updateLayoutAndSize()
364356 }
365357 }
358+ // Update terminal capabilities
359+ m.caps.Update(msg)
366360 switch msg := msg.(type) {
367361 case tea.EnvMsg:
368362 // Is this Windows Terminal?
369363 if !m.sendProgressBar {
370364 m.sendProgressBar = slices.Contains(msg, "WT_SESSION")
371365 }
372− m.imgCaps.Env = uv.Environ(msg)
373− // Only query for image capabilities if the terminal is known to
374− // support Kitty graphics protocol. This prevents character bleeding
375− // on terminals that don't understand the APC escape sequences.
376− if m.QueryCapabilities {
377− cmds = append(cmds, timage.RequestCapabilities(m.imgCaps.Env))
378− }
366+ cmds = append(cmds, common.QueryCmd(uv.Environ(msg)))
379367 case loadSessionMsg:
380368 if m.forceCompactMode {
381369 m.isCompact = true
@@ -521,8 +509,6 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
521509 case tea.WindowSizeMsg:
522510 m.width, m.height = msg.Width, msg.Height
523511 m.updateLayoutAndSize()
524− // XXX: We need to store cell dimensions for image rendering.
525− m.imgCaps.Columns, m.imgCaps.Rows = msg.Width, msg.Height
526512 case tea.KeyboardEnhancementsMsg:
527513 m.keyenh = msg
528514 if msg.SupportsKeyDisambiguation() {
@@ -689,16 +675,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
689675 if m.completionsOpen {
690676 m.completions.SetFiles(msg.Files)
691677 }
692− case uv.WindowPixelSizeEvent:
693− // [timage.RequestCapabilities] requests the terminal to send a window
694− // size event to help determine pixel dimensions.
695− m.imgCaps.PixelWidth = msg.Width
696− m.imgCaps.PixelHeight = msg.Height
697678 case uv.KittyGraphicsEvent:
698− // [timage.RequestCapabilities] sends a Kitty graphics query and this
699− // captures the response. Any response means the terminal understands
700− // the protocol.
701− m.imgCaps.SupportsKittyGraphics = true
702679 if !bytes.HasPrefix(msg.Payload, []byte("OK")) {
703680 slog.Warn("unexpected Kitty graphics response",
704681 "response", string(msg.Payload),
@@ -2776,7 +2753,7 @@ func (m *UI) openFilesDialog() tea.Cmd {
27762753 }
27772754
27782755 filePicker, cmd := dialog.NewFilePicker(m.com)
2779− filePicker.SetImageCapabilities(&m.imgCaps)
2756+ filePicker.SetImageCapabilities(&m.caps)
27802757 m.dialog.OpenDialog(filePicker)
27812758
27822759 return cmd