pi-agent: Fix final integrated review

This commit is contained in:
proxyctl coordinator
2026-07-18 02:37:54 +08:00
parent ed8b527f1f
commit 652d7eba2b
18 changed files with 566 additions and 393 deletions
+58 -71
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"proxyctl/internal/app"
@@ -91,12 +92,7 @@ func targetsChecking(snapshot app.Snapshot) bool {
return false
}
func (m Model) Init() tea.Cmd {
if m.operation.updates != nil {
return waitForOperation(m.operation.updates)
}
return nil
}
func (m Model) Init() tea.Cmd { return m.operation.wait() }
// busy is the single-operation guard for asynchronous Application work. Every
// operation start checks it, so a completion can only belong to the one current
@@ -105,12 +101,6 @@ func (m Model) busy() bool { return m.operation.busy() }
func (m Model) operationInFlight() bool { return m.operation.active }
func (m Model) dashboardNavigationAvailable() bool {
// A deferred exit rejects new work, not read-only browsing. Selection,
// details, and Help remain useful until the current mutation is safe.
return true
}
func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
if size, ok := message.(tea.WindowSizeMsg); ok {
m.width = size.Width
@@ -141,6 +131,9 @@ func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}
}
if tick, ok := message.(spinner.TickMsg); ok {
return m, m.operation.tick(tick)
}
if progress, ok := message.(operationProgress); ok {
if !m.operation.active || progress.updates != m.operation.updates {
return m, nil
@@ -170,7 +163,7 @@ func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
if progress.update.IsTerminal() {
return m, m.operation.deferredQuit()
}
return m, waitForOperation(progress.updates)
return m, m.operation.wait()
}
if done, ok := message.(probeDone); ok {
return m, m.form.applyProbeResult(done.token, done.caps, done.err)
@@ -346,44 +339,36 @@ func (m Model) updateMain(key tea.KeyMsg) (tea.Model, tea.Cmd) {
return m, m.startOperation(app.Intent{Kind: app.RefreshTargets})
}
case "tab":
if m.dashboardNavigationAvailable() {
if m.dashboardFocus == targetListPane {
m.dashboardFocus = targetDetailsPane
} else {
m.dashboardFocus = targetListPane
}
if m.dashboardFocus == targetListPane {
m.dashboardFocus = targetDetailsPane
} else {
m.dashboardFocus = targetListPane
}
case "u":
if m.dashboardNavigationAvailable() {
formerIndex := catalog.index(m.selectedTargetID)
m.showUnavailable = !m.showUnavailable
m.selectedTargetID = newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID).retain(m.selectedTargetID, formerIndex)
formerIndex := catalog.index(m.selectedTargetID)
m.showUnavailable = !m.showUnavailable
m.selectedTargetID = newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID).retain(m.selectedTargetID, formerIndex)
m.detailsOffset = 0
case "up", "k":
if m.dashboardFocus == targetDetailsPane {
m.detailsOffset = max(0, m.detailsOffset-1)
} else {
m.selectedTargetID = catalog.move(m.selectedTargetID, -1)
m.detailsOffset = 0
}
case "up", "k":
if m.dashboardNavigationAvailable() {
if m.dashboardFocus == targetDetailsPane {
m.detailsOffset = max(0, m.detailsOffset-1)
} else {
m.selectedTargetID = catalog.move(m.selectedTargetID, -1)
m.detailsOffset = 0
}
}
case "down", "j":
if m.dashboardNavigationAvailable() {
if m.dashboardFocus == targetDetailsPane {
m.detailsOffset++
} else {
m.selectedTargetID = catalog.move(m.selectedTargetID, 1)
m.detailsOffset = 0
}
if m.dashboardFocus == targetDetailsPane {
m.detailsOffset++
} else {
m.selectedTargetID = catalog.move(m.selectedTargetID, 1)
m.detailsOffset = 0
}
case "pgup":
if m.dashboardNavigationAvailable() && m.dashboardFocus == targetDetailsPane {
if m.dashboardFocus == targetDetailsPane {
m.detailsOffset = max(0, m.detailsOffset-5)
}
case "pgdown":
if m.dashboardNavigationAvailable() && m.dashboardFocus == targetDetailsPane {
if m.dashboardFocus == targetDetailsPane {
m.detailsOffset += 5
}
case " ":
@@ -641,39 +626,41 @@ func (m *Model) ensureProfileSelectionVisible() {
func (m Model) View() string {
return m.presentation.Render(presentationState{
snapshot: m.snapshot,
page: m.page,
width: m.width,
height: m.height,
selected: m.selected,
profileOffset: m.profileViewportOffset,
profileDetails: m.profileDetailsOffset,
profileDelete: m.profileDelete,
selectedTargetID: m.selectedTargetID,
dashboardFocus: m.dashboardFocus,
showUnavailable: m.showUnavailable,
detailsOffset: m.detailsOffset,
driftResolutionTargetID: m.driftResolutionTargetID,
picker: m.picker,
form: m.form,
help: m.help,
confirmation: m.operation.confirmation,
confirmationDialog: &m.operation.confirmationDialog,
status: m.status,
operationStatus: m.operation.status,
currentError: m.operation.currentError,
errorExpanded: m.operation.errorExpanded,
errorOffset: m.operation.errorOffset,
exitDeferred: m.operation.quitRequested && m.operationInFlight(),
operationActive: m.operation.active,
operationProgress: m.operation.progress,
snapshot: m.snapshot,
page: m.page,
terminal: terminalDimensions{width: m.width, height: m.height},
dashboard: dashboardPresentationState{
selectedTargetID: m.selectedTargetID,
focus: m.dashboardFocus,
showUnavailable: m.showUnavailable,
detailsOffset: m.detailsOffset,
driftResolutionTargetID: m.driftResolutionTargetID,
picker: m.picker,
},
profile: profilePresentationState{
selected: m.selected,
listOffset: m.profileViewportOffset,
detailsOffset: m.profileDetailsOffset,
deletion: m.profileDelete,
status: m.status,
},
form: formPresentationState{state: m.form},
help: helpPresentationState{dialog: m.help},
operation: operationPresentationState{
confirmation: m.operation.confirmation,
confirmationDialog: &m.operation.confirmationDialog,
status: m.operation.status,
currentError: m.operation.currentError,
errorExpanded: m.operation.errorExpanded,
errorOffset: m.operation.errorOffset,
exitDeferred: m.operation.quitRequested && m.operationInFlight(),
active: m.operation.active,
progress: m.operation.progress,
spinnerFrame: m.operation.spinner.View(),
},
})
}
func (m Model) availableTargets() []app.TargetSnapshot {
return availableTargets(m.snapshot)
}
func (m *Model) retainTargetSelection() {
catalog := newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID)
m.selectedTargetID = catalog.retain(m.selectedTargetID, catalog.index(m.selectedTargetID))