Merge mature Profile editor with contextual Help
This commit is contained in:
+40
-102
@@ -21,14 +21,6 @@ const (
|
||||
formPage
|
||||
)
|
||||
|
||||
type formState struct {
|
||||
originalName string
|
||||
profile profile.Profile
|
||||
field int
|
||||
probing bool
|
||||
probeGen uint64
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
application app.InteractiveModule
|
||||
presentation terminalPresentation
|
||||
@@ -211,6 +203,8 @@ func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.form.profile.HTTPURL = httpURL
|
||||
m.form.profile.HTTPSURL = httpURL
|
||||
m.form.profile.AllURL = strings.Replace(socksURL, "socks5h://", "socks5://", 1)
|
||||
m.form.advancedInitialized = true
|
||||
m.form.rebuildInputs(1)
|
||||
m.status = "Protocol Probe: detected HTTP and SOCKS5 proxy at " + done.address
|
||||
return m, nil
|
||||
}
|
||||
@@ -220,6 +214,7 @@ func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
m.form.profile.SingleURL = rewritten
|
||||
m.form.rebuildInputs(1)
|
||||
kind := "SOCKS5"
|
||||
if done.caps.HTTP {
|
||||
kind = "HTTP"
|
||||
@@ -500,7 +495,7 @@ func (m Model) updateProfiles(key tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
if !m.profileMutationAvailable("creating a Proxy Profile") {
|
||||
break
|
||||
}
|
||||
m.form = formState{profile: profile.Profile{Mode: profile.SingleMode}}
|
||||
m.form = newFormState("", profile.Profile{Mode: profile.SingleMode})
|
||||
m.page = formPage
|
||||
m.status = ""
|
||||
case "e", "enter":
|
||||
@@ -508,7 +503,7 @@ func (m Model) updateProfiles(key tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
break
|
||||
}
|
||||
if selected, ok := m.selectedProfile(); ok {
|
||||
m.form = formState{originalName: selected.Name, profile: selected}
|
||||
m.form = newFormState(selected.Name, selected)
|
||||
m.page = formPage
|
||||
m.status = ""
|
||||
}
|
||||
@@ -554,19 +549,39 @@ func newProfileDeleteConfirmation(selected profile.Profile, final, active bool)
|
||||
}
|
||||
|
||||
func (m Model) updateForm(key tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
switch key.String() {
|
||||
case "q", "esc":
|
||||
if m.form.currentField() == profile.BypassField {
|
||||
switch key.String() {
|
||||
case "pgup":
|
||||
m.form.scrollBypassPreview(-5)
|
||||
return m, nil
|
||||
case "pgdown":
|
||||
m.form.scrollBypassPreview(5)
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
if key.String() == "q" || key.Type == tea.KeyEsc {
|
||||
m.page = profilesPage
|
||||
m.status = ""
|
||||
m.form = formState{}
|
||||
return m, nil
|
||||
case "ctrl+s":
|
||||
}
|
||||
switch key.Type {
|
||||
case tea.KeyCtrlS:
|
||||
m.form.syncFocusedValue()
|
||||
m.form.refreshValidation()
|
||||
if err := m.form.validation.Err(); err != nil || m.form.nameConflict(m.snapshot.Profiles) != "" {
|
||||
m.status = "Fix the highlighted Profile fields before saving"
|
||||
m.form.probeGen++
|
||||
m.form.probing = false
|
||||
return m, nil
|
||||
}
|
||||
kind := app.CreateProfile
|
||||
if m.form.originalName != "" {
|
||||
kind = app.UpdateProfile
|
||||
}
|
||||
intent := app.Intent{Kind: kind, Name: m.form.originalName, Profile: m.form.profile}
|
||||
result := m.applyProfileMutation(intent, intent.Profile.Name)
|
||||
normalized := m.form.validation.Normalized
|
||||
intent := app.Intent{Kind: kind, Name: m.form.originalName, Profile: normalized}
|
||||
result := m.applyProfileMutation(intent, normalized.Name)
|
||||
if result.Err != nil {
|
||||
m.form.probeGen++ // discard any in-flight probe result while staying on the form
|
||||
m.form.probing = false
|
||||
@@ -575,12 +590,12 @@ func (m Model) updateForm(key tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
m.page = profilesPage
|
||||
m.form = formState{}
|
||||
return m, nil
|
||||
case "ctrl+d":
|
||||
case tea.KeyCtrlD:
|
||||
if m.form.profile.Mode != profile.SingleMode {
|
||||
m.status = "Protocol Probe: available in Single mode only"
|
||||
return m, nil
|
||||
}
|
||||
if m.form.field != 1 || m.form.probing {
|
||||
if m.form.currentField() != profile.SingleURLField || m.form.probing {
|
||||
return m, nil
|
||||
}
|
||||
address, err := probeAddress(m.form.profile.SingleURL)
|
||||
@@ -597,34 +612,15 @@ func (m Model) updateForm(key tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
caps, err := detect(context.Background(), address)
|
||||
return probeDone{address: address, caps: caps, err: err, gen: gen}
|
||||
}
|
||||
case "ctrl+t":
|
||||
if m.form.profile.Mode == profile.SingleMode {
|
||||
m.form.profile.Mode = profile.AdvancedMode
|
||||
m.form.profile.HTTPURL = m.form.profile.SingleURL
|
||||
m.form.profile.HTTPSURL = m.form.profile.SingleURL
|
||||
m.form.profile.AllURL = m.form.profile.SingleURL
|
||||
} else {
|
||||
m.form.profile.Mode = profile.SingleMode
|
||||
}
|
||||
m.form.field = 0
|
||||
return m, nil
|
||||
case "tab", "down":
|
||||
m.form.field = (m.form.field + 1) % m.form.fieldCount()
|
||||
return m, nil
|
||||
case "shift+tab", "up":
|
||||
m.form.field = (m.form.field - 1 + m.form.fieldCount()) % m.form.fieldCount()
|
||||
return m, nil
|
||||
case "backspace":
|
||||
value := []rune(m.form.value())
|
||||
if len(value) > 0 {
|
||||
m.form.setValue(string(value[:len(value)-1]))
|
||||
}
|
||||
return m, nil
|
||||
case tea.KeyCtrlT:
|
||||
return m, m.form.toggleMode()
|
||||
case tea.KeyTab, tea.KeyDown:
|
||||
return m, m.form.move(1)
|
||||
case tea.KeyShiftTab, tea.KeyUp:
|
||||
return m, m.form.move(-1)
|
||||
default:
|
||||
return m, m.form.updateInput(key)
|
||||
}
|
||||
if key.Type == tea.KeyRunes {
|
||||
m.form.setValue(m.form.value() + string(key.Runes))
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m Model) beginProfileActivation(intent app.Intent) (tea.Model, tea.Cmd) {
|
||||
@@ -754,61 +750,3 @@ func (m *Model) retainTargetSelection() {
|
||||
catalog := newTargetCatalog(m.snapshot, m.showUnavailable, m.selectedTargetID)
|
||||
m.selectedTargetID = catalog.retain(m.selectedTargetID, catalog.index(m.selectedTargetID))
|
||||
}
|
||||
|
||||
func (f formState) labels() []string {
|
||||
if f.profile.Mode == profile.AdvancedMode {
|
||||
return []string{"Name", "HTTP URL", "HTTPS URL", "ALL/SOCKS URL", "Bypass"}
|
||||
}
|
||||
return []string{"Name", "Single proxy URL", "Bypass"}
|
||||
}
|
||||
|
||||
func (f formState) fieldCount() int { return len(f.labels()) }
|
||||
|
||||
func (f formState) value() string { return f.valueAt(f.field) }
|
||||
|
||||
func (f formState) valueAt(index int) string {
|
||||
if index == 0 {
|
||||
return f.profile.Name
|
||||
}
|
||||
if f.profile.Mode == profile.AdvancedMode {
|
||||
switch index {
|
||||
case 1:
|
||||
return f.profile.HTTPURL
|
||||
case 2:
|
||||
return f.profile.HTTPSURL
|
||||
case 3:
|
||||
return f.profile.AllURL
|
||||
default:
|
||||
return strings.Join(f.profile.Bypass, ", ")
|
||||
}
|
||||
}
|
||||
if index == 1 {
|
||||
return f.profile.SingleURL
|
||||
}
|
||||
return strings.Join(f.profile.Bypass, ", ")
|
||||
}
|
||||
|
||||
func (f *formState) setValue(value string) {
|
||||
if f.field == 0 {
|
||||
f.profile.Name = value
|
||||
return
|
||||
}
|
||||
if f.profile.Mode == profile.AdvancedMode {
|
||||
switch f.field {
|
||||
case 1:
|
||||
f.profile.HTTPURL = value
|
||||
case 2:
|
||||
f.profile.HTTPSURL = value
|
||||
case 3:
|
||||
f.profile.AllURL = value
|
||||
default:
|
||||
f.profile.Bypass = []string{value}
|
||||
}
|
||||
return
|
||||
}
|
||||
if f.field == 1 {
|
||||
f.profile.SingleURL = value
|
||||
} else {
|
||||
f.profile.Bypass = []string{value}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user