feat(tui): token metrics aggregation, plan overlay, tool reasoning, clar dismissal
#295 — include narration_llm tokens in changes-panel aggregate count #296 — execution plan overlay (Ctrl+P in-session) #298 — surface reasoning/CoT on tool-call turns in output view #265 — dismiss clarification modal when answered externally (session.resumed)
This commit is contained in:
@@ -141,6 +141,8 @@ func (m Model) renderOverlay(base string) string {
|
||||
return m.center(m.grantsModal())
|
||||
case OverlayGrantScope:
|
||||
return m.center(m.grantScopeModal())
|
||||
case OverlayPlan:
|
||||
return m.center(m.planModal())
|
||||
case OverlayHelp:
|
||||
return m.center(m.helpModal())
|
||||
}
|
||||
@@ -926,6 +928,72 @@ func (m Model) toolPaletteModal() string {
|
||||
return m.center(modal)
|
||||
}
|
||||
|
||||
func (m Model) planModal() string {
|
||||
t := m.theme
|
||||
w := m.modalWidth()
|
||||
s := m.session(m.selectedID)
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString(m.titleLine("execution plan"))
|
||||
if s != nil {
|
||||
b.WriteString(mbg(t, " — "+s.WorkflowID, t.P.Dim))
|
||||
}
|
||||
b.WriteString("\n\n")
|
||||
|
||||
if s == nil || len(s.PlanStages) == 0 {
|
||||
b.WriteString(mbg(t, " no execution plan yet — stages appear once the plan is locked", t.P.Faint) + "\n")
|
||||
if s != nil && s.CurrentStage != "" {
|
||||
b.WriteString(mbg(t, " current stage: "+s.CurrentStage, t.P.Accent2) + "\n")
|
||||
}
|
||||
} else {
|
||||
goal := strings.TrimSpace(s.PlanGoal)
|
||||
if goal != "" {
|
||||
b.WriteString(mbg(t, " goal", t.P.Faint) + "\n")
|
||||
for _, ln := range wrap(goal, w-8) {
|
||||
b.WriteString(mbg(t, " "+ln, t.P.Fg) + "\n")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
}
|
||||
|
||||
b.WriteString(mbg(t, " stages ("+itoa(len(s.PlanStages))+" total)", t.P.Faint) + "\n")
|
||||
idW := 0
|
||||
for _, ps := range s.PlanStages {
|
||||
if len(ps.ID) > idW {
|
||||
idW = len(ps.ID)
|
||||
}
|
||||
}
|
||||
for _, ps := range s.PlanStages {
|
||||
var badge string
|
||||
switch ps.Status {
|
||||
case PlanPending:
|
||||
badge = mbg(t, " ○ pending", t.P.Faint)
|
||||
case PlanRunning:
|
||||
badge = lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.BgPanel).Bold(true).Render(" ● running")
|
||||
case PlanCompleted:
|
||||
badge = lipgloss.NewStyle().Foreground(t.P.OK).Background(t.P.BgPanel).Render(" ✓ done")
|
||||
case PlanFailed:
|
||||
badge = lipgloss.NewStyle().Foreground(t.P.Bad).Background(t.P.BgPanel).Render(" ✗ failed")
|
||||
}
|
||||
stageLine := mbg(t, " ", t.P.BgPanel) + badge + mbg(t, " "+padRaw(ps.ID, idW), t.P.Fg)
|
||||
if s.ToolsByStage != nil {
|
||||
if tools, ok := s.ToolsByStage[ps.ID]; ok && len(tools) > 0 {
|
||||
stageLine += mbg(t, " ["+itoa(len(tools))+" tools]", t.P.Faint)
|
||||
}
|
||||
}
|
||||
if budget, ok := s.TokenBudgetByStage[ps.ID]; ok && budget > 0 {
|
||||
used := 0
|
||||
if ps.ID == s.CurrentStage {
|
||||
used = s.StageTokensUsed
|
||||
}
|
||||
stageLine += mbg(t, " ("+itoa(used)+"/"+itoa(budget)+" tok)", t.P.Faint)
|
||||
}
|
||||
b.WriteString(stageLine + "\n")
|
||||
}
|
||||
}
|
||||
b.WriteString("\n" + modalHints(t, [][2]string{{"esc", "close"}}))
|
||||
return t.Overlay.Width(w).Render(b.String())
|
||||
}
|
||||
|
||||
func (m Model) modelsModal() string {
|
||||
t := m.theme
|
||||
w := m.modalWidth()
|
||||
|
||||
Reference in New Issue
Block a user