feat(tui-go): interactive clarification question view
Render stage clarification questions as an AskUserQuestion-style form: header chip, prompt, selectable option chips, and a free-text custom slot. Arrow/hjkl nav, space to select, e for custom, enter to submit. Submit guards on all-answered and sends ClarificationResponse; the parked stage re-runs with the answers in context.
This commit is contained in:
@@ -12,6 +12,7 @@ const (
|
||||
StateIdle DisplayState = iota
|
||||
StateInSession
|
||||
StateApproval
|
||||
StateClarification
|
||||
)
|
||||
|
||||
// InputMode toggles what a submitted line targets.
|
||||
@@ -107,6 +108,23 @@ type Approval struct {
|
||||
Rationale []string
|
||||
}
|
||||
|
||||
// ClarQuestion is one open question a stage raised (mirrors the wire DTO).
|
||||
type ClarQuestion struct {
|
||||
ID string
|
||||
Prompt string
|
||||
Header string
|
||||
Options []string
|
||||
MultiSelect bool
|
||||
}
|
||||
|
||||
// Clarification is a stage's pending set of open questions for the operator.
|
||||
type Clarification struct {
|
||||
RequestID string
|
||||
SessionID string
|
||||
StageID string
|
||||
Questions []ClarQuestion
|
||||
}
|
||||
|
||||
// Session is the UI's view of one server session.
|
||||
type Session struct {
|
||||
ID string
|
||||
@@ -122,7 +140,8 @@ type Session struct {
|
||||
ToolsByStage map[string][]ManifestTool
|
||||
Events []EventEntry
|
||||
Pending *Approval
|
||||
Active bool // an inference/tool is in flight (drives the spinner)
|
||||
Clar *Clarification // open questions awaiting answers (clarification view)
|
||||
Active bool // an inference/tool is in flight (drives the spinner)
|
||||
}
|
||||
|
||||
// Workflow is a launchable workflow advertised by the server.
|
||||
@@ -229,6 +248,14 @@ type Model struct {
|
||||
// approval steering input buffer
|
||||
steerBuffer string
|
||||
steering bool
|
||||
|
||||
// clarification view (the interactive question form)
|
||||
clarFocus int // focused question index
|
||||
clarCursor int // option cursor in the focused question (== len(opts) → custom slot)
|
||||
clarChosen []map[int]bool // per-question selected option indices
|
||||
clarText []string // per-question free-text / custom answer
|
||||
clarTyping bool // typing into the custom buffer for the focused question
|
||||
clarDismissed bool // peeked away from the form (it can be reopened)
|
||||
}
|
||||
|
||||
// NewModel builds the initial idle state.
|
||||
@@ -257,10 +284,16 @@ func (m Model) displayState() DisplayState {
|
||||
if m.selectedID == "" || !m.sessionEntered {
|
||||
return StateIdle
|
||||
}
|
||||
s := m.session(m.selectedID)
|
||||
// A stage's open questions take precedence: the run is parked on them, and they
|
||||
// must be answered (or peeked away) before anything else makes sense.
|
||||
if s != nil && s.Clar != nil && !m.clarDismissed {
|
||||
return StateClarification
|
||||
}
|
||||
if m.approvalDismissed {
|
||||
return StateInSession
|
||||
}
|
||||
if s := m.session(m.selectedID); s != nil && s.Pending != nil {
|
||||
if s != nil && s.Pending != nil {
|
||||
return StateApproval
|
||||
}
|
||||
return StateInSession
|
||||
|
||||
Reference in New Issue
Block a user