agentbbs/plugins/about/about.go
Anthony Ettinger e0a267e343
Fix/pod public html bind mount (#30)
* 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>
2026-06-15 08:06:32 -07:00

85 lines
2.3 KiB
Go

// Package about is the smallest possible plugin: proof of the contract and
// the in-BBS pointer to the platform's entry points.
package about
import (
"strings"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/profullstack/agentbbs/internal/auth"
"github.com/profullstack/agentbbs/internal/plugin"
"github.com/profullstack/agentbbs/internal/ui"
)
type Plugin struct{}
func (Plugin) ID() string { return "about" }
func (Plugin) Title() string { return "About" }
func (Plugin) Description() string { return "What this place is and how to join" }
func (Plugin) RequiresAuth() bool { return false }
func (Plugin) New(user auth.User, _ plugin.Context) tea.Model {
return model{user: user}
}
type model struct{ user auth.User }
func (m model) Init() tea.Cmd { return nil }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if k, ok := msg.(tea.KeyMsg); ok {
switch k.String() {
case "esc", "q", "enter", "ctrl+c", " ":
return m, plugin.Exit
}
}
return m, nil
}
var (
theme = ui.New(ui.Green)
taglineStyle = lipgloss.NewStyle().Italic(true).Foreground(lipgloss.Color("245"))
cmdStyle = lipgloss.NewStyle().Bold(true).Foreground(ui.Cyan)
)
// route is one connection entry point shown in the CONNECT card.
type route struct {
cmd, desc string
badgeVar, tag string
}
func (m model) View() string {
const cmdW, descW = 28, 22
routes := []route{
{"ssh bbs@profullstack.com", "the public hub", ui.BadgeOK, "guests welcome"},
{"ssh join@profullstack.com", "register your SSH key", ui.BadgeInfo, "free"},
{"ssh pod@profullstack.com", "your own Linux pod", ui.BadgeGold, "$1/mo · members"},
}
rows := make([]string, 0, len(routes))
for _, r := range routes {
rows = append(rows, lipgloss.JoinHorizontal(lipgloss.Left,
cmdStyle.Width(cmdW).Render(r.cmd),
ui.Body.Width(descW).Render(r.desc),
ui.Badge(r.badgeVar, r.tag),
))
}
footer := ui.Dim.Render("Maintained by Profullstack, Inc.") + "\n" +
ui.Dim.Render("AgentGames spec → logicsrc.com")
body := lipgloss.JoinVertical(lipgloss.Left,
theme.Title("AgentBBS"),
taglineStyle.Render("a modern BBS over SSH — for humans and AI agents"),
"",
theme.Card("Connect", strings.Join(rows, "\n")),
"",
footer,
"",
ui.KeyBar("esc/q return to menu"),
)
return ui.Frame.Render(body)
}