package tui import ( "fmt" "strings" "github.com/charmbracelet/lipgloss" "proxyctl/internal/profile" ) func (p terminalPresentation) mainView(model presentationState, width, height int) terminalPage { var header strings.Builder header.WriteString(p.theme.title.Render("proxyctl — Target panel")) header.WriteString("\n\n") if model.snapshot.ActiveProfile == "" { header.WriteString(p.theme.warning.Render("No Active Profile")) header.WriteString("\nCreate and activate a Proxy Profile before enabling Targets.") } else { fmt.Fprintf(&header, "Active Profile: %s", model.snapshot.ActiveProfile) } list, details, focusLine := p.targetRegions(model) page := terminalPage{ header: header.String(), primary: list, context: details, footer: "Space: toggle ↑/k ↓/j: select p: manage Proxy Profiles\nr: refresh ?: help q/Esc: quit", focusLine: focusLine, } if width >= wideWidth && height >= wideHeight { leftWidth := (width - 2) * 2 / 5 rightWidth := width - leftWidth - 2 left := lipgloss.NewStyle().Width(leftWidth).Render(list) right := lipgloss.NewStyle().Width(rightWidth).Render(details) page.primary = lipgloss.JoinHorizontal(lipgloss.Top, left, " ", right) page.context = "" } if model.targetConfirm && !model.applying { page.footer = "y: confirm n/Esc: cancel" } return page } func (p terminalPresentation) targetRegions(model presentationState) (listView, detailsView string, focusLine int) { targets := availableTargets(model.snapshot) var list strings.Builder list.WriteString(p.theme.selected.Render("Targets")) if len(targets) == 0 { list.WriteString("\nNo Targets are currently Available. Press ? to review detection reasons.") } for i, item := range targets { cursor := " " if i == model.selected { cursor = "> " } applying := (model.progressTarget != "" && item.ID == model.progressTarget) || (!model.profileTransition && model.progressTarget == "" && model.applying && i == model.selected) fmt.Fprintf(&list, "\n%s[%s] %s", cursor, p.theme.renderTargetState(item.State, applying), item.Description.Name) } var details strings.Builder details.WriteString(p.theme.section.Render("Target details")) if model.selected >= 0 && model.selected < len(targets) { selected := targets[model.selected] fmt.Fprintf(&details, "\n%s\n%s\n%s", selected.Description.Summary, selected.Description.Details, selected.Evidence) } else { details.WriteString("\nSelect an Available Target to review its details.") } return list.String(), details.String(), model.selected + 1 } func (p terminalPresentation) helpView(model presentationState) terminalPage { var body strings.Builder for _, item := range model.snapshot.Targets { fmt.Fprintf(&body, "%s\n%s\n%s\n", p.theme.section.Render(item.Description.Name), item.Description.Summary, item.Description.Details) if item.Available { fmt.Fprintf(&body, "Detected: %s\n\n", item.AvailabilityEvidence) } else { fmt.Fprintf(&body, "Not detected: %s\n\n", item.AvailabilityEvidence) } } return terminalPage{ header: p.theme.title.Render("proxyctl Help — built-in Targets"), primary: strings.TrimRight(body.String(), "\n"), footer: "↑/k ↓/j: navigate Space: toggle p: manage Proxy Profiles\nr: refresh ?: help q/Escape: back", focusLine: 0, } } func (p terminalPresentation) profilesView(model presentationState) terminalPage { var list strings.Builder if len(model.snapshot.Profiles) == 0 { list.WriteString("No Proxy Profiles. Press n to create one.") } for i, item := range model.snapshot.Profiles { cursor, active := " ", " " if i == model.selected { cursor = "> " } if item.Name == model.snapshot.ActiveProfile { active = "*" } if list.Len() > 0 { list.WriteByte('\n') } fmt.Fprintf(&list, "%s%s %s", cursor, active, item.Name) } 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 { context = "Delete Profile — this is the final Proxy Profile. Managed configuration will remain in place and become Drifted." } else { context = "Delete Profile?" } } return terminalPage{ header: p.theme.title.Render("Proxy Profiles"), primary: list.String(), context: context, footer: footer, focusLine: model.selected, } } func selectedProfileDetails(p terminalPresentation, model presentationState) string { selected, ok := profileAt(model.snapshot, model.selected) if !ok { return "" } var details strings.Builder details.WriteString(p.theme.section.Render("Selected Profile")) fmt.Fprintf(&details, "\nMode: %s", selected.Mode) if selected.Mode == profile.SingleMode { fmt.Fprintf(&details, "\nProxy: %s", profile.ObscureURL(selected.SingleURL)) } else { fmt.Fprintf(&details, "\nHTTP: %s\nHTTPS: %s\nALL/SOCKS: %s", profile.ObscureURL(selected.HTTPURL), profile.ObscureURL(selected.HTTPSURL), profile.ObscureURL(selected.AllURL)) } fmt.Fprintf(&details, "\nBypass: %s", strings.Join(selected.Bypass, ", ")) return details.String() } func (p terminalPresentation) formView(model presentationState) terminalPage { action := "Create" if model.form.originalName != "" { action = "Edit" } var fields strings.Builder for i, label := range model.form.labels() { cursor := " " if i == model.form.field { cursor = "> " } value := model.form.valueAt(i) if label != "Name" && label != "Bypass" { value = profile.ObscureURL(value) } if fields.Len() > 0 { fields.WriteByte('\n') } fmt.Fprintf(&fields, "%s%s: %s", cursor, label, value) } return terminalPage{ header: p.theme.title.Render(fmt.Sprintf("%s Proxy Profile — %s mode", action, model.form.profile.Mode)), primary: fields.String(), footer: "Tab/↑/↓: move Ctrl+T: toggle Single/Advanced\nCtrl+S: save Ctrl+D: detect protocol Esc: cancel", focusLine: model.form.field, } }