Files
sqv-engine/cmd/sqv-tea/main.go
2026-01-16 14:55:04 +01:00

204 lines
4.0 KiB
Go

package main
import (
"log"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/table"
"github.com/charmbracelet/bubbles/textarea"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
type mainModel struct {
width, height int
focused int
viewStyle lipgloss.Style
editorStyle lipgloss.Style
pickerStyle lipgloss.Style
table table.Model
editor textarea.Model
picker list.Model
}
var (
defaultStyle = lipgloss.NewStyle().
Align(lipgloss.Center).
BorderStyle(lipgloss.NormalBorder())
)
type item struct {
title, desc string
}
func (i item) Title() string { return i.title }
func (i item) Description() string { return i.desc }
func (i item) FilterValue() string { return i.title }
func newMainModerl() mainModel {
ed := textarea.New()
ed.Placeholder = "Try \"SELECT * FROM ?;\""
ed.ShowLineNumbers = false
ed.Focus()
columns := []table.Column{
{Title: "id", Width: 4},
{Title: "name", Width: 4},
{Title: "family_name", Width: 4},
}
rows := []table.Row{
{"0", "Conrad", "Adenauer"},
{"1", "Anna", "Aachen"},
{"2", "Karli", "Columbus"},
{"3", "Max", "Mustermann"},
{"4", "Bernd", "Brot"},
}
ta := table.New(
table.WithColumns(columns),
table.WithRows(rows),
)
items := []list.Item{
item{
title: "user",
desc: "users table",
},
item{
title: "job",
desc: "jobs table",
},
item{
title: "user_has_job",
desc: "user has a job",
},
}
li := list.New(items, list.NewDefaultDelegate(), 0, 0)
li.Title = "Table Picker"
return mainModel{
focused: 0,
viewStyle: defaultStyle,
editorStyle: defaultStyle,
pickerStyle: defaultStyle.BorderStyle(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color("67")),
editor: ed,
table: ta,
picker: li,
}
}
func (m mainModel) Init() tea.Cmd {
return nil
}
func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var (
edCmd tea.Cmd
taCmd tea.Cmd
liCmd tea.Cmd
)
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
case "tab":
if m.table.Focused() {
m.table.Blur()
m.editor.Focus()
} else {
m.editor.Blur()
m.table.Focus()
}
case "ctrl+e":
if m.focused == 1 {
m.focused = 0
} else {
m.focused = 1
}
}
case tea.WindowSizeMsg:
m = m.updateStyles(msg.Width, msg.Height)
}
m.editor, edCmd = m.editor.Update(msg)
m.table, taCmd = m.table.Update(msg)
if m.focused == 1 {
m.picker, liCmd = m.picker.Update(msg)
}
return m, tea.Batch(edCmd, taCmd, liCmd)
}
func (m mainModel) updateStyles(width, height int) mainModel {
h, v := defaultStyle.GetFrameSize()
topHeight := (height * 3 / 4) - h
editorHeight := (height * 1 / 4) - h
m.editorStyle = defaultStyle.
Width(width - v).
Height(editorHeight)
m.viewStyle = defaultStyle.
Width(width - v).
Height(topHeight)
m.editor.SetWidth(m.editorStyle.GetWidth())
m.editor.SetHeight(m.editorStyle.GetHeight())
m.table.SetWidth(m.viewStyle.GetWidth())
m.table.SetHeight(m.viewStyle.GetHeight())
columns := m.table.Columns()
colLen := len(columns)
colWidth := m.table.Width() / colLen
for i := range columns {
columns[i].Width = colWidth
}
m.table.SetColumns(columns)
m.pickerStyle = m.pickerStyle.
Width((width * 7 / 10) - v).
Height((height * 7 / 10) - h)
m.picker.SetSize(m.pickerStyle.GetWidth(), m.pickerStyle.GetHeight())
m.width = width
m.height = height
return m
}
func (m mainModel) View() string {
view := m.viewStyle.
Render(m.table.View())
editor := m.editorStyle.
Render(m.editor.View())
main := lipgloss.JoinVertical(lipgloss.Top, view, editor)
_ = main
if m.focused == 1 {
x := (m.width / 2) - m.picker.Width()/2
y := (m.height / 2) - m.picker.Height()/2
return PlaceOverlay(x, y, m.pickerStyle.Render(m.picker.View()), main, false)
}
return main
}
func main() {
p := tea.NewProgram(newMainModerl(), tea.WithAltScreen())
if _, err := p.Run(); err != nil {
log.Fatalf("could not start program: %v\n", err)
}
}