updated the app to use tview because the bubbletea code was unmaintainable

This commit is contained in:
2026-01-29 06:06:21 -06:00
parent e5688ae361
commit 038bc3e88c
5 changed files with 204 additions and 357 deletions
+68 -34
View File
@@ -1,50 +1,84 @@
package main
import (
"fmt"
"context"
"log"
"os"
"os/signal"
"syscall"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
func main() {
// Setup logging to file
f, err := tea.LogToFile("debug.log", "debug")
if err != nil {
fmt.Println("fatal:", err)
os.Exit(1)
func MonitorDevice(ctx context.Context, app *tview.Application, frame **tview.Frame, device *Device) {
ticker := time.NewTicker(100 * time.Millisecond) // Check every 100ms
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
port, err := device.FindCardputer()
if err != nil {
if device.Connected {
device.Close()
UpdateDeviceStatus(app, *frame, "Cardputer disconnected", tcell.ColorRed)
}
} else {
if !device.Connected {
UpdateDeviceStatus(app, *frame, "Cardputer found: "+port, tcell.ColorWhite)
err = device.Connect()
if err != nil {
UpdateDeviceStatus(app, *frame, "Error connecting to cardputer: "+err.Error(), tcell.ColorRed)
} else {
UpdateDeviceStatus(app, *frame, "Connected to cardputer: "+port, tcell.ColorGreen)
}
} else {
UpdateDeviceStatus(app, *frame, "Connected to cardputer: "+port, tcell.ColorGreen)
}
}
}
}
defer f.Close()
}
func main() {
// Initialize Device manager
device := NewDevice()
// Initial search for device
// We do this synchronously at startup for convenience,
// but the UI could also handle re-scanning.
portName, err := device.FindCardputer()
// Initialize UI Model
m := InitialModel(device)
if err == nil {
m.Status = StatusFound
log.Printf("Found Cardputer at %s", portName)
} else {
log.Printf("Cardputer not found at startup: %v", err)
// We start in Searching state, user might plug it in later if we implemented auto-scan,
// but for now it just shows searching or error.
// Since our basic implementation does a one-time scan at start,
// if it fails, we might want to let the user retry or just show the state.
// The current InitialModel defaults to StatusSearching.
// To match original behavior (sort of), if we don't find it, we stay in Searching/Error.
// Actually, let's just leave it as StatusSearching or set Error if we want to be explicit.
}
// Create and run the Bubble Tea program
p := tea.NewProgram(m, tea.WithAltScreen())
if _, err := p.Run(); err != nil {
app := tview.NewApplication()
var currentFrame *tview.Frame
frame := AppUI(app, &currentFrame)
currentFrame = frame
app.SetRoot(frame, true)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Set up signal handling for graceful shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
// Handle signals in a goroutine
go func() {
<-sigChan
// Cancel context to stop monitoring
cancel()
// Disconnect device gracefully
if device.Connected {
log.Println("Disconnecting device...")
if err := device.Close(); err != nil {
log.Printf("Error disconnecting device: %v\n", err)
}
}
// Stop the application
app.Stop()
}()
go MonitorDevice(ctx, app, &currentFrame, device)
if err := app.Run(); err != nil {
log.Fatal("Error running program:", err)
}
}