diff --git a/bin/analyze b/bin/analyze new file mode 100755 index 0000000..2dfe9d6 Binary files /dev/null and b/bin/analyze differ diff --git a/bin/analyze-go b/bin/analyze-go index 7625fbc..5cda9c4 100755 Binary files a/bin/analyze-go and b/bin/analyze-go differ diff --git a/bin/status-go b/bin/status-go index c1199b3..1019492 100755 Binary files a/bin/status-go and b/bin/status-go differ diff --git a/cmd/analyze/constants.go b/cmd/analyze/constants.go index 2f83f2f..9a5225b 100644 --- a/cmd/analyze/constants.go +++ b/cmd/analyze/constants.go @@ -7,8 +7,7 @@ const ( maxLargeFiles = 30 barWidth = 24 minLargeFileSize = 100 << 20 // 100 MB - entryViewport = 12 - largeViewport = 12 + defaultViewport = 12 // Default viewport when terminal height is unknown overviewCacheTTL = 7 * 24 * time.Hour // 7 days overviewCacheFile = "overview_sizes.json" duTimeout = 60 * time.Second // Increased for large directories diff --git a/cmd/analyze/main.go b/cmd/analyze/main.go index eb4e312..a6315cb 100644 --- a/cmd/analyze/main.go +++ b/cmd/analyze/main.go @@ -108,6 +108,8 @@ type model struct { overviewCurrentPath *string overviewScanning bool overviewScanningSet map[string]bool // Track which paths are currently being scanned + width int // Terminal width + height int // Terminal height } func (m model) inOverviewMode() bool { @@ -379,6 +381,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: return m.updateKey(msg) + case tea.WindowSizeMsg: + m.width = msg.Width + m.height = msg.Height + return m, nil case deleteProgressMsg: if msg.done { m.deleting = false @@ -560,14 +566,16 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { if m.showLargeFiles { if m.largeSelected < len(m.largeFiles)-1 { m.largeSelected++ - if m.largeSelected >= m.largeOffset+largeViewport { - m.largeOffset = m.largeSelected - largeViewport + 1 + viewport := calculateViewport(m.height, true) + if m.largeSelected >= m.largeOffset+viewport { + m.largeOffset = m.largeSelected - viewport + 1 } } } else if len(m.entries) > 0 && m.selected < len(m.entries)-1 { m.selected++ - if m.selected >= m.offset+entryViewport { - m.offset = m.selected - entryViewport + 1 + viewport := calculateViewport(m.height, false) + if m.selected >= m.offset+viewport { + m.offset = m.selected - viewport + 1 } } case "enter": @@ -863,11 +871,12 @@ func (m model) View() string { if len(m.largeFiles) == 0 { fmt.Fprintln(&b, " No large files found (>=100MB)") } else { + viewport := calculateViewport(m.height, true) start := m.largeOffset if start < 0 { start = 0 } - end := start + largeViewport + end := start + viewport if end > len(m.largeFiles) { end = len(m.largeFiles) } @@ -994,11 +1003,12 @@ func (m model) View() string { } } + viewport := calculateViewport(m.height, false) start := m.offset if start < 0 { start = 0 } - end := start + entryViewport + end := start + viewport if end > len(m.entries) { end = len(m.entries) } @@ -1111,7 +1121,8 @@ func (m *model) clampEntrySelection() { if m.selected < 0 { m.selected = 0 } - maxOffset := len(m.entries) - entryViewport + viewport := calculateViewport(m.height, false) + maxOffset := len(m.entries) - viewport if maxOffset < 0 { maxOffset = 0 } @@ -1121,8 +1132,8 @@ func (m *model) clampEntrySelection() { if m.selected < m.offset { m.offset = m.selected } - if m.selected >= m.offset+entryViewport { - m.offset = m.selected - entryViewport + 1 + if m.selected >= m.offset+viewport { + m.offset = m.selected - viewport + 1 } } @@ -1138,7 +1149,8 @@ func (m *model) clampLargeSelection() { if m.largeSelected < 0 { m.largeSelected = 0 } - maxOffset := len(m.largeFiles) - largeViewport + viewport := calculateViewport(m.height, true) + maxOffset := len(m.largeFiles) - viewport if maxOffset < 0 { maxOffset = 0 } @@ -1148,8 +1160,8 @@ func (m *model) clampLargeSelection() { if m.largeSelected < m.largeOffset { m.largeOffset = m.largeSelected } - if m.largeSelected >= m.largeOffset+largeViewport { - m.largeOffset = m.largeSelected - largeViewport + 1 + if m.largeSelected >= m.largeOffset+viewport { + m.largeOffset = m.largeSelected - viewport + 1 } } @@ -1226,3 +1238,29 @@ func scanOverviewPathCmd(path string, index int) tea.Cmd { } } } + +// calculateViewport dynamically calculates the viewport size based on terminal height +func calculateViewport(termHeight int, isLargeFiles bool) int { + if termHeight <= 0 { + // Terminal height unknown, use default + return defaultViewport + } + + // Calculate reserved space for UI elements + reserved := 6 // header (3-4 lines) + footer (2 lines) + if isLargeFiles { + reserved = 5 // Large files view has less overhead + } + + available := termHeight - reserved + + // Ensure minimum and maximum bounds + if available < 1 { + return 1 // Minimum 1 line for very short terminals + } + if available > 30 { + return 30 // Maximum 30 lines to avoid information overload + } + + return available +} diff --git a/lib/menu_paginated.sh b/lib/menu_paginated.sh index df44754..14612fc 100755 --- a/lib/menu_paginated.sh +++ b/lib/menu_paginated.sh @@ -15,6 +15,44 @@ leave_alt_screen() { fi } +# Get terminal height with fallback +_pm_get_terminal_height() { + local height=0 + + # Try stty size first (most reliable, real-time) + # Use /dev/null | awk '{print $1}') + fi + + # Fallback to tput + if [[ -z "$height" || $height -le 0 ]]; then + if command -v tput > /dev/null 2>&1; then + height=$(tput lines 2>/dev/null || echo "24") + else + height=24 + fi + fi + + echo "$height" +} + +# Calculate dynamic items per page based on terminal height +_pm_calculate_items_per_page() { + local term_height=$(_pm_get_terminal_height) + local reserved=6 # header(2) + footer(3) + spacing(1) + local available=$((term_height - reserved)) + + # Ensure minimum and maximum bounds + if [[ $available -lt 1 ]]; then + echo 1 + elif [[ $available -gt 50 ]]; then + echo 50 + else + echo "$available" + fi +} + # Parse CSV into newline list (Bash 3.2) _pm_parse_csv_to_array() { local csv="${1:-}" @@ -44,7 +82,7 @@ paginated_multi_select() { fi local total_items=${#items[@]} - local items_per_page=12 + local items_per_page=$(_pm_calculate_items_per_page) local cursor_pos=0 local top_index=0 local filter_query="" @@ -336,6 +374,9 @@ paginated_multi_select() { # Draw the complete menu draw_menu() { + # Recalculate items_per_page dynamically to handle window resize + items_per_page=$(_pm_calculate_items_per_page) + printf "\033[H" >&2 local clear_line="\r\033[2K" @@ -353,7 +394,7 @@ paginated_multi_select() { if [[ $visible_total -eq 0 ]]; then if [[ "$filter_mode" == "true" ]]; then # While editing: do not show "No items available" - for ((i = 0; i < items_per_page + 2; i++)); do + for ((i = 0; i < items_per_page; i++)); do printf "${clear_line}\n" >&2 done printf "${clear_line}${GRAY}Type to filter | Delete | Enter | / Exit | ESC${NC}\n" >&2 @@ -362,7 +403,7 @@ paginated_multi_select() { else if [[ "$searching" == "true" ]]; then printf "${clear_line}Searching…\n" >&2 - for ((i = 0; i < items_per_page + 2; i++)); do + for ((i = 0; i < items_per_page; i++)); do printf "${clear_line}\n" >&2 done printf "${clear_line}${GRAY}${ICON_NAV_UP}${ICON_NAV_DOWN} | Space | Enter | / Filter | Q Exit${NC}\n" >&2 @@ -371,7 +412,7 @@ paginated_multi_select() { else # Post-search: truly empty list printf "${clear_line}No items available\n" >&2 - for ((i = 0; i < items_per_page + 2; i++)); do + for ((i = 0; i < items_per_page; i++)); do printf "${clear_line}\n" >&2 done printf "${clear_line}${GRAY}${ICON_NAV_UP}${ICON_NAV_DOWN} | Space | Enter | / Filter | Q Exit${NC}\n" >&2 diff --git a/lib/menu_simple.sh b/lib/menu_simple.sh index 829ebd6..17420a2 100755 --- a/lib/menu_simple.sh +++ b/lib/menu_simple.sh @@ -7,6 +7,45 @@ set -euo pipefail enter_alt_screen() { tput smcup 2> /dev/null || true; } leave_alt_screen() { tput rmcup 2> /dev/null || true; } +# Get terminal height with fallback +_ms_get_terminal_height() { + local height=0 + + # Try stty size first (most reliable, real-time) + # Use /dev/null | awk '{print $1}') + fi + + # Fallback to tput + if [[ -z "$height" || $height -le 0 ]]; then + if command -v tput > /dev/null 2>&1; then + height=$(tput lines 2>/dev/null || echo "24") + else + height=24 + fi + fi + + echo "$height" +} + +# Calculate dynamic items per page based on terminal height +_ms_calculate_items_per_page() { + local term_height=$(_ms_get_terminal_height) + # Layout: header(1) + spacing(1) + items + spacing(1) + footer(1) + clear(1) = 5 fixed lines + local reserved=6 # Increased to prevent header from being overwritten + local available=$((term_height - reserved)) + + # Ensure minimum and maximum bounds + if [[ $available -lt 1 ]]; then + echo 1 + elif [[ $available -gt 50 ]]; then + echo 50 + else + echo "$available" + fi +} + # Main paginated multi-select menu function paginated_multi_select() { local title="$1" @@ -24,7 +63,7 @@ paginated_multi_select() { fi local total_items=${#items[@]} - local items_per_page=12 + local items_per_page=$(_ms_calculate_items_per_page) local cursor_pos=0 local top_index=0 local -a selected=() @@ -106,6 +145,9 @@ paginated_multi_select() { # Draw the complete menu draw_menu() { + # Recalculate items_per_page dynamically to handle window resize + items_per_page=$(_ms_calculate_items_per_page) + # Move to home position without clearing (reduces flicker) printf "\033[H" >&2 diff --git a/lib/whitelist_manager.sh b/lib/whitelist_manager.sh index c753320..249d223 100755 --- a/lib/whitelist_manager.sh +++ b/lib/whitelist_manager.sh @@ -202,12 +202,6 @@ manage_whitelist() { } manage_whitelist_categories() { - clear - echo "" - echo -e "${PURPLE}Whitelist Manager${NC}" - echo "" - echo "" - # Load currently enabled patterns from both sources load_whitelist