11package tui
22
33import (
4- "github.com/charmbracelet/lipgloss "
4+ "fmt "
55
66 tea "github.com/charmbracelet/bubbletea"
77 "github.com/mendixlabs/mxcli/tui/panels"
@@ -25,6 +25,7 @@ type Model struct {
2525 focus Focus
2626 modulesPanel panels.ModulesPanel
2727 elementsPanel panels.ElementsPanel
28+ previewPanel panels.PreviewPanel
2829}
2930
3031func New (mxcliPath , projectPath string ) Model {
@@ -34,6 +35,7 @@ func New(mxcliPath, projectPath string) Model {
3435 focus : FocusModules ,
3536 modulesPanel : panels .NewModulesPanel (30 , 20 ),
3637 elementsPanel : panels .NewElementsPanel (40 , 20 ),
38+ previewPanel : panels .NewPreviewPanel (50 , 20 ),
3739 }
3840}
3941
@@ -48,6 +50,15 @@ func (m Model) Init() tea.Cmd {
4850 }
4951}
5052
53+ // describeNode returns a tea.Cmd that runs DESCRIBE for a given node.
54+ func (m Model ) describeNode (node * panels.TreeNode ) tea.Cmd {
55+ return func () tea.Msg {
56+ out , err := runMxcli (m .mxcliPath , "-p" , m .projectPath , "-c" ,
57+ fmt .Sprintf ("DESCRIBE %s %s" , node .Type , node .QualifiedName ))
58+ return panels.DescribeResultMsg {Content : out , Err : err }
59+ }
60+ }
61+
5162func (m Model ) Update (msg tea.Msg ) (tea.Model , tea.Cmd ) {
5263 switch msg := msg .(type ) {
5364 case tea.KeyMsg :
@@ -59,7 +70,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
5970 case FocusModules :
6071 switch msg .String () {
6172 case "l" , "right" , "enter" :
62- // Open selected module → show children in elements panel
6373 if node := m .modulesPanel .SelectedNode (); node != nil && len (node .Children ) > 0 {
6474 m .elementsPanel .SetNodes (node .Children )
6575 m .focus = FocusElements
@@ -77,29 +87,66 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
7787 m .focus = FocusModules
7888 return m , nil
7989 case "l" , "right" , "enter" :
80- // Drill down into node with children
81- if node := m .elementsPanel .SelectedNode (); node != nil && len (node .Children ) > 0 {
82- m .elementsPanel .SetNodes (node .Children )
83- return m , nil
90+ if node := m .elementsPanel .SelectedNode (); node != nil {
91+ if len (node .Children ) > 0 {
92+ // Drill down into node with children
93+ m .elementsPanel .SetNodes (node .Children )
94+ return m , nil
95+ }
96+ // Leaf node → show DESCRIBE in preview
97+ if node .QualifiedName != "" {
98+ m .focus = FocusPreview
99+ m .previewPanel .SetLoading ()
100+ return m , m .describeNode (node )
101+ }
84102 }
103+ case "j" , "down" , "k" , "up" :
104+ // Forward navigation, then auto-preview
105+ var cmd tea.Cmd
106+ m .elementsPanel , cmd = m .elementsPanel .Update (msg )
107+ if node := m .elementsPanel .SelectedNode (); node != nil && node .QualifiedName != "" {
108+ m .previewPanel .SetLoading ()
109+ return m , tea .Batch (cmd , m .describeNode (node ))
110+ }
111+ return m , cmd
85112 default :
86113 var cmd tea.Cmd
87114 m .elementsPanel , cmd = m .elementsPanel .Update (msg )
88115 return m , cmd
89116 }
117+
118+ case FocusPreview :
119+ switch msg .String () {
120+ case "h" , "left" , "esc" :
121+ m .focus = FocusElements
122+ return m , nil
123+ default :
124+ var cmd tea.Cmd
125+ m .previewPanel , cmd = m .previewPanel .Update (msg )
126+ return m , cmd
127+ }
90128 }
91129
92130 case tea.WindowSizeMsg :
93131 m .width = msg .Width
94132 m .height = msg .Height
133+ mW , eW , pW := columnWidths (m .width )
95134 contentH := m .height - 2
96- m .modulesPanel .SetSize (m .width / 3 , contentH )
97- m .elementsPanel .SetSize (m .width / 3 , contentH )
135+ m .modulesPanel .SetSize (mW , contentH )
136+ m .elementsPanel .SetSize (eW , contentH )
137+ m .previewPanel .SetSize (pW , contentH )
98138
99139 case panels.LoadTreeMsg :
100140 if msg .Err == nil && msg .Nodes != nil {
101141 m .modulesPanel .SetNodes (msg .Nodes )
102142 }
143+
144+ case panels.DescribeResultMsg :
145+ if msg .Err != nil {
146+ m .previewPanel .SetContent ("-- Error:\n " + msg .Content )
147+ } else {
148+ m .previewPanel .SetContent (msg .Content )
149+ }
103150 }
104151 return m , nil
105152}
@@ -110,9 +157,22 @@ func (m Model) View() string {
110157 }
111158 m .modulesPanel .SetFocused (m .focus == FocusModules )
112159 m .elementsPanel .SetFocused (m .focus == FocusElements )
160+ m .previewPanel .SetFocused (m .focus == FocusPreview )
161+
162+ mW , eW , pW := columnWidths (m .width )
163+ contentH := m .height - 2
164+ m .modulesPanel .SetSize (mW , contentH )
165+ m .elementsPanel .SetSize (eW , contentH )
166+ m .previewPanel .SetSize (pW , contentH )
167+
168+ statusLine := fmt .Sprintf (" mxcli tui %s [Tab: cycle focus | :: command | q: quit]" ,
169+ m .projectPath )
113170
114- return lipgloss .JoinHorizontal (lipgloss .Top ,
171+ return renderLayout (
172+ m .width ,
115173 m .modulesPanel .View (),
116174 m .elementsPanel .View (),
175+ m .previewPanel .View (),
176+ statusLine ,
117177 )
118178}
0 commit comments