mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 22:37:28 +00:00
* feat(pods/admin): root admin alias, default-caps cleanup, pod rebuild script - auth: add `root` as an admin-console route alias (alongside admin/sysop); still gated by $AGENTBBS_ADMINS — the name confers nothing on its own. - pods: drop the now-redundant tuneApt apt-sandbox hack. Rootless podman keeps its default capability set, so apt/chown/su work without disabling the download sandbox. - scripts/rebuild-pods.sh: recreate all member pods (keeps home volumes) so they pick up the current container profile on next `ssh pod@`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(arcade,ui): 80s arcade classics + shared menu theme Arcade games (PRD §5.1), generalizing the sandboxed-PTY DOOM path into an external-game registry: Space Invaders (nInvaders), Pac-Man (pacman4console), Tetris (tint/vitetris), Moon Patrol (moon-buggy). Binaries resolve from assets/bin, PATH, then /usr/games, so a distro install or a hand-built binary lights each game up; missing games are skipped with a discovery hint. Installed on the host via `scripts/fetch-assets.sh --arcade` (apt), wired into setup.sh behind FETCH_ARCADE (default on). UI: new shared ui.Theme.MenuItem widget (accent cursor + badge, description shown only for the focused row) adopted by the hub and arcade menus; the hub groups rows under Features/Sessions headers and the arcade under DOOM/ARCADE/BUILT-IN. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
58 lines
1.3 KiB
Go
58 lines
1.3 KiB
Go
package arcade
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
"github.com/profullstack/agentbbs/internal/plugin"
|
|
"github.com/profullstack/agentbbs/internal/store"
|
|
"github.com/profullstack/agentbbs/internal/ui"
|
|
)
|
|
|
|
// board renders the global top scores (PRD §5.1 leaderboards).
|
|
type board struct {
|
|
ctx plugin.Context
|
|
scores []store.Score
|
|
err error
|
|
}
|
|
|
|
func newBoard(ctx plugin.Context) *board { return &board{ctx: ctx} }
|
|
|
|
func (b *board) Init() tea.Cmd {
|
|
return func() tea.Msg {
|
|
scores, err := b.ctx.Store.TopScores("snake", 10)
|
|
return boardMsg{scores: scores, err: err}
|
|
}
|
|
}
|
|
|
|
type boardMsg struct {
|
|
scores []store.Score
|
|
err error
|
|
}
|
|
|
|
func (b *board) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case boardMsg:
|
|
b.scores, b.err = msg.scores, msg.err
|
|
case tea.KeyMsg:
|
|
return b, back
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
func (b *board) View() string {
|
|
s := theme.Title("Leaderboard — snake") + "\n\n"
|
|
switch {
|
|
case b.err != nil:
|
|
s += ui.Danger.Render("error: " + b.err.Error())
|
|
case len(b.scores) == 0:
|
|
s += ui.Dim.Render("no scores yet — be the first")
|
|
default:
|
|
for i, sc := range b.scores {
|
|
s += fmt.Sprintf("%2d. %-20s %6d\n", i+1, sc.User, sc.Score)
|
|
}
|
|
}
|
|
s += "\n" + ui.KeyBar("any-key return to menu")
|
|
return ui.Frame.Render(s)
|
|
}
|