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