feat: add Go/Bubble Tea TUI rewrite (apps/tui-go)
Reimplement the TUI in Go using Bubble Tea, replacing the Kotlin/Tamboui app. Same WebSocket protocol, soft-rounded layout with blue accent theme.
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
package app
|
||||
|
||||
// PreviewFrame renders a single static frame for a named UI state at the given
|
||||
// terminal size. Used by cmd/preview to screenshot the look without a live
|
||||
// server. Not part of the runtime path.
|
||||
func PreviewFrame(kind string, w, h int) string {
|
||||
m := NewModel(nil)
|
||||
m.width, m.height = w, h
|
||||
m.theme = NewTheme(SoftBlue)
|
||||
|
||||
switch kind {
|
||||
case "idle-empty":
|
||||
m.connected = false
|
||||
|
||||
case "idle":
|
||||
m.connected = true
|
||||
m.currentModel = "llama-cpp:default"
|
||||
m.sessions = sampleSessions()
|
||||
m.workflows = sampleWorkflows()
|
||||
m.bgUpdates = 3
|
||||
m.selectedID = "04a546aa"
|
||||
|
||||
case "workflows":
|
||||
m.connected = true
|
||||
m.currentModel = "llama-cpp:default"
|
||||
m.sessions = sampleSessions()
|
||||
m.workflows = sampleWorkflows()
|
||||
m.selectedID = "04a546aa"
|
||||
m.wfVisible = true
|
||||
m.wfIndex = 1
|
||||
|
||||
case "session", "insert":
|
||||
m.connected = true
|
||||
m.currentModel = "llama-cpp:default"
|
||||
m.sessions = sampleSessions()
|
||||
m.selectedID = "04a546aa"
|
||||
m.sessionEntered = true
|
||||
m.routerConnected = true
|
||||
m.routerMessages["04a546aa"] = []RouterEntry{
|
||||
{"user", "write a healthcheck script for the api"},
|
||||
{"router", "I'll create a script that pings /health and checks the status code, then writes the results to a timestamped file."},
|
||||
{"tool", "--- a/healthcheck.sh\n+++ b/healthcheck.sh\n@@ -0,0 +1,4 @@\n+#!/usr/bin/env bash\n+curl -sf http://localhost:8080/health\n+echo \"ok $(date)\"\n"},
|
||||
}
|
||||
if s := m.session("04a546aa"); s != nil {
|
||||
s.CurrentStage = "write_script"
|
||||
s.Events = sampleEvents()
|
||||
s.Active = true
|
||||
s.ToolsByStage = sampleManifest()
|
||||
}
|
||||
if kind == "insert" {
|
||||
m.editMode = ModeInsert
|
||||
m.inputBuffer = "now add a retry with backoff"
|
||||
m.inputCursor = len(m.inputBuffer)
|
||||
}
|
||||
|
||||
case "tools":
|
||||
m.connected = true
|
||||
m.currentModel = "llama-cpp:default"
|
||||
m.sessions = sampleSessions()
|
||||
m.selectedID = "04a546aa"
|
||||
m.sessionEntered = true
|
||||
if s := m.session("04a546aa"); s != nil {
|
||||
s.CurrentStage = "write_script"
|
||||
s.ToolsByStage = sampleManifest()
|
||||
}
|
||||
m.overlay = OverlayToolPalette
|
||||
|
||||
case "approval":
|
||||
m.connected = true
|
||||
m.currentModel = "llama-cpp:default"
|
||||
m.sessions = sampleSessions()
|
||||
m.selectedID = "04a546aa"
|
||||
m.sessionEntered = true
|
||||
if s := m.session("04a546aa"); s != nil {
|
||||
s.CurrentStage = "write_script"
|
||||
s.Events = sampleEvents()
|
||||
s.Pending = &Approval{
|
||||
RequestID: "req-1", SessionID: "04a546aa", Tier: "T3", Risk: "HIGH",
|
||||
ToolName: "file_write",
|
||||
Preview: "--- a/healthcheck.sh\n+++ b/healthcheck.sh\n@@ -0,0 +1,4 @@\n+#!/usr/bin/env bash\n+curl -sf http://localhost:8080/health\n+echo ok\n",
|
||||
}
|
||||
}
|
||||
|
||||
case "diff":
|
||||
m.connected = true
|
||||
m.currentModel = "llama-cpp:default"
|
||||
m.sessions = sampleSessions()
|
||||
m.selectedID = "04a546aa"
|
||||
m.sessionEntered = true
|
||||
m.routerMessages["04a546aa"] = []RouterEntry{
|
||||
{"tool", "--- a/healthcheck.sh\n+++ b/healthcheck.sh\n@@ -1,2 +1,6 @@\n #!/usr/bin/env bash\n-echo hi\n+curl -sf http://localhost:8080/health\n+if [ $? -ne 0 ]; then\n+ echo \"unhealthy\" >&2\n+ exit 1\n+fi\n+echo \"ok $(date)\"\n"},
|
||||
}
|
||||
m.overlay = OverlayDiff
|
||||
|
||||
case "palette":
|
||||
m.connected = true
|
||||
m.currentModel = "llama-cpp:default"
|
||||
m.sessions = sampleSessions()
|
||||
m.selectedID = "04a546aa"
|
||||
m.overlay = OverlayPalette
|
||||
m.paletteFilter = "to"
|
||||
}
|
||||
return m.View()
|
||||
}
|
||||
|
||||
func sampleWorkflows() []Workflow {
|
||||
return []Workflow{
|
||||
{ID: "healthcheck", Description: "ping endpoints and report status"},
|
||||
{ID: "refactor", Description: "multi-stage code refactor with review"},
|
||||
{ID: "triage", Description: "classify and route incoming issues"},
|
||||
}
|
||||
}
|
||||
|
||||
func sampleManifest() map[string][]ManifestTool {
|
||||
return map[string][]ManifestTool{
|
||||
"plan": {{"read_file", 0}, {"list_dir", 0}},
|
||||
"write_script": {{"file_write", 3}, {"shell_exec", 4}, {"read_file", 0}},
|
||||
"verify": {{"shell_exec", 4}, {"http_get", 1}},
|
||||
}
|
||||
}
|
||||
|
||||
func sampleSessions() []Session {
|
||||
return []Session{
|
||||
{ID: "04a546aa", Status: "FAILED", WorkflowID: "healthcheck", Name: "healthcheck", CurrentStage: "write_script", LastEventAt: 1748000000000},
|
||||
{ID: "0d7097bb", Status: "COMPLETED", WorkflowID: "healthcheck", Name: "healthcheck", LastEventAt: 1748000000000},
|
||||
{ID: "1dae17cc", Status: "CHAT", WorkflowID: "chat", Name: "chat", LastEventAt: 1748000000000},
|
||||
{ID: "338f09dd", Status: "FAILED", WorkflowID: "healthcheck", Name: "healthcheck", CurrentStage: "write_script", LastEventAt: 1748000000000},
|
||||
{ID: "3f362dee", Status: "CHAT", WorkflowID: "chat", Name: "chat", LastEventAt: 1748000000000},
|
||||
{ID: "92f6c3ff", Status: "CHAT", WorkflowID: "chat", Name: "chat", LastEventAt: 1748000000000},
|
||||
{ID: "9a7682gg", Status: "COMPLETED", WorkflowID: "healthcheck", Name: "healthcheck", LastEventAt: 1748000000000},
|
||||
}
|
||||
}
|
||||
|
||||
func sampleEvents() []EventEntry {
|
||||
return []EventEntry{
|
||||
{"13:42:36", "InferenceStarted", "write_script"},
|
||||
{"13:43:00", "InferenceCompleted", "write_script"},
|
||||
{"13:43:00", "ToolStarted", "file_write"},
|
||||
{"13:43:00", "SessionPaused", "APPROVAL_PENDING"},
|
||||
{"13:44:10", "ToolCompleted", "file_write"},
|
||||
{"13:44:10", "InferenceStarted", "write_script"},
|
||||
{"13:44:10", "SessionFailed", "CANCELLED"},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user