repos / pico

pico services mono repo
git clone https://github.com/picosh/pico.git

pico / pkg / tui
Eric Bower  ·  2025-12-20

menu.go

  1package tui
  2
  3import (
  4	"git.sr.ht/~rockorager/vaxis"
  5	"git.sr.ht/~rockorager/vaxis/vxfw"
  6	"git.sr.ht/~rockorager/vaxis/vxfw/list"
  7	"git.sr.ht/~rockorager/vaxis/vxfw/text"
  8	"github.com/picosh/pico/pkg/db"
  9)
 10
 11var menuChoices = []string{
 12	"pubkeys",
 13	"tokens",
 14	"logs",
 15	"access_logs",
 16	"analytics",
 17	"pages",
 18	"tuns",
 19	"pico+",
 20	"chat",
 21}
 22
 23type LoadedUsageStats struct{}
 24
 25type MenuPage struct {
 26	shared *SharedModel
 27
 28	list     list.Dynamic
 29	features *FeaturesList
 30	stats    *db.UserStats
 31}
 32
 33func getMenuWidget(i uint, cursor uint) vxfw.Widget {
 34	if int(i) >= len(menuChoices) {
 35		return nil
 36	}
 37	var style vaxis.Style
 38	if i == cursor {
 39		style.Attribute = vaxis.AttrReverse
 40	}
 41	content := menuChoices[i]
 42	return &text.Text{
 43		Content: content,
 44		Style:   style,
 45	}
 46}
 47
 48func NewMenuPage(shrd *SharedModel) *MenuPage {
 49	m := &MenuPage{shared: shrd}
 50	m.list = list.Dynamic{Builder: getMenuWidget, DrawCursor: true}
 51	m.features = NewFeaturesList(shrd)
 52	return m
 53}
 54
 55func loadChat(shrd *SharedModel) {
 56	sp := &SenpaiCmd{
 57		Shared: shrd,
 58	}
 59	_ = sp.Run()
 60}
 61
 62func (m *MenuPage) fetchUsageStats() error {
 63	stats, err := m.shared.Dbpool.FindUserStats(m.shared.User.ID)
 64	if err != nil {
 65		return err
 66	}
 67	m.stats = stats
 68	return nil
 69}
 70
 71func (m *MenuPage) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Command, error) {
 72	switch msg := ev.(type) {
 73	case PageIn:
 74		_ = m.fetchUsageStats()
 75		cmd, _ := m.features.HandleEvent(vxfw.Init{}, phase)
 76		return vxfw.BatchCmd([]vxfw.Command{
 77			cmd,
 78			vxfw.FocusWidgetCmd(&m.list),
 79		}), nil
 80	case vaxis.Key:
 81		if msg.Matches(vaxis.KeyEnter) {
 82			cursor := int(m.list.Cursor())
 83			if cursor >= len(menuChoices) {
 84				return nil, nil
 85			}
 86			choice := menuChoices[m.list.Cursor()]
 87			m.shared.App.PostEvent(Navigate{To: choice})
 88		}
 89	}
 90	return nil, nil
 91}
 92
 93func (m *MenuPage) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) {
 94	info, _ := NewUserInfo(m.shared).Draw(ctx)
 95
 96	brd := NewBorder(&m.list)
 97	brd.Label = "menu"
 98	brd.Style = vaxis.Style{Foreground: oj}
 99	menuSurf, _ := brd.Draw(vxfw.DrawContext{
100		Characters: ctx.Characters,
101		Max: vxfw.Size{
102			Width:  30,
103			Height: uint16(len(menuChoices)) + 3,
104		},
105	})
106
107	services, _ := NewServicesList(m.shared.PlusFeatureFlag).Draw(ctx)
108	features, _ := m.features.Draw(ctx)
109
110	leftPane := NewGroupStack([]vxfw.Surface{
111		menuSurf,
112		info,
113		services,
114	})
115	leftSurf, _ := leftPane.Draw(ctx)
116
117	right := []vxfw.Surface{}
118	if len(m.features.features) > 0 {
119		right = append(right, features)
120	}
121
122	if m.stats != nil {
123		pages, _ := NewUsageInfo("pages", &m.stats.Pages).Draw(ctx)
124		prose, _ := NewUsageInfo("prose", &m.stats.Prose).Draw(ctx)
125		pastes, _ := NewUsageInfo("pastes", &m.stats.Pastes).Draw(ctx)
126		feeds, _ := NewUsageInfo("rss-to-email", &m.stats.Feeds).Draw(ctx)
127		right = append(right,
128			pages,
129			prose,
130			pastes,
131			feeds,
132		)
133	}
134	rightPane := NewGroupStack(right)
135	rightSurf, _ := rightPane.Draw(ctx)
136
137	root := vxfw.NewSurface(ctx.Max.Width, ctx.Max.Height, m)
138	root.AddChild(0, 0, leftSurf)
139	root.AddChild(30, 0, rightSurf)
140	return root, nil
141}