Utoljára aktív 1 month ago

gistfile1.txt Eredeti
1#!/usr/bin/env bash
2
3UI_ROW_WIDTH=${UI_ROW_WIDTH:-20}
4UI_SECTION_WIDTH=${UI_SECTION_WIDTH:-44}
5
6function ui::row() {
7 local label="$1" value="$2" width="${3:-$UI_ROW_WIDTH}"
8 printf " %-${width}s %s\n" "${label}:" "$value"
9}
10
11function ui::section() {
12 local title="$1" width="${2:-$UI_SECTION_WIDTH}"
13 local dashes
14 dashes=$(printf '─%.0s' $(seq 1 $(( width - ${#title} - 4 ))))
15 printf "\n \033[0;37m── %s %s\033[0m\n" "$title" "$dashes"
16}
17
18function ui::list_item() {
19 local prefix="$1" value="$2"
20 printf " %s %s\n" "$prefix" "$value"
21}
22
23function ui::print_list() {
24 local prefix="$1" input="$2"
25 [[ -z "$input" ]] && return 0 # early return for empty input
26 while IFS= read -r e; do
27 [[ -n "$e" ]] && ui::list_item "$prefix" "$e"
28 done <<< "$input"
29}
30
31function ui::divider() {
32 local width="${1:-48}"
33 printf " %s\n" "$(printf '─%.0s' $(seq 1 $width))"
34}
35
36function ui::pad() {
37 local text="$1" width="${2:-20}"
38 local visible
39 visible=$(echo -e "$text" | sed 's/\x1b\[[0-9;]*m//g')
40 local pad=$(( width - ${#visible} ))
41 printf "%b%${pad}s" "$text" ""
42}
43
44function ui::pad_mb() {
45 local text="$1" width="${2:-20}"
46 local visible
47 visible=$(printf "%b" "$text" | sed 's/\x1b\[[0-9;]*m//g')
48 local vis_len
49 vis_len=$(python3 -c "import sys; print(len(sys.stdin.read().rstrip('\n')))" \
50 <<< "$visible")
51 local pad=$(( width - vis_len ))
52 [[ $pad -lt 0 ]] && pad=0
53 printf "%b%${pad}s" "$text" ""
54}
55
56function ui::vis_len_multi() {
57 # Get visible lengths of multiple strings in one Python call
58 # Returns newline-separated integers
59 python3 -c "
60import sys, re
61ansi = re.compile(r'\x1b\[[0-9;]*m')
62for s in sys.argv[1:]:
63 print(len(ansi.sub('', s)))
64" "$@"
65}
66
67# ui::vis_len <string>
68# Returns the visible (character) length of a string,
69# stripping ANSI codes and accounting for multi-byte UTF-8.
70function ui::vis_len() {
71 local str="${1:-}"
72 # Strip ANSI codes first
73 local clean
74 clean=$(echo "$str" | sed 's/\x1b\[[0-9;]*m//g')
75 # Use Python for accurate Unicode character count
76 python3 -c "import sys; print(len('${clean//\'/\'\\\'\'}'))" 2>/dev/null || echo "${#clean}"
77}
78
79# ui::pad_to_col <string_bytes_printed> <target_visible_col>
80# Returns padding needed accounting for UTF-8 byte/char difference.
81# extra_bytes = bytes_printed - visible_chars_printed
82function ui::utf8_extra_bytes() {
83 local str="${1:-}"
84 local byte_len=${#str}
85 local vis_len
86 vis_len=$(ui::vis_len "$str")
87 echo $(( byte_len - vis_len ))
88}
89
90
91function ui::pad_status() {
92 ui::pad "${1:-}" "${2:-25}"
93}
94
95function ui::center() {
96 local text="$1" width="${2:-8}"
97 local len=${#text}
98 local pad=$(( (width - len) / 2 ))
99 local rpad=$(( width - len - pad ))
100 printf "%${pad}s%s%${rpad}s" "" "$text" ""
101}
102
103# ui::measure_col <data> <field_index> [min_width]
104# Scans pipe-delimited data and returns the max visible width
105# of the field at field_index (1-based), with optional minimum.
106# Strips ANSI codes before measuring.
107# Usage:
108# name_width=$(ui::measure_col "$data" 1 10)
109# ip_width=$(ui::measure_col "$data" 2 14)
110function ui::measure_col() {
111 local data="${1:-}" field_index="${2:-1}" min_width="${3:-0}"
112 local max=$min_width
113
114 while IFS='|' read -r line; do
115 local val
116 val=$(echo "$line" | cut -d'|' -f"$field_index")
117 # Strip ANSI codes for accurate measurement
118 local clean
119 clean=$(echo "$val" | sed 's/\x1b\[[0-9;]*m//g')
120 local len=${#clean}
121 (( len > max )) && max=$len
122 done <<< "$data"
123
124 echo $max
125}
126
127# ui::measure_cols <data> <field_indices...>
128# Measure multiple columns at once, returns space-separated widths.
129# Usage: read -r w1 w2 w3 <<< $(ui::measure_cols "$data" 1 2 3)
130function ui::measure_cols() {
131 local data="${1:-}"
132 shift
133 local widths=()
134 for idx in "$@"; do
135 widths+=("$(ui::measure_col "$data" "$idx")")
136 done
137 echo "${widths[*]}"
138}
139
140function ui::sort_rows() {
141 local field="${1:-1}"
142 sort -t'|' -k"${field},${field}V"
143}
144
145function ui::firewall_rule() {
146 local rule="$1"
147 if [[ "$rule" =~ ACCEPT|DNAT ]]; then
148 printf "\033[0;32m%s\033[0m\n" "$rule"
149 elif [[ "$rule" =~ DROP ]]; then
150 printf "\033[0;31m%s\033[0m\n" "$rule"
151 elif [[ "$rule" =~ NFLOG|LOG ]]; then
152 printf "\033[0;37m%s\033[0m\n" "$rule"
153 else
154 printf "%s\n" "$rule"
155 fi
156}
157
158# ============================================
159# Content Helpers
160# ============================================
161
162function ui::has_content() {
163 # Returns 0 (true) if content exists, 1 if empty
164 # Works with strings, arrays, or command output
165 local value="${1:-}"
166 [[ -n "$value" ]]
167}
168
169function ui::skip_if_empty() {
170 # Usage: ui::skip_if_empty "$var" || return 0
171 # Or: ui::skip_if_empty "${array[*]}" || return 0
172 local value="${1:-}"
173 [[ -z "${value// }" ]] && return 1 || return 0
174}
175
176function ui::empty() {
177 local val="${1:-}"
178 # Empty string or whitespace only
179 [[ -z "${val// }" ]] && return 0
180 # Numeric zero
181 [[ "$val" =~ ^[0-9]+$ ]] && [[ "$val" -eq 0 ]] && return 0
182 return 1
183}
184
185# Usage: ui::bool "$value" [yes_label] [no_label]
186# Default labels: yes / no
187function ui::bool() {
188 local val="${1:-}" yes="${2:-yes}" no="${3:-no}"
189 [[ "$val" == "true" ]] && echo "$yes" || echo "$no"
190}
191
192# ============================================
193# Prompt
194# ============================================
195
196function ui::confirm() {
197 local prompt="${1:-Are you sure?}"
198 local response
199 printf " %s [y/N] " "$prompt"
200 read -r response
201 [[ "${response,,}" == "y" || "${response,,}" == "yes" ]]
202}