feat: unify interactive operation coordination

This commit is contained in:
proxyctl coordinator
2026-07-17 22:23:31 +08:00
parent 177bf34354
commit 7c0b4481d7
17 changed files with 985 additions and 1013 deletions
+14 -16
View File
@@ -4,7 +4,6 @@ import (
"fmt"
"strings"
"proxyctl/internal/app"
"proxyctl/internal/profile"
)
@@ -22,11 +21,14 @@ func (p terminalPresentation) mainView(model presentationState, width, height in
catalog := newTargetCatalog(model.snapshot, model.showUnavailable, model.selectedTargetID)
footer := p.dashboardFooter(model, catalog)
if model.targetConfirm && !model.operationActive {
footer = "y: confirm Enter/n/Esc: Cancel"
if model.confirmation != nil && !model.operationActive {
footer = "y: confirm Enter/n/Esc: Cancel q: exit"
}
if model.currentError != nil {
footer += "\ne: expand/collapse current error x: acknowledge error"
if model.errorExpanded {
footer += " PgUp/PgDn: scroll error details"
}
}
page := terminalPage{
header: header,
@@ -36,9 +38,7 @@ func (p terminalPresentation) mainView(model presentationState, width, height in
selectedTargetID: model.selectedTargetID,
focus: model.dashboardFocus,
detailsOffset: model.detailsOffset,
applying: model.applying,
profileTransition: model.profileTransition,
progressTarget: model.progressTarget,
operationProgress: model.operationProgress,
},
}
if model.confirmation != nil {
@@ -66,21 +66,21 @@ func (p terminalPresentation) dashboardFooter(model presentationState, catalog t
visibility = "u: hide unavailable"
}
if model.dashboardFocus == targetDetailsPane {
footer := "↑/k ↓/j PgUp/PgDn: scroll Tab: focus Targets " + visibility + "\np: manage Proxy Profiles ?: help q/Esc: quit"
footer := "↑/k ↓/j PgUp/PgDn: scroll Tab: focus Targets " + visibility + "\np: manage Proxy Profiles ?: help q: quit"
if model.operationActive {
footer = "↑/k ↓/j PgUp/PgDn: scroll Tab: focus Targets ?: help\nMutations disabled: wait for the current mutation to finish"
}
return footer
}
actions := "↑/k ↓/j: select Tab: focus details " + visibility
if selected, ok := catalog.selected(model.selectedTargetID); ok && selected.Available && selected.DetectionPhase == app.TargetReady {
if model.operationActive {
actions = "Space: disabled (mutation in progress) " + actions
} else {
actions = "Space: toggle " + actions
}
selected, ok := catalog.selected(model.selectedTargetID)
availability := targetMutationAvailability(selected, ok, model.snapshot.ActiveProfile, model.operationActive || model.exitDeferred)
if availability.available {
actions = "Space: toggle " + actions
} else {
actions = "Space: disabled (" + availability.compactReason + ") " + actions
}
footer := actions + "\np: manage Proxy Profiles r: refresh ?: help q/Esc: quit"
footer := actions + "\np: manage Proxy Profiles r: refresh ?: help q: quit"
if model.operationActive {
footer = actions + "\n?: help q: defer safe exit; new mutations disabled"
}
@@ -127,8 +127,6 @@ func (p terminalPresentation) profilesView(model presentationState) terminalPage
context := selectedProfileDetails(p, model)
footer := "n: new e: edit d: delete a/Enter: activate\n↑/k ↓/j: select q/Esc: back"
switch {
case model.profileConfirm:
footer = "y: confirm n/Esc: cancel"
case model.confirm:
footer = "y: confirm n/Esc: cancel"
if len(model.snapshot.Profiles) == 1 {