@@ -20,11 +20,11 @@ import (
20 "github.com/charmbracelet/crush/internal/message"
20 "github.com/charmbracelet/crush/internal/message"
21 "github.com/charmbracelet/crush/internal/pubsub"
21 "github.com/charmbracelet/crush/internal/pubsub"
22 "github.com/charmbracelet/crush/internal/session"
22 "github.com/charmbracelet/crush/internal/session"
23− "github.com/charmbracelet/crush/internal/tui/util"
24 "github.com/charmbracelet/crush/internal/ui/common"
23 "github.com/charmbracelet/crush/internal/ui/common"
25 "github.com/charmbracelet/crush/internal/ui/dialog"
24 "github.com/charmbracelet/crush/internal/ui/dialog"
26 "github.com/charmbracelet/crush/internal/ui/logo"
25 "github.com/charmbracelet/crush/internal/ui/logo"
27 "github.com/charmbracelet/crush/internal/ui/styles"
26 "github.com/charmbracelet/crush/internal/ui/styles"
27+ "github.com/charmbracelet/crush/internal/uiutil"
28 "github.com/charmbracelet/crush/internal/version"
28 "github.com/charmbracelet/crush/internal/version"
29 uv "github.com/charmbracelet/ultraviolet"
29 uv "github.com/charmbracelet/ultraviolet"
30 "github.com/charmbracelet/ultraviolet/screen"
30 "github.com/charmbracelet/ultraviolet/screen"
@@ -194,12 +194,6 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
194 // TODO: Get. Rid. Of. Magic numbers!
194 // TODO: Get. Rid. Of. Magic numbers!
195 sessions.SetSize(min(120, m.width-8), 30)
195 sessions.SetSize(min(120, m.width-8), 30)
196 m.dialog.AddDialog(sessions)
196 m.dialog.AddDialog(sessions)
197− case dialog.SessionSelectedMsg:
198− m.dialog.RemoveDialog(dialog.SessionsID)
200− m.loadSession(msg.Session.ID),
201− m.loadSessionFiles(msg.Session.ID),
203 case sessionLoadedMsg:
197 case sessionLoadedMsg:
204 m.state = uiChat
198 m.state = uiChat
205 m.session = &msg.sess
199 m.session = &msg.sess
@@ -330,15 +324,6 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
331 case tea.KeyPressMsg:
325 case tea.KeyPressMsg:
332 cmds = append(cmds, m.handleKeyPressMsg(msg)...)
326 cmds = append(cmds, m.handleKeyPressMsg(msg)...)
334− // Command dialog messages
335− // TODO: Properly structure and handle these messages
336− case dialog.ToggleYoloModeMsg:
337− m.com.App.Permissions.SetSkipRequests(!m.com.App.Permissions.SkipRequests())
338− m.dialog.RemoveDialog(dialog.CommandsID)
339− case dialog.SwitchSessionsMsg:
340− cmds = append(cmds, m.loadSessionsCmd)
341− m.dialog.RemoveDialog(dialog.CommandsID)
344 // This logic gets triggered on any message type, but should it?
329 // This logic gets triggered on any message type, but should it?
@@ -384,7 +369,6 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
384 return true
369 return true
387− case key.Matches(msg, m.keyMap.Tab):
388 case key.Matches(msg, m.keyMap.Help):
372 case key.Matches(msg, m.keyMap.Help):
389 m.help.ShowAll = !m.help.ShowAll
373 m.help.ShowAll = !m.help.ShowAll
390 m.updateLayoutAndSize()
374 m.updateLayoutAndSize()
@@ -400,7 +384,7 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
401 commands, err := dialog.NewCommands(m.com, sessionID)
385 commands, err := dialog.NewCommands(m.com, sessionID)
402 if err != nil {
386 if err != nil {
403− cmds = append(cmds, util.ReportError(err))
387+ cmds = append(cmds, uiutil.ReportError(err))
405 // TODO: Get. Rid. Of. Magic numbers!
389 // TODO: Get. Rid. Of. Magic numbers!
406 commands.SetSize(min(120, m.width-8), 30)
390 commands.SetSize(min(120, m.width-8), 30)
@@ -427,64 +411,101 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
427 return cmds
411 return cmds
430− updatedDialog, cmd := m.dialog.Update(msg)
414+ msg := m.dialog.Update(msg)
431− m.dialog = updatedDialog
432− if cmd != nil {
415+ if msg == nil {
433− cmds = append(cmds, cmd)
416+ return cmds
419+ switch msg := msg.(type) {
420+ // Generic dialog messages
421+ case dialog.CloseMsg:
422+ m.dialog.RemoveFrontDialog()
423+ // Session dialog messages
424+ case dialog.SessionSelectedMsg:
425+ m.dialog.RemoveDialog(dialog.SessionsID)
427+ m.loadSession(msg.Session.ID),
428+ m.loadSessionFiles(msg.Session.ID),
430+ // Command dialog messages
431+ case dialog.ToggleYoloModeMsg:
432+ m.com.App.Permissions.SetSkipRequests(!m.com.App.Permissions.SkipRequests())
433+ m.dialog.RemoveDialog(dialog.CommandsID)
434+ case dialog.SwitchSessionsMsg:
435+ cmds = append(cmds, m.loadSessionsCmd)
436+ m.dialog.RemoveDialog(dialog.CommandsID)
437+ case dialog.CompactMsg:
438+ err := m.com.App.AgentCoordinator.Summarize(context.Background(), msg.SessionID)
440+ cmds = append(cmds, uiutil.ReportError(err))
442+ case dialog.ToggleHelpMsg:
443+ m.help.ShowAll = !m.help.ShowAll
444+ case dialog.QuitMsg:
445+ cmds = append(cmds, tea.Quit)
435 return cmds
448 return cmds
438 switch m.state {
451 switch m.state {
439 case uiChat:
452 case uiChat:
440− switch {
453+ switch m.focus {
441− case key.Matches(msg, m.keyMap.Tab):
456+ case key.Matches(msg, m.keyMap.Tab):
442− if m.focus == uiFocusMain {
443− m.focus = uiFocusEditor
444− cmds = append(cmds, m.textarea.Focus())
447 m.focus = uiFocusMain
457 m.focus = uiFocusMain
448 m.textarea.Blur()
458 m.textarea.Blur()
449 m.chat.Focus()
459 m.chat.Focus()
450 m.chat.SetSelected(m.chat.Len() - 1)
460 m.chat.SetSelected(m.chat.Len() - 1)
462+ handleGlobalKeys(msg)
466+ case key.Matches(msg, m.keyMap.Tab):
467+ m.focus = uiFocusEditor
468+ cmds = append(cmds, m.textarea.Focus())
452− case key.Matches(msg, m.keyMap.Chat.Up):
470+ case key.Matches(msg, m.keyMap.Chat.Up):
453− m.chat.ScrollBy(-1)
471+ m.chat.ScrollBy(-1)
472+ if !m.chat.SelectedItemInView() {
474+ m.chat.ScrollToSelected()
476+ case key.Matches(msg, m.keyMap.Chat.Down):
454− if !m.chat.SelectedItemInView() {
478+ if !m.chat.SelectedItemInView() {
480+ m.chat.ScrollToSelected()
482+ case key.Matches(msg, m.keyMap.Chat.UpOneItem):
455 m.chat.SelectPrev()
483 m.chat.SelectPrev()
456 m.chat.ScrollToSelected()
484 m.chat.ScrollToSelected()
458− case key.Matches(msg, m.keyMap.Chat.Down):
485+ case key.Matches(msg, m.keyMap.Chat.DownOneItem):
460− if !m.chat.SelectedItemInView() {
461 m.chat.SelectNext()
486 m.chat.SelectNext()
462 m.chat.ScrollToSelected()
487 m.chat.ScrollToSelected()
488+ case key.Matches(msg, m.keyMap.Chat.HalfPageUp):
489+ m.chat.ScrollBy(-m.chat.Height() / 2)
490+ m.chat.SelectFirstInView()
491+ case key.Matches(msg, m.keyMap.Chat.HalfPageDown):
492+ m.chat.ScrollBy(m.chat.Height() / 2)
493+ m.chat.SelectLastInView()
494+ case key.Matches(msg, m.keyMap.Chat.PageUp):
495+ m.chat.ScrollBy(-m.chat.Height())
496+ m.chat.SelectFirstInView()
497+ case key.Matches(msg, m.keyMap.Chat.PageDown):
498+ m.chat.ScrollBy(m.chat.Height())
499+ m.chat.SelectLastInView()
500+ case key.Matches(msg, m.keyMap.Chat.Home):
501+ m.chat.ScrollToTop()
502+ m.chat.SelectFirst()
503+ case key.Matches(msg, m.keyMap.Chat.End):
504+ m.chat.ScrollToBottom()
507+ handleGlobalKeys(msg)
464− case key.Matches(msg, m.keyMap.Chat.UpOneItem):
466− m.chat.ScrollToSelected()
467− case key.Matches(msg, m.keyMap.Chat.DownOneItem):
469− m.chat.ScrollToSelected()
470− case key.Matches(msg, m.keyMap.Chat.HalfPageUp):
471− m.chat.ScrollBy(-m.chat.Height() / 2)
472− m.chat.SelectFirstInView()
473− case key.Matches(msg, m.keyMap.Chat.HalfPageDown):
474− m.chat.ScrollBy(m.chat.Height() / 2)
475− m.chat.SelectLastInView()
476− case key.Matches(msg, m.keyMap.Chat.PageUp):
477− m.chat.ScrollBy(-m.chat.Height())
478− m.chat.SelectFirstInView()
479− case key.Matches(msg, m.keyMap.Chat.PageDown):
480− m.chat.ScrollBy(m.chat.Height())
481− m.chat.SelectLastInView()
482− case key.Matches(msg, m.keyMap.Chat.Home):
483− m.chat.ScrollToTop()
484− m.chat.SelectFirst()
485− case key.Matches(msg, m.keyMap.Chat.End):
486− m.chat.ScrollToBottom()
489 handleGlobalKeys(msg)
510 handleGlobalKeys(msg)
@@ -588,11 +609,29 @@ func (m *UI) Cursor() *tea.Cursor {
588 // Don't show cursor if editor is not visible
609 // Don't show cursor if editor is not visible
589 return nil
610 return nil
612+ if m.dialog.HasDialogs() {
613+ if front := m.dialog.DialogLast(); front != nil {
614+ c, ok := front.(uiutil.Cursor)
618+ pos := m.dialog.CenterPosition(m.layout.area, front.ID())
591− if m.focus == uiFocusEditor && m.textarea.Focused() {
629+ if m.textarea.Focused() {
592− cur := m.textarea.Cursor()
630+ cur := m.textarea.Cursor()
593− cur.X++ // Adjust for app margins
631+ cur.X++ // Adjust for app margins
594− cur.Y += m.layout.editor.Min.Y
632+ cur.Y += m.layout.editor.Min.Y
595− return cur
633+ return cur
597 return nil
636 return nil