Detailed changes
@@ -13,7 +13,8 @@ require (
github.com/bmatcuk/doublestar/v4 v4.9.1
github.com/charlievieth/fastwalk v1.0.14
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250820203609-601216f68ee2
- github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20251011205917-3b687ffc1619
+ github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.5
+ github.com/charmbracelet/catwalk v0.7.1-0.20251023112313-048e47f1399c
github.com/charmbracelet/fang v0.4.3
github.com/charmbracelet/glamour/v2 v2.0.0-20250811143442-a27abb32f018
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250917201909-41ff0bf215ea
@@ -49,7 +50,7 @@ require (
mvdan.cc/sh/v3 v3.12.1-0.20250902163504-3cf4fd5717a5
)
-replace charm.land/fantasy => ../../fantasy/main/
+replace charm.land/fantasy => ../fantasy
require (
cloud.google.com/go v0.116.0 // indirect
@@ -78,10 +79,9 @@ require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/charmbracelet/anthropic-sdk-go v0.0.0-20251022202715-ec1499142678 // indirect
- github.com/charmbracelet/catwalk v0.7.1-0.20251023112313-048e47f1399c
github.com/charmbracelet/colorprofile v0.3.2
github.com/charmbracelet/go-genai v0.0.0-20251021165952-9befde14ce97 // indirect
- github.com/charmbracelet/ultraviolet v0.0.0-20250915111650-81d4262876ef
+ github.com/charmbracelet/ultraviolet v0.0.0-20251017140847-d4ace4d6e731
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250811133356-e0c5dbe5ea4a // indirect
github.com/charmbracelet/x/exp/slice v0.0.0-20250904123553-b4e2667e5ad5
github.com/charmbracelet/x/json v0.2.0 // indirect
@@ -78,8 +78,8 @@ github.com/charmbracelet/anthropic-sdk-go v0.0.0-20251022202715-ec1499142678 h1:
github.com/charmbracelet/anthropic-sdk-go v0.0.0-20251022202715-ec1499142678/go.mod h1:8TIYxZxsuCqqeJ0lga/b91tBwrbjoHDC66Sq5t8N2R4=
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250820203609-601216f68ee2 h1:973OHYuq2Jx9deyuPwe/6lsuQrDCatOsjP8uCd02URE=
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250820203609-601216f68ee2/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
-github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20251011205917-3b687ffc1619 h1:hjOhtqsxa+LVuCAkzhfA43wtusOaUPyQdSTg/wbRscw=
-github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20251011205917-3b687ffc1619/go.mod h1:5IzIGXU1n0foRc8bRAherC8ZuQCQURPlwx3ANLq1138=
+github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.5 h1:oAChAeh730gtLKK/BpaTeJHzmj3KFuEfQ7AZgf2VGHM=
+github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.5/go.mod h1:SUTLq+/pGQ5qntHgt0JswfVJFfgJgWDqyvyiSLVlmbo=
github.com/charmbracelet/catwalk v0.7.1-0.20251023112313-048e47f1399c h1:FVx5lHa+uGmr3GmShsQWhrpmU8RutJm0+To9rU804Ws=
github.com/charmbracelet/catwalk v0.7.1-0.20251023112313-048e47f1399c/go.mod h1:ReU4SdrLfe63jkEjWMdX2wlZMV3k9r11oQAmzN0m+KY=
github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI=
@@ -94,8 +94,8 @@ github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250917201909-41ff0bf215ea
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250917201909-41ff0bf215ea/go.mod h1:ngHerf1JLJXBrDXdphn5gFrBPriCL437uwukd5c93pM=
github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706 h1:WkwO6Ks3mSIGnGuSdKl9qDSyfbYK50z2wc2gGMggegE=
github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706/go.mod h1:mjJGp00cxcfvD5xdCa+bso251Jt4owrQvuimJtVmEmM=
-github.com/charmbracelet/ultraviolet v0.0.0-20250915111650-81d4262876ef h1:VrWaUi2LXYLjfjCHowdSOEc6dQ9Ro14KY7Bw4IWd19M=
-github.com/charmbracelet/ultraviolet v0.0.0-20250915111650-81d4262876ef/go.mod h1:AThRsQH1t+dfyOKIwXRoJBniYFQUkUpQq4paheHMc2o=
+github.com/charmbracelet/ultraviolet v0.0.0-20251017140847-d4ace4d6e731 h1:Lr+igmzKpLPdb8yUZBP9noYWwCZP042z2nWPrJZTc+8=
+github.com/charmbracelet/ultraviolet v0.0.0-20251017140847-d4ace4d6e731/go.mod h1:KfWwUa0Oe//D72YlhbOq/g40L7UiGtATrvsGI3cciG8=
github.com/charmbracelet/x/ansi v0.10.2 h1:ith2ArZS0CJG30cIUfID1LXN7ZFXRCww6RUvAPA+Pzw=
github.com/charmbracelet/x/ansi v0.10.2/go.mod h1:HbLdJjQH4UH4AqA2HpRWuWNluRE6zxJH/yteYEYCFa8=
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250811133356-e0c5dbe5ea4a h1:zYSNtEJM9jwHbJts2k+Hroj+xQwsW1yxc4Wopdv7KaI=
@@ -10,8 +10,10 @@ import (
"slices"
"time"
+ "github.com/charmbracelet/colorprofile"
"github.com/charmbracelet/crush/internal/config"
"github.com/charmbracelet/log/v2"
+ "github.com/charmbracelet/x/term"
"github.com/nxadm/tail"
"github.com/spf13/cobra"
)
@@ -45,6 +47,9 @@ var logsCmd = &cobra.Command{
log.SetLevel(log.DebugLevel)
log.SetOutput(os.Stdout)
+ if !term.IsTerminal(os.Stdout.Fd()) {
+ log.SetColorProfile(colorprofile.NoTTY)
+ }
cfg, err := config.Load(cwd, dataDir, false)
if err != nil {
@@ -83,11 +83,8 @@ crush -y
// Set up the TUI.
program := tea.NewProgram(
tui.New(app),
- tea.WithAltScreen(),
tea.WithContext(cmd.Context()),
- tea.WithMouseCellMotion(), // Use cell motion instead of all motion to reduce event flooding
- tea.WithFilter(tui.MouseEventFilter), // Filter mouse events based on focus state
- )
+ tea.WithFilter(tui.MouseEventFilter)) // Filter mouse events based on focus state
go app.Subscribe(program)
@@ -23,8 +23,8 @@ type model struct {
anim *anim.Anim
}
-func (m model) Init() tea.Cmd { return m.anim.Init() }
-func (m model) View() string { return m.anim.View() }
+func (m model) Init() tea.Cmd { return m.anim.Init() }
+func (m model) View() tea.View { return tea.NewView(m.anim.View()) }
// Update implements tea.Model.
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
@@ -16,6 +16,7 @@ import (
"github.com/lucasb-eyer/go-colorful"
"github.com/charmbracelet/crush/internal/csync"
+ "github.com/charmbracelet/crush/internal/tui/util"
)
const (
@@ -318,7 +319,7 @@ func (a *Anim) Init() tea.Cmd {
}
// Update processes animation steps (or not).
-func (a *Anim) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (a *Anim) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case StepMsg:
if msg.id != a.id {
@@ -101,7 +101,7 @@ func (m *messageListCmp) Init() tea.Cmd {
}
// Update handles incoming messages and updates the component state.
-func (m *messageListCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (m *messageListCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
var cmds []tea.Cmd
if m.session.ID != "" && m.app.AgentCoordinator != nil {
queueSize := m.app.AgentCoordinator.QueuedPrompts(m.session.ID)
@@ -172,7 +172,7 @@ func (m *editorCmp) repositionCompletions() tea.Msg {
return completions.RepositionCompletionsMsg{X: x, Y: y}
}
-func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (m *editorCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
var cmd tea.Cmd
var cmds []tea.Cmd
switch msg := msg.(type) {
@@ -44,7 +44,7 @@ func (h *header) Init() tea.Cmd {
return nil
}
-func (h *header) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (h *header) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case pubsub.Event[session.Session]:
if msg.Type == pubsub.UpdatedEvent {
@@ -35,7 +35,7 @@ var ClearSelectionKey = key.NewBinding(key.WithKeys("esc", "alt+esc"), key.WithH
// MessageCmp defines the interface for message components in the chat interface.
// It combines standard UI model interfaces with message-specific functionality.
type MessageCmp interface {
- util.Model // Basic Bubble Tea model interface
+ util.Model // Basic Bubble util.Model interface
layout.Sizeable // Width/height management
layout.Focusable // Focus state management
GetMessage() message.Message // Access to underlying message data
@@ -94,7 +94,7 @@ func (m *messageCmp) Init() tea.Cmd {
// Update handles incoming messages and updates the component state.
// Manages animation updates for spinning messages and stops animation when appropriate.
-func (m *messageCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (m *messageCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case anim.StepMsg:
m.spinning = m.shouldSpin()
@@ -387,7 +387,7 @@ func (m *assistantSectionModel) Init() tea.Cmd {
return nil
}
-func (m *assistantSectionModel) Update(tea.Msg) (tea.Model, tea.Cmd) {
+func (m *assistantSectionModel) Update(tea.Msg) (util.Model, tea.Cmd) {
return m, nil
}
@@ -27,7 +27,7 @@ import (
// ToolCallCmp defines the interface for tool call components in the chat interface.
// It manages the display of tool execution including pending states, results, and errors.
type ToolCallCmp interface {
- util.Model // Basic Bubble Tea model interface
+ util.Model // Basic Bubble util.Model interface
layout.Sizeable // Width/height management
layout.Focusable // Focus state management
GetToolCall() message.ToolCall // Access to tool call data
@@ -147,7 +147,7 @@ func (m *toolCallCmp) Init() tea.Cmd {
// Update handles incoming messages and updates the component state.
// Manages animation updates for pending tool calls.
-func (m *toolCallCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (m *toolCallCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case anim.StepMsg:
var cmds []tea.Cmd
@@ -160,7 +160,7 @@ func (m *toolCallCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
if m.spinning {
u, cmd := m.anim.Update(msg)
- m.anim = u.(util.Model)
+ m.anim = u
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
@@ -88,7 +88,7 @@ func (m *sidebarCmp) Init() tea.Cmd {
return nil
}
-func (m *sidebarCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (m *sidebarCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case SessionFilesMsg:
m.files = csync.NewMap[string, SessionFile]()
@@ -135,7 +135,7 @@ func (s *splashCmp) SetSize(width int, height int) tea.Cmd {
}
// Update implements SplashPage.
-func (s *splashCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
return s, s.SetSize(msg.Width, msg.Height)
@@ -112,7 +112,7 @@ func (c *completionsCmp) Init() tea.Cmd {
}
// Update implements Completions.
-func (c *completionsCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (c *completionsCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
c.wWidth, c.wHeight = msg.Width, msg.Height
@@ -36,7 +36,7 @@ func (m *statusCmp) Init() tea.Cmd {
return nil
}
-func (m *statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (m *statusCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.width = msg.Width
@@ -92,7 +92,7 @@ func (c *commandArgumentsDialogCmp) Init() tea.Cmd {
}
// Update implements CommandArgumentsDialog.
-func (c *commandArgumentsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (c *commandArgumentsDialogCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
c.wWidth = msg.Width
@@ -116,7 +116,7 @@ func (c *commandDialogCmp) Init() tea.Cmd {
return c.SetCommandType(c.commandType)
}
-func (c *commandDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (c *commandDialogCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
c.wWidth = msg.Width
@@ -32,7 +32,7 @@ type CloseDialogMsg struct{}
// DialogCmp manages a stack of dialogs with keyboard navigation.
type DialogCmp interface {
- tea.Model
+ util.Model
Dialogs() []DialogModel
HasDialogs() bool
@@ -62,7 +62,7 @@ func (d dialogCmp) Init() tea.Cmd {
}
// Update handles dialog lifecycle and forwards messages to the active dialog.
-func (d dialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (d dialogCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
var cmds []tea.Cmd
@@ -98,7 +98,11 @@ func (d dialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return d, nil
}
-func (d dialogCmp) handleOpen(msg OpenDialogMsg) (tea.Model, tea.Cmd) {
+func (d dialogCmp) View() string {
+ return ""
+}
+
+func (d dialogCmp) handleOpen(msg OpenDialogMsg) (util.Model, tea.Cmd) {
if d.HasDialogs() {
dialog := d.dialogs[len(d.dialogs)-1]
if dialog.ID() == msg.Model.ID() {
@@ -88,7 +88,7 @@ func (m *model) Init() tea.Cmd {
return m.filePicker.Init()
}
-func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (m *model) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.wWidth = msg.Width
@@ -9,6 +9,7 @@ import (
"github.com/charmbracelet/crush/internal/config"
"github.com/charmbracelet/crush/internal/home"
"github.com/charmbracelet/crush/internal/tui/styles"
+ "github.com/charmbracelet/crush/internal/tui/util"
"github.com/charmbracelet/lipgloss/v2"
)
@@ -75,7 +76,7 @@ func (a *APIKeyInput) Init() tea.Cmd {
return a.spinner.Tick
}
-func (a *APIKeyInput) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (a *APIKeyInput) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case spinner.TickMsg:
if a.state == APIKeyInputStateVerifying {
@@ -98,7 +98,7 @@ func (m *modelDialogCmp) Init() tea.Cmd {
return tea.Batch(m.modelList.Init(), m.apiKeyInput.Init())
}
-func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (m *modelDialogCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.wWidth = msg.Width
@@ -95,7 +95,7 @@ func (p *permissionDialogCmp) supportsDiffView() bool {
return p.permission.ToolName == tools.EditToolName || p.permission.ToolName == tools.WriteToolName || p.permission.ToolName == tools.MultiEditToolName
}
-func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (p *permissionDialogCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
var cmds []tea.Cmd
switch msg := msg.(type) {
@@ -40,7 +40,7 @@ func (q *quitDialogCmp) Init() tea.Cmd {
}
// Update handles keyboard input for the quit dialog.
-func (q *quitDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (q *quitDialogCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
q.wWidth = msg.Width
@@ -168,7 +168,7 @@ func (r *reasoningDialogCmp) populateEffortOptions() tea.Cmd {
return nil
}
-func (r *reasoningDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (r *reasoningDialogCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
r.wWidth = msg.Width
@@ -81,7 +81,7 @@ func (s *sessionDialogCmp) Init() tea.Cmd {
return tea.Sequence(cmds...)
}
-func (s *sessionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (s *sessionDialogCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
var cmds []tea.Cmd
@@ -9,6 +9,7 @@ import (
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/crush/internal/tui/components/core/layout"
"github.com/charmbracelet/crush/internal/tui/styles"
+ "github.com/charmbracelet/crush/internal/tui/util"
"github.com/charmbracelet/lipgloss/v2"
"github.com/sahilm/fuzzy"
)
@@ -116,7 +117,7 @@ func NewFilterableList[T FilterableItem](items []T, opts ...filterableListOption
return f
}
-func (f *filterableList[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (f *filterableList[T]) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyPressMsg:
switch {
@@ -11,6 +11,7 @@ import (
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/crush/internal/tui/components/core/layout"
"github.com/charmbracelet/crush/internal/tui/styles"
+ "github.com/charmbracelet/crush/internal/tui/util"
"github.com/charmbracelet/lipgloss/v2"
"github.com/sahilm/fuzzy"
)
@@ -65,7 +66,7 @@ func NewFilterableGroupedList[T FilterableItem](items []Group[T], opts ...filter
return f
}
-func (f *filterableGroupList[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (f *filterableGroupList[T]) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyPressMsg:
switch {
@@ -58,7 +58,7 @@ func (g *groupedList[T]) Init() tea.Cmd {
return g.render()
}
-func (l *groupedList[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (l *groupedList[T]) Update(msg tea.Msg) (util.Model, tea.Cmd) {
u, cmd := l.list.Update(msg)
l.list = u.(*list[Item])
return l, cmd
@@ -7,6 +7,7 @@ import (
"github.com/charmbracelet/crush/internal/tui/components/core"
"github.com/charmbracelet/crush/internal/tui/components/core/layout"
"github.com/charmbracelet/crush/internal/tui/styles"
+ "github.com/charmbracelet/crush/internal/tui/util"
"github.com/charmbracelet/lipgloss/v2"
"github.com/charmbracelet/x/ansi"
"github.com/google/uuid"
@@ -97,7 +98,7 @@ func (c *completionItemCmp[T]) Init() tea.Cmd {
}
// Update implements CommandItem.
-func (c *completionItemCmp[T]) Update(tea.Msg) (tea.Model, tea.Cmd) {
+func (c *completionItemCmp[T]) Update(tea.Msg) (util.Model, tea.Cmd) {
return c, nil
}
@@ -348,7 +349,7 @@ func (m *itemSectionModel) Init() tea.Cmd {
return nil
}
-func (m *itemSectionModel) Update(tea.Msg) (tea.Model, tea.Cmd) {
+func (m *itemSectionModel) Update(tea.Msg) (util.Model, tea.Cmd) {
return m, nil
}
@@ -217,7 +217,7 @@ func (l *list[T]) Init() tea.Cmd {
}
// Update implements List.
-func (l *list[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (l *list[T]) Update(msg tea.Msg) (util.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.MouseWheelMsg:
if l.enableMouse {
@@ -277,7 +277,7 @@ func (l *list[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return l, nil
}
-func (l *list[T]) handleMouseWheel(msg tea.MouseWheelMsg) (tea.Model, tea.Cmd) {
+func (l *list[T]) handleMouseWheel(msg tea.MouseWheelMsg) (util.Model, tea.Cmd) {
var cmd tea.Cmd
switch msg.Button {
case tea.MouseWheelDown:
@@ -7,6 +7,7 @@ import (
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/crush/internal/tui/components/core/layout"
+ "github.com/charmbracelet/crush/internal/tui/util"
"github.com/charmbracelet/lipgloss/v2"
"github.com/charmbracelet/x/exp/golden"
"github.com/google/uuid"
@@ -602,7 +603,7 @@ func (s *simpleItem) Init() tea.Cmd {
return nil
}
-func (s *simpleItem) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (s *simpleItem) Update(msg tea.Msg) (util.Model, tea.Cmd) {
return s, nil
}
@@ -644,7 +645,7 @@ func (s *selectableItem) IsFocused() bool {
return s.focused
}
-func execCmd(m tea.Model, cmd tea.Cmd) {
+func execCmd(m util.Model, cmd tea.Cmd) {
for cmd != nil {
msg := cmd()
m, cmd = m.Update(msg)
@@ -163,7 +163,7 @@ func (p *chatPage) Init() tea.Cmd {
)
}
-func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (p *chatPage) Update(msg tea.Msg) (util.Model, tea.Cmd) {
var cmds []tea.Cmd
switch msg := msg.(type) {
case tea.KeyboardEnhancementsMsg:
@@ -89,8 +89,6 @@ func (a appModel) Init() tea.Cmd {
cmd = a.status.Init()
cmds = append(cmds, cmd)
- cmds = append(cmds, tea.EnableMouseAllMotion)
-
return tea.Batch(cmds...)
}
@@ -104,9 +102,7 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyboardEnhancementsMsg:
for id, page := range a.pages {
m, pageCmd := page.Update(msg)
- if model, ok := m.(util.Model); ok {
- a.pages[id] = model
- }
+ a.pages[id] = m
if pageCmd != nil {
cmds = append(cmds, pageCmd)
@@ -234,9 +230,7 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Forward to view.
updated, itemCmd := item.Update(msg)
- if model, ok := updated.(util.Model); ok {
- a.pages[a.currentPage] = model
- }
+ a.pages[a.currentPage] = updated
return a, itemCmd
case pubsub.Event[permission.PermissionRequest]:
@@ -263,9 +257,7 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
a.isConfigured = config.HasInitialDataConfig()
updated, pageCmd := item.Update(msg)
- if model, ok := updated.(util.Model); ok {
- a.pages[a.currentPage] = model
- }
+ a.pages[a.currentPage] = updated
cmds = append(cmds, pageCmd)
return a, tea.Batch(cmds...)
@@ -285,9 +277,7 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
updated, pageCmd := item.Update(msg)
- if model, ok := updated.(util.Model); ok {
- a.pages[a.currentPage] = model
- }
+ a.pages[a.currentPage] = updated
cmds = append(cmds, pageCmd)
}
@@ -307,9 +297,7 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
updated, pageCmd := item.Update(msg)
- if model, ok := updated.(util.Model); ok {
- a.pages[a.currentPage] = model
- }
+ a.pages[a.currentPage] = updated
cmds = append(cmds, pageCmd)
}
@@ -324,9 +312,7 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
updated, cmd := item.Update(msg)
- if model, ok := updated.(util.Model); ok {
- a.pages[a.currentPage] = model
- }
+ a.pages[a.currentPage] = updated
if a.dialog.HasDialogs() {
u, dialogCmd := a.dialog.Update(msg)
@@ -362,9 +348,7 @@ func (a *appModel) handleWindowResize(width, height int) tea.Cmd {
// Update the current view.
for p, page := range a.pages {
updated, pageCmd := page.Update(tea.WindowSizeMsg{Width: width, Height: height})
- if model, ok := updated.(util.Model); ok {
- a.pages[p] = model
- }
+ a.pages[p] = updated
cmds = append(cmds, pageCmd)
}
@@ -467,9 +451,7 @@ func (a *appModel) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd {
}
updated, cmd := item.Update(msg)
- if model, ok := updated.(util.Model); ok {
- a.pages[a.currentPage] = model
- }
+ a.pages[a.currentPage] = updated
return cmd
}
}
@@ -573,6 +555,9 @@ func (a *appModel) View() tea.View {
view.Layer = canvas
view.Cursor = cursor
+ view.MouseMode = tea.MouseModeCellMotion
+ view.AltScreen = true
+
if a.app != nil && a.app.AgentCoordinator != nil && a.app.AgentCoordinator.IsBusy() {
// HACK: use a random percentage to prevent ghostty from hiding it
// after a timeout.
@@ -12,8 +12,9 @@ type Cursor interface {
}
type Model interface {
- tea.Model
- tea.ViewModel
+ Init() tea.Cmd
+ Update(tea.Msg) (Model, tea.Cmd)
+ View() string
}
func CmdHandler(msg tea.Msg) tea.Cmd {