diff --git a/main.go b/main.go index 2fe8085..a473fc6 100644 --- a/main.go +++ b/main.go @@ -3,100 +3,204 @@ package main import ( "fmt" "os" + "strings" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) -// Brew represents our mock data structure +// --- 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 --- - // 1. Mock Data brews := []Brew{ - {"Midnight Stout", "Fuggles, Goldings", 75}, - {"Neon IPA", "Citra, Mosaic, Galaxy", 40}, - {"Summer Wheat", "Saaz, Hallertau", 95}, - {"Pacific Pale Ale", "Cascade, Amarillo", 10}, + {"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."}, } - // 2. Header with ASCII Logo (Top Right) - // We use a TextView for the logo to allow right-alignment + 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] - ____ __ __ _ - | __ ) _ __ _____ __| \/ | __ _ ___| |_ ___ _ __ - | _ \| '__/ _ \ \ /\ / /| |\/| |/ _` + "`" + `/ __| __/ _ \ '__| - | |_) | | | __/\ V V / | | | | (_| \__ \ || __/ | - |____/|_| \___| \_/\_/ |_| |_|\__,_|___/\__\___|_| + [##] ___ ___ ___ _ _ + /____\ | _ )| _ \| __| | | | + | U | | _ \| /| _|| |/\| | + |______| |___/|_|_\|___||__/\__| + |'=__='| + | | __ __ _ ___ _____ ___ ___ + |______| | \/ | /_\ / __|_ _| __| _ \ + | |\/| |/ _ \\__ \ | | | _|| / + |_| |_/_/ \_\___/ |_| |___|_|_\ `) - title := tview.NewTextView(). - SetText(" BrewMaster v0.1.0 - Active Brews"). - SetTextColor(tcell.ColorCyan) + viewTitle := tview.NewTextView().SetTextColor(cyan) header := tview.NewFlex(). - AddItem(title, 0, 1, false). + AddItem(viewTitle, 0, 1, false). AddItem(logo, 0, 2, false) - // 3. Main Table (K9s Style) - table := tview.NewTable(). - SetBorders(false). - SetSelectable(true, false) + commandBar := tview.NewInputField(). + SetLabelColor(yellow). + SetFieldBackgroundColor(tcell.ColorBlack). + SetFieldTextColor(tcell.ColorWhite) - // Set Table Headers - headers := []string{"NAME", "HOPS", "PROGRESS", "STATUS"} - for c, h := range headers { - table.SetCell(0, c, tview.NewTableCell(fmt.Sprintf("[yellow::b]%s", h)). - SetSelectable(false)) - } - - // Populate Mock Data - for r, brew := range brews { - row := r + 1 - table.SetCell(row, 0, tview.NewTableCell(brew.Name).SetTextColor(tcell.ColorWhite)) - table.SetCell(row, 1, tview.NewTableCell(brew.Hops).SetTextColor(tcell.ColorGray)) - - // Progress bar style - progressStr := fmt.Sprintf("[%d%%] ", brew.Progress) - table.SetCell(row, 2, tview.NewTableCell(progressStr).SetTextColor(tcell.ColorGreen)) - - status := "Fermenting" - if brew.Progress > 90 { - status = "Conditioning" - } - table.SetCell(row, 3, tview.NewTableCell(status).SetTextColor(tcell.ColorLightBlue)) - } - - // 4. Footer (Help Menu) footer := tview.NewTextView(). SetDynamicColors(true). - SetText(" [black:gray] Exit [white:black] [black:gray] View Details [white:black] ") + SetText(" [black:gray] <:> Command [white:black] [black:gray] Describe [white:black] [black:gray] Exit [white:black] ") - // 5. Layout Assembly - grid := tview.NewFlex().SetDirection(tview.FlexRow). - AddItem(header, 7, 0, false). - AddItem(table, 0, 1, true). + // --- 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) - // Global Keybindings + 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 event.Key() == tcell.KeyCtrlC { - app.Stop() - os.Exit(0) + 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(grid, true).EnableMouse(true).Run(); err != nil { - panic(err) + if err := app.SetRoot(root, true).Run(); err != nil { + fmt.Printf("Error running app: %v\n", err) + os.Exit(1) } -} \ No newline at end of file +}