package main import ( "log" "os" "strconv" "strings" engine "git.pablu.de/pablu/sqv-engine" "github.com/charmbracelet/bubbles/table" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) var baseStyle = lipgloss.NewStyle(). BorderStyle(lipgloss.NormalBorder()). BorderForeground(lipgloss.Color("240")) func tabBorderWithBottom(left, middle, right string) lipgloss.Border { border := lipgloss.RoundedBorder() border.BottomLeft = left border.Bottom = middle border.BottomRight = right return border } var ( inactiveTabBorder = tabBorderWithBottom("┴", "─", "┴") activeTabBorder = tabBorderWithBottom("┘", " ", "└") docStyle = lipgloss.NewStyle().Padding(1, 2, 1, 2) highlightColor = lipgloss.AdaptiveColor{Light: "#874BFD", Dark: "#7D56F4"} inactiveTabStyle = lipgloss.NewStyle().Border(inactiveTabBorder, true).BorderForeground(highlightColor).Padding(0, 1) activeTabStyle = inactiveTabStyle.Border(activeTabBorder, true) windowStyle = lipgloss.NewStyle().BorderForeground(highlightColor).Padding(2, 0).Align(lipgloss.Center).Border(lipgloss.NormalBorder()).UnsetBorderTop() ) type model struct { tables []table.Model tableNames []string currTable int } func (m model) Init() tea.Cmd { return nil } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "ctrl+c", "q": return m, tea.Quit case "right", "l", "n", "tab": m.currTable = min(m.currTable+1, len(m.tables)-1) return m, nil case "left", "h", "p", "shift+tab": m.currTable = max(m.currTable-1, 0) return m, nil } } m.tables[m.currTable], cmd = m.tables[m.currTable].Update(msg) return m, cmd } func (m model) View() string { doc := strings.Builder{} var renderedTabs []string for i, t := range m.tableNames { var style lipgloss.Style isFirst, isLast, isActive := i == 0, i == len(m.tables)-1, i == m.currTable if isActive { style = activeTabStyle } else { style = inactiveTabStyle } border, _, _, _, _ := style.GetBorder() if isFirst && isActive { border.BottomLeft = "│" } else if isFirst && !isActive { border.BottomLeft = "├" } else if isLast && isActive { border.BottomRight = "│" } else if isLast && !isActive { border.BottomRight = "┤" } style = style.Border(border) renderedTabs = append(renderedTabs, style.Render(t)) } row := lipgloss.JoinHorizontal(lipgloss.Top, renderedTabs...) doc.WriteString(row) doc.WriteString("\n") doc.WriteString(windowStyle.Width((lipgloss.Width(row) - windowStyle.GetHorizontalFrameSize())).Render(m.tables[m.currTable].View())) return docStyle.Render(doc.String()) // return baseStyle.Render(m.table.View()) + "\n" } func convertToViewTable(m *engine.Manager) ([]string, []table.Model) { tables := m.GetTables() res := make([]table.Model, len(tables)) resNames := make([]string, len(tables)) for j, t := range tables { columns := make([]table.Column, len(t.Columns)) for i, column := range t.Columns { columns[i] = table.Column{ Title: column.Name, Width: 10, } } for k := range 50 { row := engine.Row{Values: make([]string, len(t.Columns))} for i := range t.Columns { row.Values[i] = "test " + strconv.Itoa(k) } t.Rows = append(t.Rows, row) } rows := make([]table.Row, len(t.Rows)) for i, row := range t.Rows { rows[i] = row.Values } resTable := table.New( table.WithColumns(columns), table.WithRows(rows), table.WithFocused(true), table.WithHeight(7), ) s := table.DefaultStyles() s.Header = s.Header. BorderStyle(lipgloss.NormalBorder()). BorderForeground(lipgloss.Color("240")). BorderBottom(true). Bold(false) s.Selected = s.Selected. Foreground(lipgloss.Color("229")). Background(lipgloss.Color("57")). Bold(false) resTable.SetStyles(s) resNames[j] = t.Name res[j] = resTable } return resNames, res } func main() { file, err := os.ReadFile("test.sql") if err != nil { log.Fatal(err) } m := engine.NewManagerFromFile(string(file)) m.Start() tableNames, tables := convertToViewTable(m) p := tea.NewProgram(model{tables: tables, tableNames: tableNames}, tea.WithAltScreen()) if _, err := p.Run(); err != nil { log.Fatalf("Alas, theres been an error: %v", err) } }