Files
8bitMyGoUI/agent/promptbox.go
T
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

50 lines
1.6 KiB
Go

package agent
import (
"log/slog"
"strings"
"unicode/utf8"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
type PromptBoxOptions struct {
Placeholder string
// Width is in dots, zero 240; Lines zero means 3.
Width, Lines int
Disabled bool
}
type PromptBoxResult struct {
Element *ui.Element
Changed bool
// Cast is the prompt cast this frame, else empty.
Cast string
}
// PromptBox: Enter or CAST casts the prompt; Shift+Enter breaks lines.
func PromptBox(c *ui.Context, text string, value *string, o PromptBoxOptions) PromptBoxResult {
k := pixel.K(c)
w, lines := pixel.Or("PromptBox Width", o.Width, 240), pixel.Or("PromptBox Lines", o.Lines, 3)
var res PromptBoxResult
res.Element = ui.Column(c).Gap(k.D(4)).AlignItems(ui.End).Children(func() {
f := input.TextArea(c, text, value, input.TextAreaOptions{Placeholder: o.Placeholder, Width: w, Lines: lines, Disabled: o.Disabled, EnterSends: true})
empty := strings.TrimSpace(*value) == ""
b := input.Button(c, k.Msg("cast"), input.ButtonOptions{Variant: input.Primary, Icon: icon.Sparkle, Disabled: o.Disabled || empty})
res.Changed = f.Changed
// Off or veiled, a key sent this frame casts nothing.
if (f.Submitted || b.Clicked) && !empty && !f.Input.IsDisabled() {
res.Cast, *value = *value, ""
// The cleared field shows next frame and keeps focus.
f.Input.Focus()
c.Invalidate()
slog.Debug("8bit: prompt cast", "label", text, "runes", utf8.RuneCountInString(res.Cast))
}
})
return res
}