Render returns the full string representation of the pricing view. It uses a two-pass strategy: render all fixed-height sections first, measure their total line count, then give the remaining terminal height to the Results section so the UI always fills the screen.
(active bool, version string)
| 803 | // their total line count, then give the remaining terminal height to the |
| 804 | // Results section so the UI always fills the screen. |
| 805 | func (v *PricingView) Render(active bool, version string) string { |
| 806 | showSuggestions := active && v.ActiveSection == PricingSectionCommand |
| 807 | |
| 808 | // Pass 1: render every section except Results. |
| 809 | headerStr := v.renderHeader(active, version) |
| 810 | commandStr := v.renderCommand(active) |
| 811 | var suggestionsStr string |
| 812 | if showSuggestions { |
| 813 | suggestionsStr = v.renderSuggestions(active) |
| 814 | } |
| 815 | priceDetailsStr := v.renderPriceDetails() |
| 816 | helpStr := v.renderHelp() |
| 817 | |
| 818 | // Count lines consumed by fixed sections. strings.Join with "\n" does NOT |
| 819 | // add extra lines beyond the sum of each section's lineCount — the |
| 820 | // separator merely connects the last line of one section to the first |
| 821 | // line of the next, so total lines = Σ lineCount(section_i). |
| 822 | fixedLines := lineCount(headerStr) + lineCount(commandStr) + |
| 823 | lineCount(priceDetailsStr) + lineCount(helpStr) |
| 824 | if showSuggestions { |
| 825 | fixedLines += lineCount(suggestionsStr) |
| 826 | } |
| 827 | |
| 828 | // Results box must be at least 3 lines tall (top border + 1 content + bottom). |
| 829 | resultsMinHeight := v.Height - fixedLines |
| 830 | if resultsMinHeight < 3 { |
| 831 | resultsMinHeight = 3 |
| 832 | } |
| 833 | |
| 834 | // Derive ResultsPerPage from actual available inner height (subtract 2 |
| 835 | // border lines and 1 table-header row). Cap at 15 rows per page. |
| 836 | v.ResultsPerPage = resultsMinHeight - 3 |
| 837 | if v.ResultsPerPage > 15 { |
| 838 | v.ResultsPerPage = 15 |
| 839 | } |
| 840 | if v.ResultsPerPage < 1 { |
| 841 | v.ResultsPerPage = 1 |
| 842 | } |
| 843 | |
| 844 | // Keep ResultsPage valid and ensure the selected row stays visible. |
| 845 | if len(v.FilteredItems) > 0 { |
| 846 | selPage := v.Selected / v.ResultsPerPage |
| 847 | if selPage != v.ResultsPage { |
| 848 | v.ResultsPage = selPage |
| 849 | } |
| 850 | totalPages := (len(v.FilteredItems) + v.ResultsPerPage - 1) / v.ResultsPerPage |
| 851 | if v.ResultsPage >= totalPages { |
| 852 | v.ResultsPage = totalPages - 1 |
| 853 | } |
| 854 | } |
| 855 | |
| 856 | // Pass 2: render results with the computed minimum height. |
| 857 | resultsStr := v.renderResults(active, resultsMinHeight) |
| 858 | |
| 859 | sections := []string{headerStr, commandStr} |
| 860 | if showSuggestions { |
| 861 | sections = append(sections, suggestionsStr) |
| 862 | } |
no test coverage detected