@@ -42,7 +42,6 @@ import (
42 "github.com/charmbracelet/crush/internal/ui/common"
42 "github.com/charmbracelet/crush/internal/ui/common"
43 "github.com/charmbracelet/crush/internal/ui/completions"
43 "github.com/charmbracelet/crush/internal/ui/completions"
44 "github.com/charmbracelet/crush/internal/ui/dialog"
44 "github.com/charmbracelet/crush/internal/ui/dialog"
45− timage "github.com/charmbracelet/crush/internal/ui/image"
46 "github.com/charmbracelet/crush/internal/ui/logo"
45 "github.com/charmbracelet/crush/internal/ui/logo"
47 "github.com/charmbracelet/crush/internal/ui/styles"
46 "github.com/charmbracelet/crush/internal/ui/styles"
48 "github.com/charmbracelet/crush/internal/uiutil"
47 "github.com/charmbracelet/crush/internal/uiutil"
@@ -145,9 +144,8 @@ type UI struct {
145 // terminal.
144 // terminal.
146 sendProgressBar bool
145 sendProgressBar bool
148− // QueryCapabilities instructs the TUI to query for the terminal version when it
147+ // caps hold different terminal capabilities that we query for.
149− // starts.
148+ caps common.Capabilities
150− QueryCapabilities bool
152 // Editor components
150 // Editor components
153 textarea textarea.Model
151 textarea textarea.Model
@@ -182,9 +180,6 @@ type UI struct {
182 // sidebarLogo keeps a cached version of the sidebar sidebarLogo.
180 // sidebarLogo keeps a cached version of the sidebar sidebarLogo.
183 sidebarLogo string
181 sidebarLogo string
185− // imgCaps stores the terminal image capabilities.
186− imgCaps timage.Capabilities
188 // custom commands & mcp commands
183 // custom commands & mcp commands
189 customCommands []commands.CustomCommand
184 customCommands []commands.CustomCommand
190 mcpPrompts []commands.MCPPrompt
185 mcpPrompts []commands.MCPPrompt
@@ -304,9 +299,6 @@ func New(com *common.Common) *UI {
304// Init initializes the UI model.
299// Init initializes the UI model.
305func (m *UI) Init() tea.Cmd {
300func (m *UI) Init() tea.Cmd {
306 var cmds []tea.Cmd
301 var cmds []tea.Cmd
307− if m.QueryCapabilities {
308− cmds = append(cmds, tea.RequestTerminalVersion)
310 if m.state == uiOnboarding {
302 if m.state == uiOnboarding {
311 if cmd := m.openModelsDialog(); cmd != nil {
303 if cmd := m.openModelsDialog(); cmd != nil {
312 cmds = append(cmds, cmd)
304 cmds = append(cmds, cmd)
@@ -363,19 +355,15 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
363 m.updateLayoutAndSize()
355 m.updateLayoutAndSize()
358+ // Update terminal capabilities
366 switch msg := msg.(type) {
360 switch msg := msg.(type) {
367 case tea.EnvMsg:
361 case tea.EnvMsg:
368 // Is this Windows Terminal?
362 // Is this Windows Terminal?
369 if !m.sendProgressBar {
363 if !m.sendProgressBar {
370 m.sendProgressBar = slices.Contains(msg, "WT_SESSION")
364 m.sendProgressBar = slices.Contains(msg, "WT_SESSION")
372− m.imgCaps.Env = uv.Environ(msg)
366+ cmds = append(cmds, common.QueryCmd(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))
379 case loadSessionMsg:
367 case loadSessionMsg:
380 if m.forceCompactMode {
368 if m.forceCompactMode {
381 m.isCompact = true
369 m.isCompact = true
@@ -521,8 +509,6 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
521 case tea.WindowSizeMsg:
509 case tea.WindowSizeMsg:
522 m.width, m.height = msg.Width, msg.Height
510 m.width, m.height = msg.Width, msg.Height
523 m.updateLayoutAndSize()
511 m.updateLayoutAndSize()
524− // XXX: We need to store cell dimensions for image rendering.
525− m.imgCaps.Columns, m.imgCaps.Rows = msg.Width, msg.Height
526 case tea.KeyboardEnhancementsMsg:
512 case tea.KeyboardEnhancementsMsg:
527 m.keyenh = msg
513 m.keyenh = msg
528 if msg.SupportsKeyDisambiguation() {
514 if msg.SupportsKeyDisambiguation() {
@@ -689,16 +675,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
689 if m.completionsOpen {
675 if m.completionsOpen {
690 m.completions.SetFiles(msg.Files)
676 m.completions.SetFiles(msg.Files)
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
697 case uv.KittyGraphicsEvent:
678 case uv.KittyGraphicsEvent:
698− // [timage.RequestCapabilities] sends a Kitty graphics query and this
699− // captures the response. Any response means the terminal understands
701− m.imgCaps.SupportsKittyGraphics = true
702 if !bytes.HasPrefix(msg.Payload, []byte("OK")) {
679 if !bytes.HasPrefix(msg.Payload, []byte("OK")) {
703 slog.Warn("unexpected Kitty graphics response",
680 slog.Warn("unexpected Kitty graphics response",
704 "response", string(msg.Payload),
681 "response", string(msg.Payload),
@@ -2776,7 +2753,7 @@ func (m *UI) openFilesDialog() tea.Cmd {
2778 filePicker, cmd := dialog.NewFilePicker(m.com)
2755 filePicker, cmd := dialog.NewFilePicker(m.com)
2779− filePicker.SetImageCapabilities(&m.imgCaps)
2756+ filePicker.SetImageCapabilities(&m.caps)
2780 m.dialog.OpenDialog(filePicker)
2757 m.dialog.OpenDialog(filePicker)
2782 return cmd
2759 return cmd