Compare commits

..

3 Commits

Author SHA1 Message Date
Pablu
b0f7d74adc looking good so far 2026-01-20 00:39:20 +01:00
Pablu
3dd1cfef55 Update to use lipgloss table, and simplify code significantly 2026-01-19 23:54:33 +01:00
Pablu
555ca1f6ab Remove debug.log 2026-01-19 22:50:00 +01:00
3 changed files with 97 additions and 231 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
db.* db.*
main main
debug.log

View File

@@ -1,67 +0,0 @@
2026/01/19 11:46:36 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:46:36 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:46:53 DEBU Enter was pressed
2026/01/19 11:46:53 DEBU Selected item okay and batched
2026/01/19 11:47:01 DEBU Enter was pressed
2026/01/19 11:47:01 DEBU Selected item okay and batched
2026/01/19 11:47:03 DEBU Enter was pressed
2026/01/19 11:47:03 DEBU Selected item okay and batched
2026/01/19 11:47:08 DEBU Enter was pressed
2026/01/19 11:47:08 DEBU Selected item okay and batched
2026/01/19 11:47:21 DEBU Enter was pressed
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:04 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:05 DEBU Update styles bh=2 bv=2 h=2 v=2
2026/01/19 11:50:09 DEBU Enter was pressed
2026/01/19 11:50:09 DEBU Selected item okay and batched
2026/01/19 11:50:10 DEBU Enter was pressed
2026/01/19 11:50:10 DEBU Selected item okay and batched
2026/01/19 11:50:11 DEBU Enter was pressed
2026/01/19 11:50:11 DEBU Selected item okay and batched
2026/01/19 11:50:12 DEBU Enter was pressed
2026/01/19 11:50:12 DEBU Selected item okay and batched
2026/01/19 11:50:13 DEBU Enter was pressed
2026/01/19 11:50:13 DEBU Selected item okay and batched
2026/01/19 11:50:15 DEBU Enter was pressed
2026/01/19 11:50:15 DEBU Selected item okay and batched
2026/01/19 11:50:17 DEBU Enter was pressed
2026/01/19 11:50:17 DEBU Selected item okay and batched
2026/01/19 11:50:21 DEBU Enter was pressed
2026/01/19 11:50:21 DEBU Selected item okay and batched
2026/01/19 11:50:22 DEBU Enter was pressed
2026/01/19 11:50:22 DEBU Selected item okay and batched

View File

@@ -2,31 +2,35 @@ package main
import ( import (
"fmt" "fmt"
"math"
"os" "os"
engine "git.pablu.de/pablu/sqv-engine" engine "git.pablu.de/pablu/sqv-engine"
"github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/table"
"github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/textarea"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/lipgloss/table"
"github.com/charmbracelet/log" "github.com/charmbracelet/log"
) )
type Focused int
const (
EDITOR Focused = iota
TABLE
PICKER
)
type mainModel struct { type mainModel struct {
width, height int width, height int
pickerFocused bool
manager *engine.Manager manager *engine.Manager
lastFocused int lastFocused, focused Focused
viewStyle lipgloss.Style table *table.Table
editorStyle lipgloss.Style
pickerStyle lipgloss.Style
table table.Model
editor textarea.Model editor textarea.Model
picker list.Model picker list.Model
@@ -39,9 +43,6 @@ var (
BorderStyle(lipgloss.NormalBorder()) BorderStyle(lipgloss.NormalBorder())
focusedStyle = defaultStyle.BorderForeground(lipgloss.Color("202")) focusedStyle = defaultStyle.BorderForeground(lipgloss.Color("202"))
baseTableStyle = lipgloss.NewStyle().
BorderStyle(lipgloss.NormalBorder())
) )
type item struct { type item struct {
@@ -57,54 +58,17 @@ func newMainModerl(manager *engine.Manager) mainModel {
ed.Placeholder = "Try \"SELECT * FROM ?;\"" ed.Placeholder = "Try \"SELECT * FROM ?;\""
ed.ShowLineNumbers = false ed.ShowLineNumbers = false
ed.Focus()
columns := []table.Column{ ta := table.New().Border(lipgloss.NormalBorder())
{Title: "id", Width: 4},
{Title: "name", Width: 4},
{Title: "family_name", Width: 4},
}
rows := []table.Row{ li := list.New(nil, list.NewDefaultDelegate(), 0, 0)
{"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" li.Title = "Table Picker"
return mainModel{ return mainModel{
pickerFocused: false, editor: ed,
viewStyle: baseTableStyle, table: ta,
editorStyle: defaultStyle, picker: li,
pickerStyle: defaultStyle.BorderStyle(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color("67")), manager: manager,
editor: ed,
table: ta,
picker: li,
manager: manager,
} }
} }
@@ -146,38 +110,16 @@ func (m mainModel) GetTable(name string) tea.Cmd {
m.manager.LoadTable(&t) m.manager.LoadTable(&t)
msg := tableMsg{ msg := tableMsg{
Columns: make([]table.Column, len(t.Columns)), Columns: make([]string, len(t.Columns)),
Rows: make([]table.Row, len(t.Rows)), Rows: make([][]string, len(t.Rows)),
} }
lRow := make([]int, len(t.Columns))
lRowC := 0
for i, r := range t.Rows { for i, r := range t.Rows {
msg.Rows[i] = r.Values msg.Rows[i] = r.Values
// Pure stupid estemation of how long each row should be
if i == 0 {
for j, v := range r.Values {
vLen := len(v)
vLen = max(vLen, len(t.Columns[j].Name))
lRow[j] = vLen
lRowC += vLen
}
}
} }
h, _ := m.viewStyle.GetFrameSize()
width := m.viewStyle.GetWidth() - h
width -= (len(t.Columns) + 1) * 2
for i, c := range t.Columns { for i, c := range t.Columns {
columnWidth := float64(width) * (float64(lRow[i]) / float64(lRowC)) msg.Columns[i] = c.Name
msg.Columns[i] = table.Column{
Title: c.Name,
Width: int(columnWidth),
}
} }
return msg return msg
@@ -185,15 +127,13 @@ func (m mainModel) GetTable(name string) tea.Cmd {
} }
type tableMsg struct { type tableMsg struct {
Columns []table.Column Columns []string
Rows []table.Row Rows [][]string
} }
func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var ( var (
edCmd tea.Cmd edCmd tea.Cmd
taCmd tea.Cmd
liCmd tea.Cmd
pickerCmd tea.Cmd pickerCmd tea.Cmd
cmds tea.Cmd cmds tea.Cmd
) )
@@ -204,43 +144,35 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case "ctrl+c": case "ctrl+c":
return m, tea.Quit return m, tea.Quit
case "tab": case "tab":
if m.table.Focused() { if m.focused == EDITOR {
m.table.Blur() m.focused = TABLE
m.editor.Focus()
m.lastFocused = 0
} else {
m.editor.Blur() m.editor.Blur()
m.table.Focus() } else {
m.lastFocused = 1 m.focused = EDITOR
} }
case "j":
m.table.Offset(50)
case "ctrl+e": case "ctrl+e":
m.pickerFocused = !m.pickerFocused if m.focused == PICKER {
m.focused = m.lastFocused
// Picker is now focused
if m.pickerFocused {
m.table.Blur()
m.editor.Blur()
} else { } else {
if m.lastFocused == 0 { // Maybe blur is not even needed
m.editor.Focus() m.editor.Blur()
} else { m.lastFocused = m.focused
m.table.Focus() m.focused = PICKER
}
} }
case "enter": case "enter":
log.Debug("Enter was pressed") log.Debug("Enter was pressed")
if m.pickerFocused { if m.focused == PICKER {
i, ok := m.picker.SelectedItem().(item) i, ok := m.picker.SelectedItem().(item)
if ok { if ok {
log.Debug("Selected item okay and batched") log.Debug("Selected item okay and batched")
cmds = tea.Batch(cmds, m.GetTable(i.title)) cmds = tea.Batch(cmds, m.GetTable(i.title))
} }
m.pickerFocused = !m.pickerFocused m.focused = m.lastFocused
if m.lastFocused == 0 { if m.focused == EDITOR {
m.editor.Focus() m.editor.Focus()
} else {
m.table.Focus()
} }
} }
} }
@@ -249,87 +181,85 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = tea.Batch(cmds, m.GetFirstTable) cmds = tea.Batch(cmds, m.GetFirstTable)
case tableMsg: case tableMsg:
// log.Debug("Setting new table view", "columns", msg.Columns, "rows", msg.Rows) m.table.Offset(0)
m.table.ClearRows()
m.table = table.New( m.table.Headers(msg.Columns...)
table.WithColumns(msg.Columns), m.table.Rows(msg.Rows...)
table.WithRows(msg.Rows),
)
m.table.SetWidth(m.viewStyle.GetWidth() - 2)
m.table.SetHeight(m.viewStyle.GetHeight() - 2)
// m.table.SetColumns(msg.Columns)
// m.table.SetRows(msg.Rows)
case tableDefinitionsMsg: case tableDefinitionsMsg:
pickerCmd = m.picker.SetItems(msg.Tables) pickerCmd = m.picker.SetItems(msg.Tables)
} }
m.editor, edCmd = m.editor.Update(msg) switch m.focused {
m.table, taCmd = m.table.Update(msg) case EDITOR:
if m.pickerFocused { m.editor.Focus()
m.picker, liCmd = m.picker.Update(msg) m.editor, edCmd = m.editor.Update(msg)
case PICKER:
m.picker, pickerCmd = m.picker.Update(msg)
} }
return m, tea.Batch(edCmd, taCmd, liCmd, cmds, pickerCmd) return m, tea.Batch(edCmd, cmds, pickerCmd)
} }
func (m mainModel) updateStyles(width, height int) mainModel { func (m mainModel) updateStyles(width, height int) mainModel {
h, v := defaultStyle.GetFrameSize()
bh, bv := baseTableStyle.GetFrameSize()
log.Debug("Update styles", "bh", bh, "bv", bv, "h", h, "v", v)
topHeight := (height * 3 / 4) - bh
editorHeight := (height * 1 / 4) - h
m.editorStyle = defaultStyle.
Width(width - v).
Height(editorHeight)
m.viewStyle = baseTableStyle.
Width(width - bv).
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.width = width
m.height = height m.height = height
h, v := defaultStyle.GetFrameSize()
topHeight := math.Ceil(float64(m.height)*3/4 - float64(h))
editorHeight := math.Ceil(float64(m.height)*1/4 - float64(h))
m.editor.SetWidth(m.width - v)
m.editor.SetHeight(int(editorHeight))
m.table.Width(m.width - v)
m.table.Height(int(topHeight) - h*2)
m.picker.SetSize(m.width*7/10, m.height*7/10)
return m return m
} }
func (m mainModel) View() string { func (m mainModel) View() string {
view := m.viewStyle. var (
Render(m.table.View()) view, editor string
)
editor := m.editorStyle. h, _ := defaultStyle.GetFrameSize()
Render(m.editor.View()) topHeight := (m.height * 3 / 4) - h
editorHeight := (m.height * 1 / 4) - h
switch m.focused {
case EDITOR:
view = defaultStyle.
Height(topHeight).
Render(m.table.Render())
editor = focusedStyle.
Height(editorHeight).
Render(m.editor.View())
case TABLE:
view = focusedStyle.
Height(topHeight).
Render(m.table.Render())
editor = defaultStyle.
Height(editorHeight).
Render(m.editor.View())
case PICKER:
view = defaultStyle.
Height(topHeight).
Render(m.table.Render())
editor = defaultStyle.
Height(editorHeight).
Render(m.editor.View())
}
main := lipgloss.JoinVertical(lipgloss.Top, view, editor) main := lipgloss.JoinVertical(lipgloss.Top, view, editor)
if m.pickerFocused { if m.focused == PICKER {
x := (m.width / 2) - m.picker.Width()/2 x := (m.width / 2) - m.picker.Width()/2
y := (m.height / 2) - m.picker.Height()/2 y := (m.height / 2) - m.picker.Height()/2
return PlaceOverlay(x, y, m.pickerStyle.Render(m.picker.View()), main, false) return PlaceOverlay(x, y, focusedStyle.Render(m.picker.View()), main, false)
} }
return main return main
@@ -358,6 +288,8 @@ func main() {
os.Exit(1) os.Exit(1)
} }
table.New()
p := tea.NewProgram(newMainModerl(m), tea.WithAltScreen()) p := tea.NewProgram(newMainModerl(m), tea.WithAltScreen())
if _, err := p.Run(); err != nil { if _, err := p.Run(); err != nil {
fmt.Println("fatal:", err) fmt.Println("fatal:", err)