@@ -0,0 +1,229 @@
4+ "github.com/charmbracelet/bubbles/key"
5+ tea "github.com/charmbracelet/bubbletea"
6+ "github.com/charmbracelet/lipgloss"
7+ "github.com/kujtimiihoxha/termai/internal/tui/styles"
10+type SplitPaneLayout interface {
15+type splitPaneLayout struct {
19+ verticalRatio float64
23+ bottomPanel Container
25+ backgroundColor lipgloss.TerminalColor
28+type SplitPaneOption func(*splitPaneLayout)
30+func (s *splitPaneLayout) Init() tea.Cmd {
33+ if s.leftPanel != nil {
34+ cmds = append(cmds, s.leftPanel.Init())
37+ if s.rightPanel != nil {
38+ cmds = append(cmds, s.rightPanel.Init())
41+ if s.bottomPanel != nil {
42+ cmds = append(cmds, s.bottomPanel.Init())
45+ return tea.Batch(cmds...)
48+func (s *splitPaneLayout) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
50+ switch msg := msg.(type) {
51+ case tea.WindowSizeMsg:
52+ s.SetSize(msg.Width, msg.Height)
56+ if s.rightPanel != nil {
57+ u, cmd := s.rightPanel.Update(msg)
58+ s.rightPanel = u.(Container)
60+ cmds = append(cmds, cmd)
64+ if s.leftPanel != nil {
65+ u, cmd := s.leftPanel.Update(msg)
66+ s.leftPanel = u.(Container)
68+ cmds = append(cmds, cmd)
72+ if s.bottomPanel != nil {
73+ u, cmd := s.bottomPanel.Update(msg)
74+ s.bottomPanel = u.(Container)
76+ cmds = append(cmds, cmd)
80+ return s, tea.Batch(cmds...)
83+func (s *splitPaneLayout) View() string {
84+ var topSection string
86+ if s.leftPanel != nil && s.rightPanel != nil {
87+ leftView := s.leftPanel.View()
88+ rightView := s.rightPanel.View()
89+ topSection = lipgloss.JoinHorizontal(lipgloss.Top, leftView, rightView)
90+ } else if s.leftPanel != nil {
91+ topSection = s.leftPanel.View()
92+ } else if s.rightPanel != nil {
93+ topSection = s.rightPanel.View()
100+ if s.bottomPanel != nil && topSection != "" {
101+ bottomView := s.bottomPanel.View()
102+ finalView = lipgloss.JoinVertical(lipgloss.Left, topSection, bottomView)
103+ } else if s.bottomPanel != nil {
104+ finalView = s.bottomPanel.View()
106+ finalView = topSection
109+ if s.backgroundColor != nil && finalView != "" {
110+ style := lipgloss.NewStyle().
113+ Background(s.backgroundColor)
115+ return style.Render(finalView)
121+func (s *splitPaneLayout) SetSize(width, height int) {
125+ var topHeight, bottomHeight int
126+ if s.bottomPanel != nil {
127+ topHeight = int(float64(height) * s.verticalRatio)
128+ bottomHeight = height - topHeight
134+ var leftWidth, rightWidth int
135+ if s.leftPanel != nil && s.rightPanel != nil {
136+ leftWidth = int(float64(width) * s.ratio)
137+ rightWidth = width - leftWidth
138+ } else if s.leftPanel != nil {
141+ } else if s.rightPanel != nil {
146+ if s.leftPanel != nil {
147+ s.leftPanel.SetSize(leftWidth, topHeight)
150+ if s.rightPanel != nil {
151+ s.rightPanel.SetSize(rightWidth, topHeight)
154+ if s.bottomPanel != nil {
155+ s.bottomPanel.SetSize(width, bottomHeight)
159+func (s *splitPaneLayout) GetSize() (int, int) {
160+ return s.width, s.height
163+func (s *splitPaneLayout) BindingKeys() []key.Binding {
164+ keys := []key.Binding{}
165+ if s.leftPanel != nil {
166+ if b, ok := s.leftPanel.(Bindings); ok {
167+ keys = append(keys, b.BindingKeys()...)
170+ if s.rightPanel != nil {
171+ if b, ok := s.rightPanel.(Bindings); ok {
172+ keys = append(keys, b.BindingKeys()...)
175+ if s.bottomPanel != nil {
176+ if b, ok := s.bottomPanel.(Bindings); ok {
177+ keys = append(keys, b.BindingKeys()...)
183+func NewSplitPane(options ...SplitPaneOption) SplitPaneLayout {
184+ layout := &splitPaneLayout{
186+ verticalRatio: 0.9, // Default 80% for top section, 20% for bottom
187+ backgroundColor: styles.Background,
189+ for _, option := range options {
195+func WithLeftPanel(panel Container) SplitPaneOption {
196+ return func(s *splitPaneLayout) {
201+func WithRightPanel(panel Container) SplitPaneOption {
202+ return func(s *splitPaneLayout) {
203+ s.rightPanel = panel
207+func WithRatio(ratio float64) SplitPaneOption {
208+ return func(s *splitPaneLayout) {
213+func WithSplitBackgroundColor(color lipgloss.TerminalColor) SplitPaneOption {
214+ return func(s *splitPaneLayout) {
215+ s.backgroundColor = color
219+func WithBottomPanel(panel Container) SplitPaneOption {
220+ return func(s *splitPaneLayout) {
221+ s.bottomPanel = panel
225+func WithVerticalRatio(ratio float64) SplitPaneOption {
226+ return func(s *splitPaneLayout) {
227+ s.verticalRatio = ratio