@@ -20,11 +20,11 @@ import (
2020 "github.com/charmbracelet/crush/internal/message"
2121 "github.com/charmbracelet/crush/internal/pubsub"
2222 "github.com/charmbracelet/crush/internal/session"
23− "github.com/charmbracelet/crush/internal/tui/util"
2423 "github.com/charmbracelet/crush/internal/ui/common"
2524 "github.com/charmbracelet/crush/internal/ui/dialog"
2625 "github.com/charmbracelet/crush/internal/ui/logo"
2726 "github.com/charmbracelet/crush/internal/ui/styles"
27+ "github.com/charmbracelet/crush/internal/uiutil"
2828 "github.com/charmbracelet/crush/internal/version"
2929 uv "github.com/charmbracelet/ultraviolet"
3030 "github.com/charmbracelet/ultraviolet/screen"
@@ -194,12 +194,6 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
194194 // TODO: Get. Rid. Of. Magic numbers!
195195 sessions.SetSize(min(120, m.width-8), 30)
196196 m.dialog.AddDialog(sessions)
197− case dialog.SessionSelectedMsg:
198− m.dialog.RemoveDialog(dialog.SessionsID)
199− cmds = append(cmds,
200− m.loadSession(msg.Session.ID),
201− m.loadSessionFiles(msg.Session.ID),
202− )
203197 case sessionLoadedMsg:
204198 m.state = uiChat
205199 m.session = &msg.sess
@@ -330,15 +324,6 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
330324 }
331325 case tea.KeyPressMsg:
332326 cmds = append(cmds, m.handleKeyPressMsg(msg)...)
333−
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)
342327 }
343328
344329 // 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) {
384369 return true
385370 }
386371 switch {
387− case key.Matches(msg, m.keyMap.Tab):
388372 case key.Matches(msg, m.keyMap.Help):
389373 m.help.ShowAll = !m.help.ShowAll
390374 m.updateLayoutAndSize()
@@ -400,7 +384,7 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
400384 }
401385 commands, err := dialog.NewCommands(m.com, sessionID)
402386 if err != nil {
403− cmds = append(cmds, util.ReportError(err))
387+ cmds = append(cmds, uiutil.ReportError(err))
404388 } else {
405389 // TODO: Get. Rid. Of. Magic numbers!
406390 commands.SetSize(min(120, m.width-8), 30)
@@ -427,64 +411,101 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
427411 return cmds
428412 }
429413
430− updatedDialog, cmd := m.dialog.Update(msg)
431− m.dialog = updatedDialog
432− if cmd != nil {
433− cmds = append(cmds, cmd)
414+ msg := m.dialog.Update(msg)
415+ if msg == nil {
416+ return cmds
434417 }
418+
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)
426+ cmds = append(cmds,
427+ m.loadSession(msg.Session.ID),
428+ m.loadSessionFiles(msg.Session.ID),
429+ )
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)
439+ if err != nil {
440+ cmds = append(cmds, uiutil.ReportError(err))
441+ }
442+ case dialog.ToggleHelpMsg:
443+ m.help.ShowAll = !m.help.ShowAll
444+ case dialog.QuitMsg:
445+ cmds = append(cmds, tea.Quit)
446+ }
447+
435448 return cmds
436449 }
437450
438451 switch m.state {
439452 case uiChat:
440− switch {
441− case key.Matches(msg, m.keyMap.Tab):
442− if m.focus == uiFocusMain {
443− m.focus = uiFocusEditor
444− cmds = append(cmds, m.textarea.Focus())
445− m.chat.Blur()
446− } else {
453+ switch m.focus {
454+ case uiFocusEditor:
455+ switch {
456+ case key.Matches(msg, m.keyMap.Tab):
447457 m.focus = uiFocusMain
448458 m.textarea.Blur()
449459 m.chat.Focus()
450460 m.chat.SetSelected(m.chat.Len() - 1)
461+ default:
462+ handleGlobalKeys(msg)
451463 }
452− case key.Matches(msg, m.keyMap.Chat.Up):
453− m.chat.ScrollBy(-1)
454− if !m.chat.SelectedItemInView() {
464+ case uiFocusMain:
465+ switch {
466+ case key.Matches(msg, m.keyMap.Tab):
467+ m.focus = uiFocusEditor
468+ cmds = append(cmds, m.textarea.Focus())
469+ m.chat.Blur()
470+ case key.Matches(msg, m.keyMap.Chat.Up):
471+ m.chat.ScrollBy(-1)
472+ if !m.chat.SelectedItemInView() {
473+ m.chat.SelectPrev()
474+ m.chat.ScrollToSelected()
475+ }
476+ case key.Matches(msg, m.keyMap.Chat.Down):
477+ m.chat.ScrollBy(1)
478+ if !m.chat.SelectedItemInView() {
479+ m.chat.SelectNext()
480+ m.chat.ScrollToSelected()
481+ }
482+ case key.Matches(msg, m.keyMap.Chat.UpOneItem):
455483 m.chat.SelectPrev()
456484 m.chat.ScrollToSelected()
457− }
458− case key.Matches(msg, m.keyMap.Chat.Down):
459− m.chat.ScrollBy(1)
460− if !m.chat.SelectedItemInView() {
485+ case key.Matches(msg, m.keyMap.Chat.DownOneItem):
461486 m.chat.SelectNext()
462487 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()
505+ m.chat.SelectLast()
506+ default:
507+ handleGlobalKeys(msg)
463508 }
464− case key.Matches(msg, m.keyMap.Chat.UpOneItem):
465− m.chat.SelectPrev()
466− m.chat.ScrollToSelected()
467− case key.Matches(msg, m.keyMap.Chat.DownOneItem):
468− m.chat.SelectNext()
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()
487− m.chat.SelectLast()
488509 default:
489510 handleGlobalKeys(msg)
490511 }
@@ -588,11 +609,29 @@ func (m *UI) Cursor() *tea.Cursor {
588609 // Don't show cursor if editor is not visible
589610 return nil
590611 }
591− if m.focus == uiFocusEditor && m.textarea.Focused() {
592− cur := m.textarea.Cursor()
593− cur.X++ // Adjust for app margins
594− cur.Y += m.layout.editor.Min.Y
595− return cur
612+ if m.dialog.HasDialogs() {
613+ if front := m.dialog.DialogLast(); front != nil {
614+ c, ok := front.(uiutil.Cursor)
615+ if ok {
616+ cur := c.Cursor()
617+ if cur != nil {
618+ pos := m.dialog.CenterPosition(m.layout.area, front.ID())
619+ cur.X += pos.Min.X
620+ cur.Y += pos.Min.Y
621+ return cur
622+ }
623+ }
624+ }
625+ return nil
626+ }
627+ switch m.focus {
628+ case uiFocusEditor:
629+ if m.textarea.Focused() {
630+ cur := m.textarea.Cursor()
631+ cur.X++ // Adjust for app margins
632+ cur.Y += m.layout.editor.Min.Y
633+ return cur
634+ }
596635 }
597636 return nil
598637}