package tui import ( "fmt" "strings" "proxyctl/internal/app" "proxyctl/internal/profile" ) func (p terminalPresentation) mainView(model presentationState, width, height int) terminalPage { activeProfile := model.snapshot.ActiveProfile if activeProfile == "" { activeProfile = "No Active Profile" } header := fmt.Sprintf("%s Operations Console\nActive Profile: %s Targets: %s", p.theme.title.Render("proxyctl"), activeProfile, p.stateCountSummary(model.snapshot)) if width < wideWidth { header = fmt.Sprintf("%s Operations Console\nActive Profile: %s\nTargets: %s", p.theme.title.Render("proxyctl"), activeProfile, p.stateCountSummary(model.snapshot)) } catalog := newTargetCatalog(model.snapshot, model.showUnavailable, model.selectedTargetID) footer := p.dashboardFooter(model, catalog) if model.confirmation != nil && !model.operationActive { footer = "y: confirm Enter/n/Esc: Cancel q: exit" } if model.currentError != nil { footer += "\ne: expand/collapse current error x: acknowledge error" if model.errorExpanded { footer += " PgUp/PgDn: scroll error details" } } page := terminalPage{ header: header, footer: footer, dashboard: &dashboardRenderState{ catalog: catalog, selectedTargetID: model.selectedTargetID, focus: model.dashboardFocus, detailsOffset: model.detailsOffset, operationProgress: model.operationProgress, }, } if model.driftResolutionTargetID != "" && model.confirmation == nil { if selected, ok := newTargetCatalog(model.snapshot, true, model.driftResolutionTargetID).selected(model.driftResolutionTargetID); ok { page.dashboard = nil page.primary, page.context = p.driftResolutionView(selected) page.footer = "r: Reapply Active Profile o: Restore Original\nEnter/Esc: Cancel" } } if model.confirmation != nil { var details strings.Builder details.WriteString(p.theme.section.Render(model.confirmation.Title)) if model.confirmation.Summary != "" { fmt.Fprintf(&details, "\n%s", model.confirmation.Summary) } for _, detail := range model.confirmation.Details { fmt.Fprintf(&details, "\n• %s", detail) } page.dashboard = nil page.primary = details.String() page.context = "Cancellation is selected by default." } return page } func (p terminalPresentation) driftResolutionView(selected app.TargetSnapshot) (string, string) { var choices strings.Builder fmt.Fprintf(&choices, "%s\nChoose how to resolve %s without an automatic recovery direction.", p.theme.section.Render("Drift Resolution — "+selected.Description.Name), selected.Description.Name) for _, kind := range []app.TargetActionKind{app.ReapplyActiveProfileAction, app.RestoreOriginalAction} { action, ok := driftResolutionAction(selected, kind) label := targetActionLabel(kind) if !ok || !action.Available { reason := action.DisabledReason if reason == "" { reason = "unavailable" } fmt.Fprintf(&choices, "\n\n%s — unavailable: %s", label, reason) continue } fmt.Fprintf(&choices, "\n\n%s", label) } context := "Reapply Active Profile writes the Active Profile again. Restore Original asks the Target Adapter to use its existing ownership and restoration safeguards; insufficient evidence fails safely without overwriting external changes." return choices.String(), context } func (p terminalPresentation) dashboardFooter(model presentationState, catalog targetCatalog) string { if model.exitDeferred { return "Exit pending; browsing remains safe while the current mutation completes" } visibility := "u: show unavailable" if model.showUnavailable { visibility = "u: hide unavailable" } if model.dashboardFocus == targetDetailsPane { footer := "↑/k ↓/j PgUp/PgDn: scroll Tab: focus Targets " + visibility + "\np: manage Proxy Profiles ?: help q: quit" if model.operationActive { footer = "↑/k ↓/j PgUp/PgDn: scroll Tab: focus Targets ?: help\nMutations disabled: wait for the current mutation to finish" } return footer } actions := "↑/k ↓/j: select Tab: focus details " + visibility selected, ok := catalog.selected(model.selectedTargetID) availability := targetMutationAvailability(selected, ok, model.operationActive || model.exitDeferred) label := targetActionLabel(selected.PrimaryAction.Kind) if availability.available { actions = "Space: " + label + " " + actions } else { actions = "Space: " + label + " disabled (" + availability.compactReason + ") " + actions } footer := actions + "\np: manage Proxy Profiles r: refresh ?: help q: quit" if model.operationActive { footer = actions + "\n?: help q: defer safe exit; new mutations disabled" } return footer } 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.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, } }