feat(server+cli): configurable menu via --menu JSON; UI tweaks to form layout

This commit is contained in:
Syahdan 2025-10-16 18:10:17 +07:00
parent c93b38e8ec
commit e998fbd230
2 changed files with 27 additions and 17 deletions

26
main.go
View File

@ -12,7 +12,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh" "github.com/charmbracelet/huh"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
@ -427,7 +426,7 @@ func (m model) View() string {
var leftCol string var leftCol string
if m.form != nil { if m.form != nil {
formView := m.form.View() formView := m.form.WithHeight(m.height - 10).View()
leftCol = lipgloss.NewStyle(). leftCol = lipgloss.NewStyle().
Width(m.width/2 - 2). Width(m.width/2 - 2).
Height(m.height - 6). Height(m.height - 6).
@ -465,12 +464,6 @@ func (m *model) buildForm() *huh.Form {
m.formFields.quantityStr = "" m.formFields.quantityStr = ""
m.formFields.confirm = false m.formFields.confirm = false
keymap := huh.NewDefaultKeyMap()
keymap.Quit = key.NewBinding(
key.WithKeys("ctrl+c", "esc"),
key.WithHelp("ctrl+c/esc", "quit"),
)
f := huh.NewForm( f := huh.NewForm(
huh.NewGroup( huh.NewGroup(
huh.NewInput(). huh.NewInput().
@ -494,6 +487,8 @@ func (m *model) buildForm() *huh.Form {
} }
return nil return nil
}), }),
),
huh.NewGroup(
huh.NewInput(). huh.NewInput().
Title("Quantity"). Title("Quantity").
Prompt("> "). Prompt("> ").
@ -512,7 +507,7 @@ func (m *model) buildForm() *huh.Form {
Negative("No"). Negative("No").
Value(&m.formFields.confirm), Value(&m.formFields.confirm),
), ),
).WithTheme(huh.ThemeBase()).WithKeyMap(keymap) ).WithTheme(huh.ThemeBase())
return f return f
} }
@ -652,20 +647,27 @@ func main() {
var ( var (
host string host string
serverOnly bool serverOnly bool
menuJSON string
) )
flag.StringVar(&host, "host", "localhost:9000", "host:port to connect to or bind the server on") flag.StringVar(&host, "host", "localhost:9000", "host:port to connect to or bind the server on")
flag.BoolVar(&serverOnly, "server", false, "run only the server") flag.BoolVar(&serverOnly, "server", false, "run only the server")
flag.StringVar(&menuJSON, "menu", "", "JSON array of menu items (server mode only), e.g. '[{\"id\":\"tea\",\"name\":\"Green Tea\",\"price\":2.5}]'")
flag.Parse() flag.Parse()
// If requested, start the TCP server (chat server as-is).
if serverOnly { if serverOnly {
if err := startTCPServer(host); err != nil { var menu []menuItem
if menuJSON != "" {
if err := json.Unmarshal([]byte(menuJSON), &menu); err != nil {
fmt.Printf("Invalid menu JSON: %v\n", err)
return
}
}
if err := startTCPServer(host, menu); err != nil {
fmt.Println("Server error:", err) fmt.Println("Server error:", err)
} }
return return
} }
// Client TUI
m := initialModel(host) m := initialModel(host)
p := tea.NewProgram(m, tea.WithAltScreen()) p := tea.NewProgram(m, tea.WithAltScreen())
if _, err := p.Run(); err != nil { if _, err := p.Run(); err != nil {

View File

@ -19,6 +19,8 @@ var defaultMenu = []menuItem{
{ID: "esp", Name: "Espresso", Price: 3.00}, {ID: "esp", Name: "Espresso", Price: 3.00},
} }
var serverMenu []menuItem
// order is the structure the server expects for ORDER. // order is the structure the server expects for ORDER.
type order struct { type order struct {
Name string `json:"name"` Name string `json:"name"`
@ -147,7 +149,7 @@ func handleConn(h *Hub, c net.Conn) {
// New protocol commands: // New protocol commands:
// MENU -> server returns single-line JSON array of menuItem // MENU -> server returns single-line JSON array of menuItem
if strings.EqualFold(line, "MENU") { if strings.EqualFold(line, "MENU") {
b, err := json.Marshal(defaultMenu) b, err := json.Marshal(serverMenu)
if err != nil { if err != nil {
fmt.Fprintln(c, `[error] failed to encode menu`) fmt.Fprintln(c, `[error] failed to encode menu`)
continue continue
@ -191,9 +193,9 @@ func handleConn(h *Hub, c net.Conn) {
continue continue
} }
var chosen *menuItem var chosen *menuItem
for i := range defaultMenu { for i := range serverMenu {
if defaultMenu[i].ID == ord.ItemID { if serverMenu[i].ID == ord.ItemID {
chosen = &defaultMenu[i] chosen = &serverMenu[i]
break break
} }
} }
@ -248,12 +250,18 @@ func handleConn(h *Hub, c net.Conn) {
} }
// startTCPServer starts a TCP chat server and never returns unless an error occurs. // startTCPServer starts a TCP chat server and never returns unless an error occurs.
func startTCPServer(addr string) error { func startTCPServer(addr string, menu []menuItem) error {
if len(menu) == 0 {
menu = defaultMenu
}
serverMenu = menu
ln, err := net.Listen("tcp", addr) ln, err := net.Listen("tcp", addr)
if err != nil { if err != nil {
return err return err
} }
log.Printf("TCP chat server listening on %s", ln.Addr()) log.Printf("TCP chat server listening on %s", ln.Addr())
log.Printf("Menu items: %d", len(serverMenu))
hub := NewHub() hub := NewHub()
go hub.Run() go hub.Run()