Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 70 additions & 60 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,16 @@ const (
promptListView
)

type focusedPanel int

const (
mainPanelFocus focusedPanel = iota
debugPanelFocus
)

type AppModel struct {
state viewState
focusedPanel focusedPanel
ctx context.Context
session *mcp.ClientSession
toolList list.Model
Expand Down Expand Up @@ -310,6 +318,7 @@ func initialModel(ctx context.Context, session *mcp.ClientSession) *AppModel {

return &AppModel{
state: toolSelectionView,
focusedPanel: mainPanelFocus,
ctx: ctx,
session: session,
toolList: toolList,
Expand Down Expand Up @@ -361,9 +370,17 @@ func (m AppModel) Init() tea.Cmd {

func (m *AppModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
var cmds []tea.Cmd

switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
debugPanelWidth := m.width / 3
m.debugViewport.Width = debugPanelWidth - 2
m.debugViewport.Height = m.height - 2
m.debugViewport, cmd = m.debugViewport.Update(msg)
return m, cmd

case toolResult:
if msg.err != nil {
m.err = msg.err
Expand All @@ -374,6 +391,8 @@ func (m *AppModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
m.logf("Result:\n========\n%s", msg.result)
m.result = msg.result
return m, nil

case resourceResult:
if msg.err != nil {
m.err = msg.err
Expand All @@ -382,13 +401,22 @@ func (m *AppModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if verbose {
m.logf("Resource result received")
}
m.logf("Result:\n========\n%s", msg.result)
m.resourceResult = msg.result
return m, nil

case tea.KeyMsg:
if verbose {
m.logf("Key pressed: %s", msg.String())
}
// Global key bindings that work regardless of focus
switch msg.Type {
case tea.KeyTab:
if m.focusedPanel == mainPanelFocus {
m.focusedPanel = debugPanelFocus
} else {
m.focusedPanel = mainPanelFocus
}
return m, nil
case tea.KeyEsc:
if m.state == resourceDetailView {
m.state = resourceListView
Expand All @@ -399,54 +427,29 @@ func (m *AppModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyCtrlC:
return m, tea.Quit
}
}

switch m.state {
case toolSelectionView:
var model tea.Model
model, cmd = m.updateToolSelectionView(msg)
cmds = append(cmds, cmd)
m.debugViewport, cmd = m.debugViewport.Update(msg)
cmds = append(cmds, cmd)
return model, tea.Batch(cmds...)
case resourceListView:
var model tea.Model
model, cmd = m.updateResourceListView(msg)
cmds = append(cmds, cmd)
m.debugViewport, cmd = m.debugViewport.Update(msg)
cmds = append(cmds, cmd)
return model, tea.Batch(cmds...)
case promptListView:
var model tea.Model
model, cmd = m.updatePromptListView(msg)
cmds = append(cmds, cmd)
m.debugViewport, cmd = m.debugViewport.Update(msg)
cmds = append(cmds, cmd)
return model, tea.Batch(cmds...)
case argumentInputView:
var model tea.Model
model, cmd = m.updateArgumentInputView(msg)
cmds = append(cmds, cmd)
m.debugViewport, cmd = m.debugViewport.Update(msg)
cmds = append(cmds, cmd)
return model, tea.Batch(cmds...)
case resourceDetailView:
m.debugViewport, cmd = m.debugViewport.Update(msg)
cmds = append(cmds, cmd)
return m, tea.Batch(cmds...)
}

case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
debugWidth := m.width / 3
m.debugViewport.Width = debugWidth
m.debugViewport.Height = m.height - 2
// Delegate message to the focused panel
if m.focusedPanel == debugPanelFocus {
m.debugViewport, cmd = m.debugViewport.Update(msg)
return m, cmd
}

m.debugViewport, cmd = m.debugViewport.Update(msg)
cmds = append(cmds, cmd)
// Main panel has focus, delegate to the active view
switch m.state {
case toolSelectionView:
return m.updateToolSelectionView(msg)
case resourceListView:
return m.updateResourceListView(msg)
case promptListView:
return m.updatePromptListView(msg)
case argumentInputView:
return m.updateArgumentInputView(msg)
case resourceDetailView:
return m, nil
}

return m, tea.Batch(cmds...)
return m, nil
}

func (m *AppModel) updateToolSelectionView(msg tea.Msg) (tea.Model, tea.Cmd) {
Expand Down Expand Up @@ -586,19 +589,19 @@ func (m AppModel) View() string {
return fmt.Sprintf("Error: %v\n\nPress ctrl+c to quit.", m.err)
}

mainWidth := m.width / 3
//debugWidth := m.width - mainWidth
debugPanelWidth := m.width / 3
mainPanelWidth := m.width - debugPanelWidth

var mainContent strings.Builder
switch m.state {
case toolSelectionView:
m.toolList.SetSize(mainWidth-2, m.height-2)
m.toolList.SetSize(mainPanelWidth-2, m.height-2)
mainContent.WriteString(m.toolList.View())
case resourceListView:
m.resourceList.SetSize(mainWidth-2, m.height-2)
m.resourceList.SetSize(mainPanelWidth-2, m.height-2)
mainContent.WriteString(m.resourceList.View())
case promptListView:
m.promptList.SetSize(mainWidth-2, m.height-2)
m.promptList.SetSize(mainPanelWidth-2, m.height-2)
mainContent.WriteString(m.promptList.View())
case resourceDetailView:
var b strings.Builder
Expand All @@ -619,17 +622,24 @@ func (m AppModel) View() string {
mainContent.WriteString(b.String())
}

mainPanel := lipgloss.NewStyle().
mainPanelStyle := lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
Width(mainWidth).
Height(m.height - 2).
Render(mainContent.String())
Width(mainPanelWidth - 2).
Height(m.height - 2)

debugPanel := lipgloss.NewStyle().
debugPanelStyle := lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
Width(m.debugViewport.Width).
Height(m.debugViewport.Height).
Render(m.debugViewport.View())
Width(debugPanelWidth - 2).
Height(m.debugViewport.Height)

if m.focusedPanel == mainPanelFocus {
mainPanelStyle = mainPanelStyle.BorderForeground(lipgloss.Color("228")) // Yellow
} else {
debugPanelStyle = debugPanelStyle.BorderForeground(lipgloss.Color("228")) // Yellow
}

mainPanel := mainPanelStyle.Render(mainContent.String())
debugPanel := debugPanelStyle.Render(m.debugViewport.View())

return lipgloss.JoinHorizontal(lipgloss.Top, mainPanel, debugPanel)
}
Expand Down Expand Up @@ -777,7 +787,7 @@ func handleSession(ctx context.Context, session *mcp.ClientSession) {
}
defer f.Close()
}
p := tea.NewProgram(initialModel(ctx, session), tea.WithAltScreen())
p := tea.NewProgram(initialModel(ctx, session), tea.WithAltScreen(), tea.WithMouseCellMotion())
if _, err := p.Run(); err != nil {
log.Fatalf("Error running program: %v", err)
}
Expand Down
Loading