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"
"time"
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh"
"github.com/charmbracelet/lipgloss"
@ -427,7 +426,7 @@ func (m model) View() string {
var leftCol string
if m.form != nil {
formView := m.form.View()
formView := m.form.WithHeight(m.height - 10).View()
leftCol = lipgloss.NewStyle().
Width(m.width/2 - 2).
Height(m.height - 6).
@ -465,12 +464,6 @@ func (m *model) buildForm() *huh.Form {
m.formFields.quantityStr = ""
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(
huh.NewGroup(
huh.NewInput().
@ -494,6 +487,8 @@ func (m *model) buildForm() *huh.Form {
}
return nil
}),
),
huh.NewGroup(
huh.NewInput().
Title("Quantity").
Prompt("> ").
@ -512,7 +507,7 @@ func (m *model) buildForm() *huh.Form {
Negative("No").
Value(&m.formFields.confirm),
),
).WithTheme(huh.ThemeBase()).WithKeyMap(keymap)
).WithTheme(huh.ThemeBase())
return f
}
@ -652,20 +647,27 @@ func main() {
var (
host string
serverOnly bool
menuJSON string
)
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.StringVar(&menuJSON, "menu", "", "JSON array of menu items (server mode only), e.g. '[{\"id\":\"tea\",\"name\":\"Green Tea\",\"price\":2.5}]'")
flag.Parse()
// If requested, start the TCP server (chat server as-is).
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)
}
return
}
// Client TUI
m := initialModel(host)
p := tea.NewProgram(m, tea.WithAltScreen())
if _, err := p.Run(); err != nil {

View File

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