feat: wip tui application
This commit is contained in:
parent
a161b86c9a
commit
5d7e9d79c4
7 changed files with 286 additions and 0 deletions
40
main.go
Normal file
40
main.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"sourcery.dny.nu/pana"
|
||||||
|
"github.com/alexflint/go-arg"
|
||||||
|
"github.com/cato-001/twink/tui"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
|
||||||
|
|
||||||
|
var args struct{
|
||||||
|
Auth *struct{
|
||||||
|
} `arg:"subcommand:auth"`
|
||||||
|
|
||||||
|
Tui *tui.Args `arg:"subcommand:tui"`
|
||||||
|
}
|
||||||
|
|
||||||
|
parser, err := arg.NewParser(arg.Config{}, &args)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("could not create cli argument parser", slog.Any("error", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.MustParse(os.Args[1:])
|
||||||
|
|
||||||
|
processor := pana.NewProcessor(logger)
|
||||||
|
_ = processor
|
||||||
|
|
||||||
|
if args.Tui != nil {
|
||||||
|
err := tui.Start(*args.Tui)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("app exited unexpectedly", slog.Any("error", err))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
47
tui/app.go
Normal file
47
tui/app.go
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
AppModel struct{
|
||||||
|
Size Size
|
||||||
|
Header HeaderModel
|
||||||
|
Screen tea.Model
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewApp() AppModel {
|
||||||
|
return AppModel{
|
||||||
|
Header: NewHeader(),
|
||||||
|
Screen: NewHomeScreen(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AppModel) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AppModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
var cmd, headerCmd, screenCmd tea.Cmd
|
||||||
|
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case tea.WindowSizeMsg:
|
||||||
|
m.Size = NewSizeFromWindow(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Header, headerCmd = m.Header.Update(msg)
|
||||||
|
m.Screen, screenCmd = m.Screen.Update(msg)
|
||||||
|
|
||||||
|
return m, tea.Batch(cmd, headerCmd, screenCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m AppModel) View() string {
|
||||||
|
return strings.Join([]string{
|
||||||
|
m.Header.View(),
|
||||||
|
m.Screen.View(),
|
||||||
|
}, "\n")
|
||||||
|
}
|
||||||
7
tui/args.go
Normal file
7
tui/args.go
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
type (
|
||||||
|
Args struct{
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
114
tui/header.go
Normal file
114
tui/header.go
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
HeaderModel struct{
|
||||||
|
Tabs []HeaderTabModel
|
||||||
|
Width int
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderTabModel struct{
|
||||||
|
Current bool
|
||||||
|
Name string
|
||||||
|
Unread int
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewHeader() HeaderModel {
|
||||||
|
return HeaderModel{
|
||||||
|
Tabs: []HeaderTabModel {
|
||||||
|
NewHeaderTab("Home", 10, true),
|
||||||
|
NewHeaderTab("Notifications", 3, false),
|
||||||
|
NewHeaderTab("Lists", 5, false),
|
||||||
|
NewHeaderTab("Private", 0, false),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHeaderTab(name string, unread int, current bool) HeaderTabModel {
|
||||||
|
return HeaderTabModel{ Name: name, Unread: unread, Current: current }
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
activeTabBorder = lipgloss.Border{
|
||||||
|
Top: "─",
|
||||||
|
Bottom: " ",
|
||||||
|
Left: "│",
|
||||||
|
Right: "│",
|
||||||
|
TopLeft: "╭",
|
||||||
|
TopRight: "╮",
|
||||||
|
BottomLeft: "┘",
|
||||||
|
BottomRight: "└",
|
||||||
|
}
|
||||||
|
|
||||||
|
tabBorder = lipgloss.Border{
|
||||||
|
Top: "─",
|
||||||
|
Bottom: "─",
|
||||||
|
Left: "│",
|
||||||
|
Right: "│",
|
||||||
|
TopLeft: "╭",
|
||||||
|
TopRight: "╮",
|
||||||
|
BottomLeft: "┴",
|
||||||
|
BottomRight: "┴",
|
||||||
|
}
|
||||||
|
|
||||||
|
headerTabStyle = lipgloss.NewStyle().
|
||||||
|
Border(tabBorder, true)
|
||||||
|
|
||||||
|
activeHeaderTabStyle = headerTabStyle.
|
||||||
|
Border(activeTabBorder, true)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m HeaderModel) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HeaderModel) Update(msg tea.Msg) (HeaderModel, tea.Cmd) {
|
||||||
|
var cmd tea.Cmd
|
||||||
|
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case Size:
|
||||||
|
m.Width = msg.Width
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HeaderModel) View() string {
|
||||||
|
header := lipgloss.NewStyle().
|
||||||
|
Padding(1, 0).
|
||||||
|
Align(lipgloss.Center).
|
||||||
|
Width(m.Width)
|
||||||
|
|
||||||
|
renderedTabs := make([]string, len(m.Tabs))
|
||||||
|
for i, tab := range m.Tabs {
|
||||||
|
renderedTabs[i] = tab.View()
|
||||||
|
}
|
||||||
|
|
||||||
|
return header.Render(renderedTabs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HeaderTabModel) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HeaderTabModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
var cmd tea.Cmd
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HeaderTabModel) View() string {
|
||||||
|
content := lipgloss.JoinHorizontal(lipgloss.Center, m.Name, fmt.Sprintf("(%d)", m.Unread))
|
||||||
|
|
||||||
|
if m.Current {
|
||||||
|
return lipgloss.NewStyle().Underline(true).Render(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
||||||
|
}
|
||||||
53
tui/home.go
Normal file
53
tui/home.go
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
HomeScreenModel struct{
|
||||||
|
Size Size
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewHomeScreen() HomeScreenModel {
|
||||||
|
return HomeScreenModel{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HomeScreenModel) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HomeScreenModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
var cmd tea.Cmd
|
||||||
|
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case tea.KeyMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
case "q", "ctrl-c":
|
||||||
|
cmd = tea.Quit
|
||||||
|
}
|
||||||
|
case Size:
|
||||||
|
m.Size = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m HomeScreenModel) View() string {
|
||||||
|
header := lipgloss.NewStyle().
|
||||||
|
Align(lipgloss.Center).
|
||||||
|
Width(m.Size.Width).
|
||||||
|
SetString(fmt.Sprintf("Home\n%s", "@example@mastodon.social"))
|
||||||
|
|
||||||
|
window := lipgloss.NewStyle().
|
||||||
|
Width(m.Size.Width).
|
||||||
|
Height(m.Size.Height)
|
||||||
|
|
||||||
|
return window.Render(
|
||||||
|
header.Render(),
|
||||||
|
)
|
||||||
|
}
|
||||||
15
tui/size.go
Normal file
15
tui/size.go
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import tea "github.com/charmbracelet/bubbletea"
|
||||||
|
|
||||||
|
type Size struct{
|
||||||
|
Width, Height int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSize(width, height int) Size {
|
||||||
|
return Size { Width: width, Height: height }
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSizeFromWindow(size tea.WindowSizeMsg) Size {
|
||||||
|
return Size { Width: size.Width, Height: size.Height }
|
||||||
|
}
|
||||||
10
tui/start.go
Normal file
10
tui/start.go
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import tea "github.com/charmbracelet/bubbletea"
|
||||||
|
|
||||||
|
func Start(args Args) error {
|
||||||
|
app := NewApp()
|
||||||
|
program := tea.NewProgram(app, tea.WithAltScreen())
|
||||||
|
_, err := program.Run()
|
||||||
|
return err
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue