nanite/main.go
2025-10-11 20:20:36 -03:00

167 lines
2.9 KiB
Go

package main
import (
"bufio"
"context"
"errors"
"fmt"
"math"
"net"
"os"
"sync"
"time"
"codeberg.org/lobo/nanite/widgets/pager"
"git.sr.ht/~rockorager/vaxis"
"git.sr.ht/~rockorager/vaxis/widgets/textinput"
)
var (
ErrAlreadyConnected = errors.New("already connected")
)
type App struct {
ctx context.Context
stop context.CancelFunc
mu sync.RWMutex
conn net.Conn
scanner *bufio.Scanner
host, port string
nick string
last int
ticker *time.Ticker
incoming chan IncomingEvent
outgoing chan OutgoingEvent
vx *vaxis.Vaxis
w struct {
log, title, status, input vaxis.Window
}
pager *pager.Model
input *textinput.Model
dirty bool
}
func (app *App) Connect(host, port string) (err error) {
if app.conn != nil {
return ErrAlreadyConnected
}
app.host = host
app.port = port
app.conn, err = net.Dial("tcp", net.JoinHostPort(host, port))
if err != nil {
return err
}
app.scanner = bufio.NewScanner(app.conn)
app.incoming = make(chan IncomingEvent)
app.outgoing = make(chan OutgoingEvent)
app.ticker = time.NewTicker(2 * time.Second)
go func() {
for {
select {
case ev := <-app.outgoing:
ev.HandleOutgoing(app)
case <-app.ctx.Done():
return
}
}
}()
return nil
}
func (app *App) Disconnect() {
if app.conn != nil {
app.conn.Write([]byte("QUIT\n"))
app.conn.Close()
app.conn = nil
}
}
func (app *App) AppendMessage(data string) {
app.pager.Segments = append(app.pager.Segments,
vaxis.Segment{Text: data},
vaxis.Segment{Text: "\n"},
)
app.last += 1
app.pager.Offset = math.MaxInt
app.dirty = true
}
func (app *App) HandleTerminalEvent(ev vaxis.Event) {
app.dirty = true
switch ev := ev.(type) {
case vaxis.Resize:
app.resize()
case vaxis.Key:
if ev.MatchString("ctrl+c") {
app.stop()
}
switch {
case ev.MatchString("up"):
app.pager.ScrollUp()
case ev.MatchString("down"):
app.pager.ScrollDown()
case ev.MatchString("ctrl+l"):
app.Redraw()
app.vx.Refresh()
app.dirty = false
case ev.MatchString("enter"):
if len(app.input.Characters()) != 0 {
message := fmt.Sprintf("%s: %s", app.nick, app.input.String())
app.AppendMessage(message)
app.outgoing <- Message(message)
app.input.SetContent("")
}
}
}
app.input.Update(ev)
}
func main() {
args := os.Args
if len(args) < 2 || len(args) > 3 {
fmt.Printf("usage: %s host [port]\n", args[0])
os.Exit(1)
}
port := "44322"
if len(args) == 3 {
port = args[2]
}
app := App{}
app.ctx, app.stop = context.WithCancel(context.Background())
defer app.stop()
if err := app.Connect(args[1], port); err != nil {
panic(err)
}
defer app.Disconnect()
app.InitUI()
defer app.FinishUI()
app.nick = "wolfdog"
go app.Last(20)
for {
select {
case ev := <-app.vx.Events():
app.HandleTerminalEvent(ev)
case ev := <-app.incoming:
ev.HandleIncoming(&app)
case <-app.ticker.C:
app.outgoing <- Poll(app.last)
case <-app.ctx.Done():
return
}
app.Redraw()
}
}