nub/internal/ui/plain/plain.go
Tom a97013d876 initial commit
- v 0.1.0 siehe CHANGELOG.md
2026-07-25 11:08:01 +02:00

61 lines
1.6 KiB
Go

// Package plain ist der stdout-Renderer für den Print-Modus (5.8): Textdeltas
// nach stdout, Tool-Aktivität nach stderr. Reiner Consumer von tool.UIEvent.
package plain
import (
"fmt"
"io"
"nub/internal/tool"
)
type Renderer struct {
Out io.Writer
Err io.Writer
}
// Render konsumiert den Event-Channel, bis er schließt, und liefert den
// zuletzt gesehenen Fehler zurück (für die Exit-Code-Entscheidung in main).
func (r *Renderer) Render(events <-chan tool.UIEvent) error {
var lastErr error
openText := false
for ev := range events {
switch e := ev.(type) {
case tool.TextDelta:
fmt.Fprint(r.Out, e.Text)
openText = true
case tool.ThinkingDelta:
// v1: kein separates Rendering für Thinking im Print-Modus.
case tool.ToolCallStart:
if openText {
fmt.Fprintln(r.Out)
openText = false
}
fmt.Fprintf(r.Err, "→ %s %s\n", e.Name, string(e.Input))
case tool.ToolStream:
fmt.Fprint(r.Err, e.Text)
case tool.ToolCallOutput:
status := "ok"
if e.Result.IsError {
status = "error"
}
fmt.Fprintf(r.Err, "← %s [%s]\n", e.Name, status)
case tool.TurnDone:
if openText {
fmt.Fprintln(r.Out)
openText = false
}
if e.Usage.InputTokens > 0 || e.Usage.OutputTokens > 0 {
fmt.Fprintf(r.Err, "usage: input=%d output=%d cache_read=%d\n",
e.Usage.InputTokens, e.Usage.OutputTokens, e.Usage.CacheReadTokens)
}
case tool.ErrorEvent:
fmt.Fprintf(r.Err, "error: %v\n", e.Err)
lastErr = e.Err
case tool.CompactionEvent:
fmt.Fprintf(r.Err, "compacted %d nodes into a %d-token summary\n", e.ReplacedNodes, e.SummaryTokens)
}
}
return lastErr
}