Agent: components 261-280

This commit is contained in:
ZacharyZhang-NY committed 2026-10-05 14:29:40 -04:00
1 parent 6f0c02d339
commit 7c94e9e190
108 files changed
+6363 -7

No files matched your search

+280 -1
View File
@@ -1,3 +1,282 @@
package main
var agentPages = []page{}
import (
"fmt"
"time"
"github.com/egoist/mygo/ui"
mujica "mujicaui"
)
const agentCategory = "Agent"
var agentPages = []page{
{ID: "261", Name: "ToolCallCard", ZhName: "工具调用卡片", Category: agentCategory, Demo: toolCallCardDemo, Code: toolCallCardCode,
Keys: "Tab 到参数、结果折叠按钮,Enter / Space 展开或折叠。"},
{ID: "262", Name: "ToolCallGroup", ZhName: "工具调用组", Category: agentCategory, Demo: toolCallGroupDemo, Code: toolCallGroupCode,
Keys: "Tab 到组标题,Enter / Space 展开或折叠。"},
{ID: "263", Name: "ToolApprovalDialog", ZhName: "工具审批对话框", Category: agentCategory, Demo: toolApprovalDemo, Code: toolApprovalDialogCode,
Keys: "打开时焦点在拒绝;Enter 只触发聚焦按钮,Tab 到允许才能批准;Escape 拒绝。"},
{ID: "264", Name: "PermissionPrompt", ZhName: "权限请求", Category: agentCategory, Demo: permissionDemo, Code: permissionPromptCode,
Keys: "打开时焦点在拒绝;Tab 到始终允许、允许一次;Escape 拒绝。"},
{ID: "265", Name: "AgentStepList", ZhName: "执行步骤列表", Category: agentCategory, Demo: agentStepListDemo, Code: agentStepListCode,
Keys: "↑ ↓ 在步骤与重试间移动;Enter 展开详情或重试。"},
{ID: "266", Name: "AgentPlan", ZhName: "执行计划", Category: agentCategory, Demo: agentPlanDemo, Code: agentPlanCode,
Keys: "Space 勾选;Tab 到跳过或恢复;在输入框中按 Enter 新增。"},
{ID: "267", Name: "AgentStatus", ZhName: "Agent 状态", Category: agentCategory, Demo: agentStatusDemo, Code: agentStatusCode,
Keys: "不获取焦点;屏幕阅读器读出状态文字。"},
{ID: "268", Name: "AgentProgress", ZhName: "Agent 进度", Category: agentCategory, Demo: agentProgressDemo, Code: agentProgressCode,
Keys: "不获取焦点;当前动作以状态读出。"},
{ID: "269", Name: "SubAgentTree", ZhName: "子 Agent 树", Category: agentCategory, Demo: subAgentTreeDemo, Code: subAgentTreeCode,
Keys: "↑ ↓ 移动;→ 展开,← 折叠;Enter / Space 切换。"},
{ID: "270", Name: "FileChangeCard", ZhName: "文件变更卡片", Category: agentCategory, Demo: fileChangeCardDemo, Code: fileChangeCardCode,
Keys: "Tab 到打开差异、拒绝、接受或撤销,Enter / Space 触发。"},
{ID: "271", Name: "MultiFileDiffReview", ZhName: "多文件差异审阅", Category: agentCategory, Demo: multiFileDiffDemo, Code: multiFileDiffReviewCode,
Keys: "文件列表中 ↑ ↓ 移动,Enter 选择;Tab 到接受、拒绝、全部接受。"},
{ID: "272", Name: "CommandExecutionCard", ZhName: "命令执行卡片", Category: agentCategory, Demo: commandExecutionDemo, Code: commandExecutionCardCode,
Keys: "Tab 到中止,Enter / Space 请求中止;输出区可滚动。"},
{ID: "273", Name: "ScreenshotStream", ZhName: "Agent 画面流", Category: agentCategory, Demo: screenshotStreamDemo, Code: screenshotStreamCode,
Keys: "画面区中 ← → 逐帧,Space 暂停或恢复。"},
{ID: "274", Name: "ArtifactPanel", ZhName: "产物面板", Category: agentCategory, Demo: artifactPanelDemo, Code: artifactPanelCode,
Keys: "切换组中 ← → 移动、Enter 选择;Tab 到复制、导出。"},
{ID: "275", Name: "ArtifactVersionSwitcher", ZhName: "产物版本切换", Category: agentCategory, Demo: artifactVersionDemo, Code: artifactVersionSwitcherCode,
Keys: "焦点在组件内时 ← → 切换版本;Tab 到比较按钮。"},
{ID: "276", Name: "SandboxStatus", ZhName: "沙箱状态", Category: agentCategory, Demo: sandboxStatusDemo, Code: sandboxStatusCode,
Keys: "不获取焦点;剩余时间每秒刷新。"},
{ID: "277", Name: "MCPServerList", ZhName: "MCP 服务器列表", Category: agentCategory, Demo: mcpServerListDemo, Code: mcpServerListCode,
Keys: "Tab 到各行的启动、停止、重新连接,Enter / Space 触发。"},
{ID: "278", Name: "MemoryPanel", ZhName: "记忆面板", Category: agentCategory, Demo: memoryPanelDemo, Code: memoryPanelCode,
Keys: "Tab 到搜索、编辑、删除;删除确认默认聚焦取消,Escape 取消。"},
{ID: "279", Name: "CheckpointList", ZhName: "检查点列表", Category: agentCategory, Demo: checkpointListDemo, Code: checkpointListCode,
Keys: "Tab 到回滚,Enter 打开确认;确认框默认聚焦取消。"},
{ID: "280", Name: "HumanInputRequest", ZhName: "人工输入请求", Category: agentCategory, Demo: humanInputDemo, Code: humanInputRequestCode,
Keys: "方向键选择选项;输入框中 Enter 发送;Tab 到发送回答。"},
}
// agentStates are the states a demo can pick from.
var agentStates = []string{"Pending", "Planning", "Running", "Waiting", "Done", "Failed", "Skipped"}
// agentStatePick is a select over agentStates, logging changes.
func agentStatePick(c *ui.Context, log func(string), v *string) mujica.AgentState {
if ui.Select(c, v, agentStates).Changed() {
log("state = " + *v)
}
for i, s := range agentStates {
if s == *v {
return mujica.AgentState(i)
}
}
panic("gallery: unknown state " + *v)
}
type toolCallDemoState struct {
state string
failed bool
still bool
}
func toolCallCardDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "261", func() toolCallDemoState { return toolCallDemoState{state: "Done"} })
var st mujica.AgentState
overlayControls(c, func() {
st = agentStatePick(c, log, &s.state)
overlayCheck(c, log, &s.failed, "Error")
overlayCheck(c, log, &s.still, "Still")
})
call := mujica.ToolCall{ID: "1", Name: "read_file", Args: `{"path":"court/ledger.md","limit":40}`,
Result: `{"lines":40,"truncated":false}`, State: st, Duration: 420 * time.Millisecond}
if s.failed {
call.Error = "permission denied: court/ledger.md"
}
if mujica.ToolCallCard(c, call, mujica.ToolCallCardOptions{Still: s.still}).Changed() {
log("folded or unfolded a section")
}
}
func toolCallGroupDemo(c *ui.Context, log func(string)) {
open := ui.Local(c.Root(), "262", func() bool { return true })
calls := []mujica.ToolCall{
{ID: "a", Name: "list_dir", Args: `{"path":"court"}`, Result: `["ledger.md","menu.md"]`, State: mujica.AgentDone, Duration: 300 * time.Millisecond},
{ID: "b", Name: "read_file", Args: `{"path":"court/wine.md"}`, Error: "not found", State: mujica.AgentFailed, Duration: 1200 * time.Millisecond},
{ID: "c", Name: "grep", Args: `{"pattern":"Sakiko"}`, Result: `{"matches":3}`, State: mujica.AgentDone, Duration: 500 * time.Millisecond},
}
if mujica.ToolCallGroup(c, open, calls, mujica.ToolCallGroupOptions{}).Changed() {
log(fmt.Sprintf("group open = %v", *open))
}
}
func toolApprovalDemo(c *ui.Context, log func(string)) {
open := ui.Local(c.Root(), "263", func() bool { return false })
if mujica.Button(c, "Run delete_files", mujica.ButtonOptions{}).Clicked() {
*open = true
log("asked for approval")
}
r := mujica.ToolApprovalDialog(c, open, mujica.ToolApprovalOptions{Tool: "delete_files",
Args: `{"glob":"build/*.tmp","dry_run":false}`, Reason: "The agent wants to clean temporary files from the build folder."})
if r.Allowed {
log("allowed")
}
if r.Denied {
log("denied")
}
}
type permissionDemoState struct {
open bool
grant string
}
func permissionDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "264", func() permissionDemoState { return permissionDemoState{grant: "none"} })
ui.Row(c).Gap(12).AlignItems(ui.Center).Children(func() {
if mujica.Button(c, "Ask to write", mujica.ButtonOptions{}).Clicked() {
s.open = true
log("asked for permission")
}
ui.Text(c, "Grant: "+s.grant)
})
r := mujica.PermissionPrompt(c, &s.open, mujica.PermissionPromptOptions{Title: "Write to the ledger?",
Description: "The agent wants to record tonight's guests.", Scope: "Write access to ~/court/ledger and its subfolders"})
if r.Choice != mujica.PermissionNone {
s.grant = [...]string{"none", "once", "always", "denied"}[r.Choice]
log("choice = " + s.grant)
}
}
type stepDemoState struct {
steps []mujica.AgentStep
disabled bool
}
func agentStepListDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "265", func() stepDemoState {
return stepDemoState{steps: []mujica.AgentStep{
{ID: "read", Title: "Read the guest list", State: mujica.AgentDone, Duration: 800 * time.Millisecond, Detail: "42 guests, 3 arrive late."},
{ID: "seat", Title: "Assign the seats", State: mujica.AgentFailed, Duration: 2 * time.Second, Detail: "Table 4 is over capacity."},
{ID: "send", Title: "Send the invitations", State: mujica.AgentPending},
}}
})
overlayControls(c, func() { overlayCheck(c, log, &s.disabled, "Disabled") })
r := mujica.AgentStepList(c, s.steps, mujica.AgentStepListOptions{Disabled: s.disabled})
if r.Toggled != "" {
log("toggled " + r.Toggled)
}
if r.Retry != "" {
log("retry " + r.Retry)
for i := range s.steps {
if s.steps[i].ID == r.Retry {
s.steps[i].State, s.steps[i].Detail = mujica.AgentRunning, "Retrying with a larger table."
}
}
}
}
type planDemoState struct {
items []mujica.PlanItem
disabled bool
}
func agentPlanDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "266", func() planDemoState {
return planDemoState{items: []mujica.PlanItem{
{ID: "a", Text: "Read the guest list", State: mujica.AgentDone},
{ID: "b", Text: "Assign the seats", State: mujica.AgentRunning},
{ID: "c", Text: "Order the flowers"},
}}
})
overlayControls(c, func() {
overlayCheck(c, log, &s.disabled, "Disabled")
if mujica.Button(c, "Agent finishes a step", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
for i := range s.items {
if s.items[i].State != mujica.AgentDone && s.items[i].State != mujica.AgentSkipped {
s.items[i].State = mujica.AgentDone
log("agent checked " + s.items[i].Text)
break
}
}
}
})
if mujica.AgentPlan(c, &s.items, mujica.AgentPlanOptions{Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("plan changed (%d items)", len(s.items)))
}
}
type statusDemoState struct {
state string
still bool
}
func agentStatusDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "267", func() statusDemoState { return statusDemoState{state: "Running"} })
var st mujica.AgentState
overlayControls(c, func() {
st = agentStatePick(c, log, &s.state)
overlayCheck(c, log, &s.still, "Still")
})
ui.Row(c).Gap(8).Wrap().Children(func() {
mujica.AgentStatus(c, st, mujica.AgentStatusOptions{Still: s.still})
for _, v := range []mujica.AgentState{mujica.AgentPlanning, mujica.AgentRunning, mujica.AgentWaiting, mujica.AgentDone, mujica.AgentFailed} {
mujica.AgentStatus(c, v, mujica.AgentStatusOptions{Still: s.still})
}
})
}
type progressDemoState struct {
value float64
indeterminate bool
}
func agentProgressDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "268", func() progressDemoState { return progressDemoState{value: 0.4} })
overlayControls(c, func() {
if ui.Slider(c, &s.value, 0, 1).Width(200).Changed() {
log(fmt.Sprintf("value = %.2f", s.value))
}
overlayCheck(c, log, &s.indeterminate, "Indeterminate")
})
action := "Reading ledger.md"
if s.value > 0.5 {
action = "Writing the seating chart"
}
mujica.AgentProgress(c, s.value, mujica.AgentProgressOptions{Label: "Preparing the ball", Action: action, Indeterminate: s.indeterminate})
}
func subAgentTreeDemo(c *ui.Context, log func(string)) {
disabled := ui.Local(c.Root(), "269", func() bool { return false })
overlayControls(c, func() { overlayCheck(c, log, disabled, "Disabled") })
agents := []mujica.SubAgent{{ID: "lead", Name: "Steward", Task: "Prepare the ball", State: mujica.AgentRunning, Children: []mujica.SubAgent{
{ID: "music", Name: "Musician", Task: "Hire the quartet", State: mujica.AgentDone},
{ID: "kitchen", Name: "Cook", Task: "Plan the menu", State: mujica.AgentWaiting, Children: []mujica.SubAgent{
{ID: "wine", Name: "Sommelier", Task: "Choose the wine", State: mujica.AgentFailed},
}},
}}}
if mujica.SubAgentTree(c, agents, mujica.SubAgentTreeOptions{Disabled: *disabled}).Changed() {
log("expanded or collapsed an agent")
}
}
type fileChangeDemoState struct {
decision mujica.FileDecision
disabled bool
preview float64
}
func fileChangeCardDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "270", func() fileChangeDemoState { return fileChangeDemoState{preview: 8} })
overlayControls(c, func() {
overlayCheck(c, log, &s.disabled, "Disabled")
overlaySlider(c, log, &s.preview, "Preview", "lines", 2, 12, 1)
})
r := mujica.FileChangeCard(c, &s.decision, agentDemoChange, mujica.FileChangeCardOptions{Preview: int(s.preview), Disabled: s.disabled})
if r.Decided {
log(fmt.Sprintf("decision = %d", s.decision))
}
if r.OpenDiff {
log("open the full diff")
}
}
var agentDemoChange = mujica.FileChange{Path: "court/ledger.md",
Old: "# Ledger\nGuests: 40\nWine: claret\nMusic: none\nHall: east wing\n",
New: "# Ledger\nGuests: 42\nWine: claret\nMusic: quartet\nFlowers: lilies\nHall: east wing\n"}
+196
View File
@@ -0,0 +1,196 @@
package main
// Code snippets of the Agent pages, the same as their compiled Examples.
const toolCallCardCode = `call := mujica.ToolCall{ID: "1", Name: "read_file", Args: "{\"path\":\"ledger.md\"}",
Result: "{\"lines\":40}", State: mujica.AgentDone, Duration: 420 * time.Millisecond}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.ToolCallCard(c, call, mujica.ToolCallCardOptions{OpenArgs: true})
}
ui.Render(view, 400, 300, 1)`
const toolCallGroupCode = `open := false
calls := []mujica.ToolCall{
{ID: "a", Name: "list_dir", State: mujica.AgentDone, Duration: 300 * time.Millisecond},
{ID: "b", Name: "read_file", State: mujica.AgentFailed, Error: "not found"},
}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.ToolCallGroup(c, &open, calls, mujica.ToolCallGroupOptions{})
}
ui.Render(view, 400, 300, 1)`
const toolApprovalDialogCode = `open := true
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.ToolApprovalDialog(c, &open, mujica.ToolApprovalOptions{
Tool: "delete_files", Args: "{\"glob\":\"*.tmp\"}", Reason: "Clean the build folder.",
})
if r.Allowed {
fmt.Println("run the tool")
}
}
ui.Render(view, 400, 300, 1)`
const permissionPromptCode = `open := true
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.PermissionPrompt(c, &open, mujica.PermissionPromptOptions{
Title: "Write to the ledger?", Scope: "Write access to ~/court/ledger",
})
if r.Choice == mujica.PermissionAllowAlways {
fmt.Println("remember the grant")
}
}
ui.Render(view, 400, 300, 1)`
const agentStepListCode = `steps := []mujica.AgentStep{
{ID: "read", Title: "Read the guest list", State: mujica.AgentDone, Duration: time.Second},
{ID: "seat", Title: "Assign the seats", State: mujica.AgentFailed, Detail: "Table 4 is full."},
{ID: "send", Title: "Send invitations", State: mujica.AgentPending},
}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if r := mujica.AgentStepList(c, steps, mujica.AgentStepListOptions{}); r.Retry != "" {
fmt.Println("retry", r.Retry)
}
}
ui.Render(view, 400, 300, 1)`
const agentPlanCode = `items := []mujica.PlanItem{
{ID: "a", Text: "Read the guest list", State: mujica.AgentDone},
{ID: "b", Text: "Assign the seats", State: mujica.AgentRunning},
{ID: "c", Text: "Order the flowers"},
}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.AgentPlan(c, &items, mujica.AgentPlanOptions{})
}
ui.Render(view, 400, 300, 1)`
const agentStatusCode = `view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.AgentStatus(c, mujica.AgentWaiting, mujica.AgentStatusOptions{})
}
ui.Render(view, 400, 300, 1)`
const agentProgressCode = `view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.AgentProgress(c, 0.4, mujica.AgentProgressOptions{Action: "Reading ledger.md"})
}
ui.Render(view, 400, 300, 1)`
const subAgentTreeCode = `agents := []mujica.SubAgent{{ID: "lead", Name: "Steward", State: mujica.AgentRunning, Children: []mujica.SubAgent{
{ID: "cook", Name: "Cook", Task: "Plan the menu", State: mujica.AgentWaiting},
}}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.SubAgentTree(c, agents, mujica.SubAgentTreeOptions{})
}
ui.Render(view, 400, 300, 1)`
const fileChangeCardCode = `decision := mujica.FilePending
change := mujica.FileChange{Path: "ledger.md", Old: "Guests: 40\n", New: "Guests: 42\n"}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if mujica.FileChangeCard(c, &decision, change, mujica.FileChangeCardOptions{}).OpenDiff {
fmt.Println("open the full diff")
}
}
ui.Render(view, 400, 300, 1)`
const multiFileDiffReviewCode = `files := []mujica.FileChange{
{Path: "ledger.md", Old: "Guests: 40\n", New: "Guests: 42\n"},
{Path: "menu.md", Old: "soup\n", New: "soup\ntart\n"},
}
decisions := make([]mujica.FileDecision, len(files))
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.MultiFileDiffReview(c, &decisions, files, mujica.MultiFileDiffReviewOptions{})
}
ui.Render(view, 400, 300, 1)`
const commandExecutionCardCode = `run := mujica.CommandRun{Command: "go test ./...", Dir: "~/court", Running: true, Output: "\x1b[32mok\x1b[0m court\n"}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if mujica.CommandExecutionCard(c, run, mujica.CommandExecutionCardOptions{}).Changed() {
fmt.Println("abort the process")
}
}
ui.Render(view, 400, 300, 1)`
const screenshotStreamCode = `var st mujica.ScreenshotStreamState
var frames []mujica.ScreenshotFrame // appended as the agent captures them
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.ScreenshotStream(c, &st, frames, mujica.ScreenshotStreamOptions{})
}
ui.Render(view, 400, 300, 1)`
const artifactPanelCode = `art := mujica.Artifact{Title: "seating.go", Language: "go", Content: "package court\n"}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if mujica.ArtifactPanel(c, art, mujica.ArtifactPanelOptions{}).Exported {
fmt.Println("save the file")
}
}
ui.Render(view, 400, 300, 1)`
const artifactVersionSwitcherCode = `current := 1
versions := []mujica.ArtifactVersion{{Label: "v1", Note: "First draft"}, {Label: "v2", Note: "Added music"}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if mujica.ArtifactVersionSwitcher(c, &current, versions, mujica.ArtifactVersionSwitcherOptions{}).Compare {
fmt.Println("diff v1 and v2")
}
}
ui.Render(view, 400, 300, 1)`
const sandboxStatusCode = `info := mujica.SandboxInfo{Name: "sandbox-7", State: mujica.SandboxRunning,
Resources: []mujica.SandboxResource{{Name: "CPU", Used: 1.5, Limit: 2, Unit: "cores"}}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.SandboxStatus(c, info, mujica.SandboxStatusOptions{})
}
ui.Render(view, 400, 300, 1)`
const mcpServerListCode = `servers := []mujica.MCPServer{{ID: "fs", Name: "filesystem", Transport: "stdio", State: mujica.MCPConnected, Tools: 12}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if r := mujica.MCPServerList(c, servers, mujica.MCPServerListOptions{}); r.Action == mujica.MCPStop {
fmt.Println("stop", r.Server)
}
}
ui.Render(view, 400, 300, 1)`
const memoryPanelCode = `entries := []mujica.MemoryEntry{{ID: "1", Text: "Lady Sakiko prefers the east wing.", Source: "Chat"}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if r := mujica.MemoryPanel(c, &entries, mujica.MemoryPanelOptions{}); r.Deleted != "" {
fmt.Println("forget", r.Deleted)
}
}
ui.Render(view, 400, 300, 1)`
const checkpointListCode = `current := "c2"
points := []mujica.Checkpoint{{ID: "c2", Label: "Menu drafted"}, {ID: "c1", Label: "Start"}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if mujica.CheckpointList(c, &current, points, mujica.CheckpointListOptions{}).Changed() {
fmt.Println("restore", current)
}
}
ui.Render(view, 400, 300, 1)`
const humanInputRequestCode = `answer := ""
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.HumanInputRequest(c, &answer, mujica.HumanInputRequestOptions{
Question: "Which hall?", Options: []string{"East wing", "Great hall"}, AllowFree: true,
})
if r.Submitted {
fmt.Println("reply", answer)
}
}
ui.Render(view, 400, 300, 1)`
+285
View File
@@ -0,0 +1,285 @@
package main
import (
"fmt"
"image"
"time"
"github.com/egoist/mygo/ui"
mujica "mujicaui"
)
type multiFileDemoState struct {
decisions []mujica.FileDecision
disabled bool
}
var agentDemoFiles = []mujica.FileChange{
agentDemoChange,
{Path: "court/menu.md", Old: "soup\nroast\n", New: "soup\nroast\ntart\n"},
{Path: "court/old-plan.md", Old: "Hold the ball in the cellar.\n"},
}
func multiFileDiffDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "271", func() multiFileDemoState {
return multiFileDemoState{decisions: make([]mujica.FileDecision, len(agentDemoFiles))}
})
overlayControls(c, func() {
overlayCheck(c, log, &s.disabled, "Disabled")
if mujica.Button(c, "Reset", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
clear(s.decisions)
log("reset")
}
})
if mujica.MultiFileDiffReview(c, &s.decisions, agentDemoFiles, mujica.MultiFileDiffReviewOptions{Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("decisions = %v", s.decisions))
}
}
type commandDemoState struct {
run mujica.CommandRun
disabled bool
lines int
}
func commandExecutionDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "272", func() commandDemoState {
return commandDemoState{run: mujica.CommandRun{Command: "go test ./court/...", Dir: "~/projects/court", Running: true}}
})
overlayControls(c, func() {
overlayCheck(c, log, &s.disabled, "Disabled")
if mujica.Button(c, "Print a line", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
s.lines++
color := []string{"32mok ", "33mwarn", "31mFAIL"}[s.lines%3]
s.run.Output += fmt.Sprintf("\x1b[%s\x1b[0m court/case%d 0.%02ds\n", color, s.lines, s.lines)
log("printed a line")
}
if mujica.Button(c, "Restart", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
s.run.Running, s.run.Output, s.lines = true, "", 0
log("restarted")
}
})
if mujica.CommandExecutionCard(c, s.run, mujica.CommandExecutionCardOptions{Disabled: s.disabled}).Changed() {
s.run.Running, s.run.ExitCode, s.run.Duration = false, 130, time.Duration(s.lines)*time.Second
s.run.Output += "\x1b[31maborted by user\x1b[0m\n"
log("abort requested")
}
}
type screenshotDemoState struct {
st mujica.ScreenshotStreamState
frames []mujica.ScreenshotFrame
disabled bool
}
// agentDemoFrame paints a frame with a moving block.
func agentDemoFrame(i int) mujica.ScreenshotFrame {
img := image.NewRGBA(image.Rect(0, 0, 320, 180))
for y := range 180 {
for x := range 320 {
o := img.PixOffset(x, y)
v := uint8(235)
if x >= 20+i*24%260 && x < 60+i*24%260 && y > 60 && y < 120 {
v = 120
}
img.Pix[o], img.Pix[o+1], img.Pix[o+2], img.Pix[o+3] = v, v-10, v-20, 255
}
}
return mujica.ScreenshotFrame{Image: ui.NewBitmap(img), Caption: fmt.Sprintf("Clicked button %d", i+1), At: time.Duration(i) * 2 * time.Second}
}
func screenshotStreamDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "273", func() screenshotDemoState {
return screenshotDemoState{frames: []mujica.ScreenshotFrame{agentDemoFrame(0), agentDemoFrame(1)}}
})
overlayControls(c, func() {
overlayCheck(c, log, &s.disabled, "Disabled")
if mujica.Button(c, "Capture a frame", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
s.frames = append(s.frames, agentDemoFrame(len(s.frames)))
log(fmt.Sprintf("frame %d arrived", len(s.frames)))
}
})
if mujica.ScreenshotStream(c, &s.st, s.frames, mujica.ScreenshotStreamOptions{Height: 220, Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("frame %d, paused = %v", s.st.Index+1, s.st.Paused))
}
}
type artifactDemoState struct {
kind string
disabled bool
}
func artifactPanelDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "274", func() artifactDemoState { return artifactDemoState{kind: "Code"} })
overlayControls(c, func() {
if ui.Select(c, &s.kind, []string{"Code", "Document"}).Changed() {
log("kind = " + s.kind)
}
overlayCheck(c, log, &s.disabled, "Disabled")
})
art := mujica.Artifact{Title: "seating.go", Language: "go",
Content: "package court\n\n// Seats maps guests to tables.\nfunc Seats() map[string]int {\n\treturn map[string]int{\"Sakiko\": 1, \"Uika\": 2}\n}\n"}
opts := mujica.ArtifactPanelOptions{Height: 200, Disabled: s.disabled}
if s.kind == "Document" {
art = mujica.Artifact{Title: "Invitation", Kind: mujica.ArtifactDocument, Content: "# You are invited\n\nThe masquerade begins at nine.\n"}
opts.Preview = func() {
mujica.Heading(c, "You are invited", mujica.HeadingOptions{})
ui.Text(c, "The masquerade begins at nine.")
}
}
r := mujica.ArtifactPanel(c, art, opts)
if r.Copied {
log("copied to the clipboard")
}
if r.Exported {
log("export requested")
}
}
type versionDemoState struct {
current int
disabled bool
}
func artifactVersionDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "275", func() versionDemoState { return versionDemoState{current: 2} })
overlayControls(c, func() { overlayCheck(c, log, &s.disabled, "Disabled") })
versions := []mujica.ArtifactVersion{{Label: "v1", Note: "First draft"}, {Label: "v2", Note: "Added the quartet"}, {Label: "v3", Note: "Moved the wine table"}}
r := mujica.ArtifactVersionSwitcher(c, &s.current, versions, mujica.ArtifactVersionSwitcherOptions{Disabled: s.disabled})
if r.Changed {
log("version = " + versions[s.current].Label)
}
if r.Compare {
log("compare " + versions[s.current-1].Label + " and " + versions[s.current].Label)
}
}
type sandboxDemoState struct {
state string
cpu float64
expires time.Time
}
func sandboxStatusDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "276", func() sandboxDemoState {
return sandboxDemoState{state: "Running", cpu: 1.2, expires: time.Now().Add(10 * time.Minute)}
})
names := []string{"Starting", "Running", "Stopped", "Failed"}
state := mujica.SandboxStarting
overlayControls(c, func() {
if ui.Select(c, &s.state, names).Changed() {
log("state = " + s.state)
}
overlaySlider(c, log, &s.cpu, "CPU", "cores", 0, 2, 0.1)
if mujica.Button(c, "Extend 5 min", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
s.expires = s.expires.Add(5 * time.Minute)
log("extended")
}
})
for i, n := range names {
if n == s.state {
state = mujica.SandboxState(i)
}
}
mujica.SandboxStatus(c, mujica.SandboxInfo{Name: "sandbox-7", State: state, Expires: s.expires, Resources: []mujica.SandboxResource{
{Name: "CPU", Used: s.cpu, Limit: 2, Unit: "cores"}, {Name: "Memory", Used: 3.1, Limit: 4, Unit: "GB"},
}}, mujica.SandboxStatusOptions{})
}
type mcpDemoState struct {
servers []mujica.MCPServer
disabled bool
}
func mcpServerListDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "277", func() mcpDemoState {
return mcpDemoState{servers: []mujica.MCPServer{
{ID: "fs", Name: "filesystem", Transport: "stdio", State: mujica.MCPConnected, Tools: 12},
{ID: "gh", Name: "github", Transport: "http", State: mujica.MCPError, Error: "401 Unauthorized"},
{ID: "db", Name: "postgres", Transport: "stdio", State: mujica.MCPStopped},
}}
})
overlayControls(c, func() { overlayCheck(c, log, &s.disabled, "Disabled") })
r := mujica.MCPServerList(c, s.servers, mujica.MCPServerListOptions{Disabled: s.disabled})
if r.Action == mujica.MCPNone {
return
}
log(fmt.Sprintf("%s: %s", r.Server, [...]string{"", "start", "stop", "reconnect"}[r.Action]))
for i := range s.servers {
if s.servers[i].ID != r.Server {
continue
}
switch r.Action {
case mujica.MCPStop:
s.servers[i].State = mujica.MCPStopped
default:
s.servers[i].State, s.servers[i].Tools, s.servers[i].Error = mujica.MCPConnected, 5, ""
}
}
}
type memoryDemoState struct {
entries []mujica.MemoryEntry
disabled bool
}
func memoryPanelDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "278", func() memoryDemoState {
return memoryDemoState{entries: []mujica.MemoryEntry{
{ID: "1", Text: "Lady Sakiko prefers the east wing.", Source: "Chat, 3 Oct"},
{ID: "2", Text: "The quartet needs two days' notice.", Source: "Plan, 1 Oct"},
{ID: "3", Text: "Claret is served after nine.", Source: "Ledger"},
}}
})
overlayControls(c, func() { overlayCheck(c, log, &s.disabled, "Disabled") })
r := mujica.MemoryPanel(c, &s.entries, mujica.MemoryPanelOptions{Disabled: s.disabled})
if r.Edited != "" {
log("edited " + r.Edited)
}
if r.Deleted != "" {
log("deleted " + r.Deleted)
}
}
type checkpointDemoState struct {
current string
disabled bool
}
func checkpointListDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "279", func() checkpointDemoState { return checkpointDemoState{current: "c3"} })
overlayControls(c, func() { overlayCheck(c, log, &s.disabled, "Disabled") })
points := []mujica.Checkpoint{{ID: "c3", Label: "Seats assigned", Note: "12 files changed"},
{ID: "c2", Label: "Menu drafted", Note: "3 files changed"}, {ID: "c1", Label: "Start"}}
if mujica.CheckpointList(c, &s.current, points, mujica.CheckpointListOptions{Disabled: s.disabled}).Changed() {
log("rolled back to " + s.current)
}
}
type humanDemoState struct {
answer string
free bool
deadline time.Time
disabled bool
}
func humanInputDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "280", func() humanDemoState { return humanDemoState{free: true} })
overlayControls(c, func() {
overlayCheck(c, log, &s.free, "AllowFree")
overlayCheck(c, log, &s.disabled, "Disabled")
if mujica.Button(c, "Start a 30 s timer", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
s.deadline = time.Now().Add(30 * time.Second)
log("timer started")
}
})
r := mujica.HumanInputRequest(c, &s.answer, mujica.HumanInputRequestOptions{Question: "Which hall should host the masquerade?",
Options: []string{"East wing", "Great hall"}, AllowFree: s.free, Deadline: s.deadline, Disabled: s.disabled})
if r.Submitted {
log("answer sent: " + s.answer)
}
if r.TimedOut {
log("timed out")
}
}