Files
ZacharyZhang-NY d60d53ef90 Add agent components 451-465 and icon.Crystal
Fifteen components that dress an AI agent's work as an RPG party:
AgentCard, ToolCall, PlanQuest, ThinkingOrb, TokenMana, ApprovalPrompt,
AgentLog, SubAgentParty, MemoryCrystals, AgentStatus, DiffScroll,
ModelSelect, PromptBox, CostCoins and Checkpoints.

The context window is HP and the token budget MP; tool calls are spells
that CAST, glow, then land OK or FAILED; the transcript reads as a battle
log whose newest line types itself out; models line up like fighters.
Busy signs keep moving under Reduce Motion, decoration stops. Commands
and prompts never reach the log, and a veiled PromptBox casts nothing.

icon.Crystal is the new 12x12 save and memory crystal.
2026-10-12 12:03:44 -04:00

128 lines
4.7 KiB
Go

package gallery
import (
"fmt"
"time"
"unicode/utf8"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/agent"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
var guildEntries = []Entry{
{459, "MemoryCrystals", "记忆水晶", "agent", "Memory slots as crystals. Lit ones hold something; the arrows peek inside.", demoMemoryCrystals},
{460, "AgentStatus", "状态环", "agent", "A ring that says it all: sleepy, busy, stuck or done.", demoAgentStatus},
{461, "DiffScroll", "改动卷轴", "agent", "What changed, on a scroll: each file with lines added and removed.", demoDiffScroll},
{462, "ModelSelect", "模型选择", "agent", "Pick a model like a fighter: stats on the right, a flag on your pick.", demoModelSelect},
{463, "PromptBox", "咒语输入", "agent", "Write the spell, then CAST. Enter casts; Shift+Enter starts a new line.", demoPromptBox},
{464, "CostCoins", "花费", "agent", "What it cost, in coins: gold dollars, silver dimes, bronze cents.", demoCostCoins},
{465, "Checkpoints", "存档点", "agent", "Save points along the way. RESTORE takes you back.", demoCheckpoints},
}
type guildDemo struct {
model, cents int
prompt string
saves []agent.Checkpoint
}
func guildState(c *ui.Context) *guildDemo {
return ui.Local(c.Root(), demoKey("guild"), func() guildDemo {
now := pixel.K(c).Now()
return guildDemo{model: 1, cents: 1234, saves: []agent.Checkpoint{
{Name: "Project start", At: now.Add(-50 * time.Hour)},
{Name: "Before the refactor", At: now.Add(-3 * time.Hour)},
{Name: "Tests green", At: now.Add(-5 * time.Minute)},
}}
})
}
func demoMemoryCrystals(c *ui.Context, log func(string)) {
memories := []string{"Prefers tabs", "Tests before commits", "Never force-push", "Coffee: black", "Hates dark patterns"}
if i := agent.MemoryCrystals(c, "Memories", memories, agent.MemoryCrystalsOptions{}).Picked; i >= 0 {
if i < len(memories) {
log("recalled: " + memories[i])
return
}
log(fmt.Sprintf("crystal %d is empty", i+1))
}
}
func demoAgentStatus(c *ui.Context, log func(string)) {
k := pixel.K(c)
ui.Row(c).Gap(k.D(8)).Children(func() {
for _, s := range []agent.Status{agent.Idle, agent.Working, agent.Blocked, agent.Done} {
agent.AgentStatus(c, fmt.Sprintf("Robo %d", s+1), s, agent.AgentStatusOptions{})
}
})
}
func demoDiffScroll(c *ui.Context, log func(string)) {
agent.DiffScroll(c, "Changes", []agent.FileDiff{
{Path: "pixel/canvas.go", Added: 42, Removed: 7},
{Path: "agent/agentstatus.go", Added: 88},
{Path: "README.md", Added: 3, Removed: 1},
{Path: "old/clever_hack.go", Removed: 120},
}, agent.DiffScrollOptions{Width: 260})
}
func demoModelSelect(c *ui.Context, log func(string)) {
s := guildState(c)
models := []agent.Model{
{Name: "SPRITE", Portrait: icon.Bolt, Speed: 5, Power: 2, Cost: 1},
{Name: "KNIGHT", Portrait: icon.Shield, Speed: 3, Power: 4, Cost: 3},
{Name: "WIZARD", Portrait: icon.Sparkle, Speed: 2, Power: 5, Cost: 5},
{Name: "BARD", Portrait: icon.Music, Speed: 4, Power: 3, Cost: 2},
}
if agent.ModelSelect(c, "Models", models, &s.model, agent.ModelSelectOptions{}).Changed {
log(models[s.model].Name + " joins the party")
}
}
func demoPromptBox(c *ui.Context, log func(string)) {
s := guildState(c)
r := agent.PromptBox(c, "Prompt", &s.prompt, agent.PromptBoxOptions{Placeholder: "Describe the quest…"})
// Prompts can hold secrets, so the log only counts.
if r.Changed {
log(fmt.Sprintf("%d runes written", utf8.RuneCountInString(s.prompt)))
}
if r.Cast != "" {
log(fmt.Sprintf("cast %d runes", utf8.RuneCountInString(r.Cast)))
}
}
func demoCostCoins(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := guildState(c)
ui.Row(c).Gap(k.D(8)).AlignItems(ui.End).Children(func() {
agent.CostCoins(c, "Spent", s.cents, agent.CostCoinsOptions{})
ui.Column(c).Gap(k.D(4)).Children(func() {
if input.Button(c, "RUN THE AGENT", input.ButtonOptions{Size: input.Small}).Clicked {
s.cents += 37
log(fmt.Sprintf("spent $%d.%02d", s.cents/100, s.cents%100))
}
if input.Button(c, "NEW MONTH", input.ButtonOptions{Size: input.Small}).Clicked {
s.cents = 0
log("the bill is paid")
}
})
})
}
func demoCheckpoints(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := guildState(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
if i := agent.Checkpoints(c, "Save points", s.saves, agent.CheckpointsOptions{}).Restored; i >= 0 {
log("restored: " + s.saves[i].Name)
}
if input.Button(c, "SAVE HERE", input.ButtonOptions{Size: input.Small}).Clicked {
s.saves = append(s.saves, agent.Checkpoint{Name: fmt.Sprintf("Save %d", len(s.saves)+1), At: k.Now()})
log("saved")
}
})
}