feat: add searchable Active Profile picker

This commit is contained in:
proxyctl coordinator
2026-07-17 22:32:41 +08:00
parent 7c0b4481d7
commit 306b02331f
10 changed files with 483 additions and 22 deletions
+28 -3
View File
@@ -42,6 +42,7 @@ type Model struct {
dashboardFocus dashboardPane
showUnavailable bool
detailsOffset int
picker profilePickerState
form formState
confirm bool
status string
@@ -74,6 +75,7 @@ func NewModel(application app.InteractiveModule, configure ...ModelOption) Model
width: 80,
height: 24,
operation: newOperationCoordinator(),
picker: newProfilePickerState(),
detect: probe.Detect,
}
if targetsChecking(snapshot) {
@@ -126,9 +128,9 @@ func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}
if key, ok := message.(tea.KeyMsg); ok {
// Dashboard q always means exit, even when confirmation is visible.
// Escape remains the deterministic cancel/back key.
exitRequest := key.String() == "ctrl+c" || (m.page == mainPage && key.String() == "q")
// q exits the unobscured dashboard. While the searchable picker is open,
// it is ordinary filter input; Escape remains deterministic cancellation.
exitRequest := key.String() == "ctrl+c" || (m.page == mainPage && !m.picker.open && key.String() == "q")
if exitRequest {
return m, m.operation.requestQuit()
}
@@ -259,6 +261,22 @@ func targetMutationAvailability(selected app.TargetSnapshot, selectedOK bool, ac
}
func (m Model) updateMain(key tea.KeyMsg) (tea.Model, tea.Cmd) {
if m.picker.open {
switch key.String() {
case "esc":
m.picker.close()
return m, nil
case "enter":
selected, ok := m.picker.selected(m.snapshot)
if !ok {
return m, nil
}
m.picker.close()
return m.beginProfileActivation(app.Intent{Kind: app.ActivateProfile, Name: selected.Name})
default:
return m, m.picker.update(key, m.snapshot)
}
}
if m.operation.confirmation != nil {
switch key.String() {
case "y":
@@ -294,6 +312,12 @@ func (m Model) updateMain(key tea.KeyMsg) (tea.Model, tea.Cmd) {
}
catalog := newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID)
switch key.String() {
case "a":
if m.operation.active || m.operation.quitRequested {
m.operation.status = app.OperationStatus{Kind: app.StatusBlocked, Summary: "Wait for the current mutation to finish before changing the Active Profile"}
} else {
return m, m.picker.show(m.snapshot)
}
case "p":
if m.operation.active || m.operation.quitRequested {
m.operation.status = app.OperationStatus{Kind: app.StatusBlocked, Summary: "Wait for the current mutation to finish before opening Profile management"}
@@ -562,6 +586,7 @@ func (m Model) View() string {
dashboardFocus: m.dashboardFocus,
showUnavailable: m.showUnavailable,
detailsOffset: m.detailsOffset,
picker: m.picker,
form: m.form,
confirm: m.confirm,
confirmation: m.operation.confirmation,