fix: finish progressive Target detection

This commit is contained in:
proxyctl coordinator
2026-07-17 21:25:43 +08:00
parent ad651402fd
commit 8348fa3ac8
17 changed files with 672 additions and 221 deletions
+18 -27
View File
@@ -68,6 +68,7 @@ type refreshDone struct {
type targetDetectionProgress struct {
update app.TargetDetectionUpdate
updates <-chan app.TargetDetectionUpdate
closed bool
}
type profileOperationProgress struct {
@@ -90,7 +91,7 @@ func waitForTargetDetection(updates <-chan app.TargetDetectionUpdate) tea.Cmd {
return func() tea.Msg {
update, ok := <-updates
if !ok {
return targetDetectionProgress{update: app.TargetDetectionUpdate{Complete: true}, updates: updates}
return targetDetectionProgress{updates: updates, closed: true}
}
return targetDetectionProgress{update: update, updates: updates}
}
@@ -120,7 +121,7 @@ func NewModel(application app.Module, configure ...ModelOption) Model {
application: application,
presentation: terminalPresentation{theme: newSemanticTheme(options.theme, options.output)},
snapshot: snapshot,
selectedTargetID: newTargetCatalog(snapshot, false).firstID(),
selectedTargetID: newTargetCatalog(snapshot, false, "").firstID(),
width: 80,
height: 24,
refreshing: detectionUpdates != nil,
@@ -173,9 +174,17 @@ func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
if !m.refreshing || progress.updates != m.detectionUpdates {
return m, nil
}
if progress.update.Result.TargetID != "" {
m.applyTargetDetection(progress.update.Result)
if progress.closed {
// A stale Application generation closes without claiming an
// authoritative completion snapshot.
m.refreshing = false
m.detectionUpdates = nil
return m, m.deferredQuit()
}
formerCatalog := newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID)
formerIndex := formerCatalog.index(m.selectedTargetID)
m.snapshot = progress.update.Snapshot
m.selectedTargetID = newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID).retain(m.selectedTargetID, formerIndex)
if progress.update.Complete {
m.refreshing = false
m.detectionUpdates = nil
@@ -189,10 +198,10 @@ func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}
m.refreshing = false
formerCatalog := newTargetCatalog(m.snapshot, m.showUnavailable)
formerCatalog := newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID)
formerIndex := formerCatalog.index(m.selectedTargetID)
m.snapshot = completed.result.Snapshot
m.selectedTargetID = newTargetCatalog(m.snapshot, m.showUnavailable).retain(m.selectedTargetID, formerIndex)
m.selectedTargetID = newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID).retain(m.selectedTargetID, formerIndex)
if completed.result.Err != nil {
m.status = "Error: " + completed.result.Err.Error()
} else {
@@ -356,7 +365,7 @@ func (m Model) updateMain(key tea.KeyMsg) (tea.Model, tea.Cmd) {
}
return m, nil
}
catalog := newTargetCatalog(m.snapshot, m.showUnavailable)
catalog := newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID)
switch key.String() {
case "p":
if !m.busy() {
@@ -394,7 +403,7 @@ func (m Model) updateMain(key tea.KeyMsg) (tea.Model, tea.Cmd) {
if m.dashboardNavigationAvailable() {
formerIndex := catalog.index(m.selectedTargetID)
m.showUnavailable = !m.showUnavailable
m.selectedTargetID = newTargetCatalog(m.snapshot, m.showUnavailable).retain(m.selectedTargetID, formerIndex)
m.selectedTargetID = newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID).retain(m.selectedTargetID, formerIndex)
m.detailsOffset = 0
}
case "up", "k":
@@ -681,28 +690,10 @@ func (m Model) availableTargets() []app.TargetSnapshot {
}
func (m *Model) retainTargetSelection() {
catalog := newTargetCatalog(m.snapshot, m.showUnavailable)
catalog := newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID)
m.selectedTargetID = catalog.retain(m.selectedTargetID, catalog.index(m.selectedTargetID))
}
func (m *Model) applyTargetDetection(result app.TargetDetectionResult) {
formerCatalog := newTargetCatalog(m.snapshot, m.showUnavailable)
formerIndex := formerCatalog.index(m.selectedTargetID)
for i := range m.snapshot.Targets {
if m.snapshot.Targets[i].ID != result.TargetID {
continue
}
m.snapshot.Targets[i].DetectionPhase = result.Phase
m.snapshot.Targets[i].DetectionErr = result.Err
m.snapshot.Targets[i].Available = result.Availability.Available
m.snapshot.Targets[i].AvailabilityEvidence = result.Availability.Evidence
m.snapshot.Targets[i].State = result.Inspection.State
m.snapshot.Targets[i].Evidence = result.Inspection.Evidence
break
}
m.selectedTargetID = newTargetCatalog(m.snapshot, m.showUnavailable).retain(m.selectedTargetID, formerIndex)
}
func (f formState) labels() []string {
if f.profile.Mode == profile.AdvancedMode {
return []string{"Name", "HTTP URL", "HTTPS URL", "ALL/SOCKS URL", "Bypass"}