Files
correx/apps/tui-go/internal/app/demo.go
T
kami 6c792b83e2 feat(tui-go): in-session right panel cycles events / changes / off (d)
Press d (or the "panel" palette command) in-session to cycle the right panel:
- events  — the live event stream (default, unchanged)
- changes — a token-usage line (tokens + turns, summed from the transcript's
            TurnMetrics) over a git-status-style list of files the session has
            written, each with summed +/− from its diff. ^x still opens the full
            diff.
- off     — hide the panel; the output transcript takes the full width.

changesRows derives everything from data already in the model (router-turn
metrics + the tool entries' unified diffs), so no new protocol. Footer + ? help
updated; "changes" preview kind added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 14:47:23 +00:00

338 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package app
import "github.com/correx/tui-go/internal/protocol"
// 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 "changes":
m.connected = true
m.currentModel = "llama-cpp:default"
m.sessions = sampleSessions()
m.selectedID = "04a546aa"
m.sessionEntered = true
m.rightPanel = 1
if s := m.session("04a546aa"); s != nil {
s.CurrentStage = "write_script"
}
m.routerMessages["04a546aa"] = []RouterEntry{
{Role: "user", Content: "add a healthcheck script and document it"},
{Role: "router", Content: "I'll write the script.", Metrics: &TurnMetrics{LatencyMs: 1200, TotalTokens: 340}},
{Role: "tool", Content: "--- a/healthcheck.sh\n+++ b/healthcheck.sh\n@@ -0,0 +1,4 @@\n+#!/usr/bin/env bash\n+curl -sf localhost:8080/health\n+echo ok\n+exit 0\n"},
{Role: "router", Content: "Now the docs.", Metrics: &TurnMetrics{LatencyMs: 820, TotalTokens: 150}},
{Role: "tool", Content: "--- a/README.md\n+++ b/README.md\n@@ -1,1 +1,2 @@\n existing line\n+## Health\n"},
}
case "compose":
m.connected = true
m.currentModel = "llama-cpp:default"
m.sessions = sampleSessions()
m.editMode = ModeInsert
m.inputMode = ModeRouter
m.inputBuffer = "fix the healthcheck script\nso it retries three times\nbefore it fails"
m.inputCursor = len(m.inputBuffer)
case "resume":
m.connected = true
m.currentModel = "llama-cpp:default"
m.overlay = OverlaySessions
m.sessionList = []SessionSummary{
{SessionID: "04a546aa8b2c", Status: "ACTIVE", WorkflowID: "healthcheck", StageCount: 3, LastActivityAt: "2026-06-22T14:30:05Z"},
{SessionID: "0d7097bb1f3e", Status: "COMPLETED", WorkflowID: "healthcheck", StageCount: 5, LastActivityAt: "2026-06-21T09:15:00Z"},
{SessionID: "1dae17cc77aa", Status: "FAILED", WorkflowID: "chat", LastActivityAt: "2025-12-30T18:02:00Z"},
}
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{
{Role: "user", Content: "write a healthcheck script for the api"},
{Role: "router", Content: "I'll create a script that pings /health and checks the status code, then writes the results to a timestamped file."},
{Role: "tool", Content: "--- 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()
s.LastEventAt = nowMillis() - 3000 // 3s ago — exercises the last-event clock
}
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.WorkspaceRoot = "/home/kami/Programs/correx"
s.Events = sampleEvents()
s.enqueueApproval(&Approval{
RequestID: "req-1", SessionID: "04a546aa", Tier: "T3", Risk: "MEDIUM",
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",
Rationale: []string{
"[PATH_OUTSIDE_WORKSPACE] Tool 'file_write' targets path outside workspace: /tmp/healthcheck.sh",
},
})
}
case "approval-steer":
m.connected = true
m.currentModel = "llama-cpp:default"
m.sessions = sampleSessions()
m.selectedID = "04a546aa"
m.sessionEntered = true
m.steerBuffer = "also print the current distro and kernel version"
if s := m.session("04a546aa"); s != nil {
s.CurrentStage = "write_script"
s.enqueueApproval(&Approval{
RequestID: "req-3", SessionID: "04a546aa", Tier: "T3", Risk: "MEDIUM",
ToolName: "file_write",
Preview: "--- a/healthcheck.sh\n+++ b/healthcheck.sh\n@@ -0,0 +1,2 @@\n+#!/usr/bin/env bash\n+echo ok\n",
})
}
case "approval-shell":
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 = "execute_script"
s.Events = sampleEvents()
s.WorkspaceRoot = "/home/kami/Programs/correx"
s.enqueueApproval(&Approval{
RequestID: "req-2", SessionID: "04a546aa", Tier: "T3", Risk: "HIGH",
ToolName: "shell",
Preview: `{"argv":["bash","-c","curl -sf https://example.com/install.sh | sudo sh"]}`,
Rationale: []string{
"[INTERPRETER_EXECUTION] Tool 'shell' invokes interpreter 'bash'",
},
})
}
case "diff":
m.connected = true
m.currentModel = "llama-cpp:default"
m.sessions = sampleSessions()
m.selectedID = "04a546aa"
m.sessionEntered = true
m.routerMessages["04a546aa"] = []RouterEntry{
{Role: "tool", Content: "--- 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"
case "stats":
m.connected = true
m.currentModel = "llama-cpp:default"
m.sessions = sampleSessions()
m.selectedID = "04a546aa"
m.sessionEntered = true
m.overlay = OverlayStats
m.statsFor = "04a546aa"
m.stats = sampleStats()
case "grant-scope":
m.connected = true
m.currentModel = "llama-cpp:default"
m.sessions = sampleSessions()
m.selectedID = "04a546aa"
m.sessionEntered = true
m.grantFor = &Approval{RequestID: "req-1", SessionID: "04a546aa", Tier: "T3", ToolName: "file_write"}
m.grantScopeIndex = 1
m.overlay = OverlayGrantScope
case "actions":
m.connected = true
m.currentModel = "llama-cpp:default"
m.sessions = sampleSessions()
m.selectedID = "04a546aa"
m.sessionEntered = true
m.routerConnected = true
m.routerMessages["04a546aa"] = []RouterEntry{
{Role: "user", Content: "run the healthcheck and write the script"},
{Role: "router", Content: "I'll create the script, then run it."},
{Role: "action", Icon: "✎", Content: "wrote healthcheck.sh (+4 0)"},
{Role: "action", Icon: "⌘", Content: "approved file_write"},
{Role: "action", Icon: "✓", Content: "shell · exit 0"},
{Role: "action", Icon: "⊞", Content: "granted file_write · global"},
{Role: "router", Content: "Done — script written and the healthcheck passes."},
}
if s := m.session("04a546aa"); s != nil {
s.CurrentStage = "execute_script"
s.LastEventAt = nowMillis() - 3000
}
case "help":
m.connected = true
m.currentModel = "llama-cpp:default"
m.sessions = sampleSessions()
m.selectedID = "04a546aa"
m.sessionEntered = true
m.overlay = OverlayHelp
case "events-filter":
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.Events = sampleEvents()
}
m.overlay = OverlayEventInspector
m.eventFilter = "tool"
m.eventFilterTyping = true
case "grants":
m.connected = true
m.currentModel = "llama-cpp:default"
m.sessions = sampleSessions()
m.selectedID = "04a546aa"
m.sessionEntered = true
m.grants = []protocol.GrantDto{
{GrantID: "g-1", Scope: "GLOBAL", ToolName: "read_file", Tiers: []string{"T1", "T2"}},
{GrantID: "g-2", Scope: "PROJECT", ToolName: "file_write", Tiers: []string{"T3"}, ProjectID: "/home/kami/Programs/correx"},
{GrantID: "g-3", Scope: "GLOBAL", ToolName: "shell", Tiers: []string{"T3", "T4"}},
}
m.grantIndex = 1
m.overlay = OverlayGrants
}
return m.View()
}
func sampleStats() *protocol.StatsDto {
return &protocol.StatsDto{
SessionID: "04a546aa",
EventCount: 28,
SessionDurationMs: 204_000,
InferenceCount: 5,
InferenceMs: 38_400,
PromptTokens: 4120,
CompletionTokens: 1860,
TokensPerSecond: 48.4,
PerProvider: []protocol.ProviderStatsDto{
{Provider: "llama-cpp:qwen2.5-coder-14b", CompletedCount: 4, TotalLatencyMs: 33_200, PromptTokens: 3800, CompletionTokens: 1700, TokensPerSecond: 51.2},
{Provider: "llama-cpp:default", CompletedCount: 1, TotalLatencyMs: 5200, PromptTokens: 320, CompletionTokens: 160, TokensPerSecond: 30.8},
},
ToolCount: 6,
ToolMs: 4300,
PerTool: []protocol.ToolStatsDto{
{ToolName: "file_write", CompletedCount: 2, TotalDurationMs: 2600},
{ToolName: "read_file", CompletedCount: 3, TotalDurationMs: 1200},
{ToolName: "shell_exec", CompletedCount: 1, TotalDurationMs: 500},
},
ApprovalsRequested: 2,
ApprovalsResolved: 2,
ApprovalsPending: 0,
ApprovalWaitMs: 45_000,
AvgApprovalWaitMs: 22_500,
PerTier: []protocol.TierApprovalStatsDto{
{Tier: "T2", RequestedCount: 1, ResolvedCount: 1, TotalWaitMs: 15_000, AvgWaitMs: 15_000},
{Tier: "T3", RequestedCount: 1, ResolvedCount: 1, TotalWaitMs: 30_000, AvgWaitMs: 30_000},
},
Failures: protocol.FailureMetricsDto{ToolFailures: 1},
InferencePct: 18.8,
ToolPct: 2.1,
ApprovalWaitPct: 22.1,
}
}
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"},
}
}