package main import ( "fmt" "os" "strings" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) // --- Data Structures --- type Brew struct { Name string Hops string Progress int Notes string } type InventoryItem struct { Name string Type string Amount float64 Unit string } func main() { app := tview.NewApplication() pages := tview.NewPages() // Colors - Using GetColor for maximum compatibility cyan := tcell.GetColor("cyan") yellow := tcell.GetColor("yellow") green := tcell.GetColor("green") gray := tcell.GetColor("gray") // --- Mock Data --- brews := []Brew{ {"Midnight Stout", "Fuggles, Goldings", 75, "Primary fermentation looking good. Smells like coffee."}, {"Neon IPA", "Citra, Mosaic, Galaxy", 40, "Dry hopping starts in 2 days."}, {"Summer Wheat", "Saaz, Hallertau", 95, "Ready for bottling/kegging."}, } inventory := []InventoryItem{ {"Citra", "Hops", 500, "g"}, {"Pilsner Malt", "Grain", 25, "kg"}, {"US-05 SafAle", "Yeast", 4, "packets"}, } // --- Shared UI Components --- logo := tview.NewTextView(). SetTextAlign(tview.AlignRight). SetDynamicColors(true). SetText(`[yellow] [##] ___ ___ ___ _ _ /____\ | _ )| _ \| __| | | | | U | | _ \| /| _|| |/\| | |______| |___/|_|_\|___||__/\__| |'=__='| | | __ __ _ ___ _____ ___ ___ |______| | \/ | /_\ / __|_ _| __| _ \ | |\/| |/ _ \\__ \ | | | _|| / |_| |_/_/ \_\___/ |_| |___|_|_\ `) viewTitle := tview.NewTextView().SetTextColor(cyan) header := tview.NewFlex(). AddItem(viewTitle, 0, 1, false). AddItem(logo, 0, 2, false) commandBar := tview.NewInputField(). SetLabelColor(yellow). SetFieldBackgroundColor(tcell.ColorBlack). SetFieldTextColor(tcell.ColorWhite) footer := tview.NewTextView(). SetDynamicColors(true). SetText(" [black:gray] <:> Command [white:black] [black:gray] Describe [white:black] [black:gray] Exit [white:black] ") // --- View Logic Helpers --- // Detail View (The "Describe" screen) detailsView := tview.NewTextView(). SetDynamicColors(true). SetRegions(true). SetWrap(true) showDetails := func(content string) { detailsView.SetText(content) pages.SwitchToPage("details") } // --- Page 1: Active Brews --- brewsTable := tview.NewTable().SetSelectable(true, false) for r, b := range brews { brewsTable.SetCell(r, 0, tview.NewTableCell(b.Name).SetTextColor(tcell.ColorWhite)) brewsTable.SetCell(r, 1, tview.NewTableCell(b.Hops).SetTextColor(gray)) brewsTable.SetCell(r, 2, tview.NewTableCell(fmt.Sprintf("%d%%", b.Progress)).SetTextColor(green)) } brewsTable.SetSelectedFunc(func(row, column int) { b := brews[row] detailText := fmt.Sprintf("\n [yellow]NAME:[-] %s\n [yellow]HOPS:[-] %s\n [yellow]PROGRESS:[-] %d%%\n\n [yellow]NOTES:[-]\n %s\n\n [gray]Press to return", b.Name, b.Hops, b.Progress, b.Notes) showDetails(detailText) }) // --- Page 2: Inventory --- invTable := tview.NewTable().SetSelectable(true, false) for r, item := range inventory { invTable.SetCell(r, 0, tview.NewTableCell(item.Name)) invTable.SetCell(r, 1, tview.NewTableCell(fmt.Sprintf("%.1f %s", item.Amount, item.Unit)).SetTextColor(green)) } invTable.SetSelectedFunc(func(row, column int) { item := inventory[row] detailText := fmt.Sprintf("\n [yellow]ITEM:[-] %s\n [yellow]TYPE:[-] %s\n [yellow]STOCK:[-] %.1f %s\n\n [gray]Inventory check passed. Ready for next brew.\n\n [gray]Press to return", item.Name, item.Type, item.Amount, item.Unit) showDetails(detailText) }) pages.AddPage("brews", brewsTable, true, true) pages.AddPage("inventory", invTable, true, false) pages.AddPage("details", detailsView, true, false) // --- Layout Setup --- mainLayout := tview.NewFlex().SetDirection(tview.FlexRow). AddItem(header, 9, 0, false). AddItem(pages, 0, 1, true). AddItem(footer, 1, 0, false) root := tview.NewFlex().SetDirection(tview.FlexRow). AddItem(mainLayout, 0, 1, true) // --- Global Navigation --- showCommandBar := func(label string) { if root.GetItemCount() == 1 { commandBar.SetLabel(label) root.AddItem(commandBar, 1, 0, false) app.SetFocus(commandBar) } } hideCommandBar := func() { if root.GetItemCount() > 1 { root.RemoveItem(commandBar) commandBar.SetText("") _, activePage := pages.GetFrontPage() app.SetFocus(activePage) } } switchTo := func(pageID string, title string) { viewTitle.SetText(" BrewMaster v0.1.0 - " + title) pages.SwitchToPage(pageID) _, active := pages.GetFrontPage() app.SetFocus(active) } switchTo("brews", "Active Brews") // --- Input Handling --- commandBar.SetDoneFunc(func(key tcell.Key) { if key == tcell.KeyEscape { hideCommandBar() return } if key == tcell.KeyEnter { cmd := strings.ToLower(strings.TrimSpace(commandBar.GetText())) switch cmd { case "q", "quit": app.Stop() case "i", "inv": switchTo("inventory", "Inventory") case "b", "brew": switchTo("brews", "Active Brews") } hideCommandBar() } }) app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if app.GetFocus() != commandBar { // ESC to go back from Details to whatever was previous if event.Key() == tcell.KeyEscape { currentPage, _ := pages.GetFrontPage() if currentPage == "details" { // Default back to brews for now switchTo("brews", "Active Brews") } } switch event.Rune() { case ':': showCommandBar(": ") return nil } } return event }) if err := app.SetRoot(root, true).Run(); err != nil { fmt.Printf("Error running app: %v\n", err) os.Exit(1) } }