@@ -6,6 +6,7 @@ import (
6 "github.com/charmbracelet/bubbles/v2/key"
6 "github.com/charmbracelet/bubbles/v2/key"
7 tea "github.com/charmbracelet/bubbletea/v2"
7 tea "github.com/charmbracelet/bubbletea/v2"
8 "github.com/opencode-ai/opencode/internal/app"
8 "github.com/opencode-ai/opencode/internal/app"
9+ "github.com/opencode-ai/opencode/internal/logging"
9 "github.com/opencode-ai/opencode/internal/message"
10 "github.com/opencode-ai/opencode/internal/message"
10 "github.com/opencode-ai/opencode/internal/session"
11 "github.com/opencode-ai/opencode/internal/session"
11 "github.com/opencode-ai/opencode/internal/tui/components/chat"
12 "github.com/opencode-ai/opencode/internal/tui/components/chat"
@@ -19,32 +20,28 @@ import (
20var ChatPage page.PageID = "chat"
21var ChatPage page.PageID = "chat"
23+type ChatFocusedMsg struct {
24+ Focused bool // True if the chat input is focused, false otherwise
22type chatPage struct {
27type chatPage struct {
23 app *app.App
28 app *app.App
25 layout layout.SplitPaneLayout
30 layout layout.SplitPaneLayout
27 session session.Session
32 session session.Session
30−type ChatKeyMap struct {
34+ keyMap KeyMap
31− NewSession key.Binding
35−var keyMap = ChatKeyMap{
36+ chatFocused bool
36− NewSession: key.NewBinding(
37− key.WithKeys("ctrl+n"),
38− key.WithHelp("ctrl+n", "new session"),
40− Cancel: key.NewBinding(
42− key.WithHelp("esc", "cancel"),
46func (p *chatPage) Init() tea.Cmd {
39func (p *chatPage) Init() tea.Cmd {
47− return p.layout.Init()
40+ cmd := p.layout.Init()
43+ p.layout.FocusPanel(layout.BottomPanel), // Focus on the bottom panel (editor)
50func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
47func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
@@ -79,13 +76,28 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
79 p.session = msg
76 p.session = msg
80 case tea.KeyPressMsg:
77 case tea.KeyPressMsg:
82− case key.Matches(msg, keyMap.NewSession):
79+ case key.Matches(msg, p.keyMap.NewSession):
83 p.session = session.Session{}
80 p.session = session.Session{}
84 return p, tea.Batch(
81 return p, tea.Batch(
85 p.clearMessages(),
82 p.clearMessages(),
86 util.CmdHandler(chat.SessionClearedMsg{}),
83 util.CmdHandler(chat.SessionClearedMsg{}),
86+ case key.Matches(msg, p.keyMap.Tab):
87+ logging.Info("Tab key pressed, toggling chat focus")
88+ if p.session.ID == "" {
91+ p.chatFocused = !p.chatFocused
93+ cmds = append(cmds, p.layout.FocusPanel(layout.LeftPanel))
94+ cmds = append(cmds, util.CmdHandler(ChatFocusedMsg{Focused: true}))
96+ cmds = append(cmds, p.layout.FocusPanel(layout.BottomPanel))
97+ cmds = append(cmds, util.CmdHandler(ChatFocusedMsg{Focused: false}))
99+ return p, tea.Batch(cmds...)
88− case key.Matches(msg, keyMap.Cancel):
100+ case key.Matches(msg, p.keyMap.Cancel):
89 if p.session.ID != "" {
101 if p.session.ID != "" {
90 // Cancel the current session's generation process
102 // Cancel the current session's generation process
91 // This allows users to interrupt long-running operations
103 // This allows users to interrupt long-running operations
@@ -148,11 +160,6 @@ func (p *chatPage) View() tea.View {
148 return p.layout.View()
160 return p.layout.View()
151−func (p *chatPage) BindingKeys() []key.Binding {
152− bindings := layout.KeyMapToSlice(keyMap)
156func NewChatPage(app *app.App) util.Model {
163func NewChatPage(app *app.App) util.Model {
157 sidebarContainer := layout.NewContainer(
164 sidebarContainer := layout.NewContainer(
158 sidebar.NewSidebarCmp(),
165 sidebar.NewSidebarCmp(),
@@ -169,5 +176,6 @@ func NewChatPage(app *app.App) util.Model {
169 layout.WithFixedBottomHeight(5),
176 layout.WithFixedBottomHeight(5),
170 layout.WithFixedRightWidth(31),
177 layout.WithFixedRightWidth(31),
179+ keyMap: DefaultKeyMap(),