最後活躍 1 month ago

nuno 已修改 1 month ago. 還原成這個修訂版本

1 file changed, 202 insertions

gistfile1.txt(檔案已創建)

@@ -0,0 +1,202 @@
1 + #!/usr/bin/env bash
2 +
3 + UI_ROW_WIDTH=${UI_ROW_WIDTH:-20}
4 + UI_SECTION_WIDTH=${UI_SECTION_WIDTH:-44}
5 +
6 + function ui::row() {
7 + local label="$1" value="$2" width="${3:-$UI_ROW_WIDTH}"
8 + printf " %-${width}s %s\n" "${label}:" "$value"
9 + }
10 +
11 + function 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 +
18 + function ui::list_item() {
19 + local prefix="$1" value="$2"
20 + printf " %s %s\n" "$prefix" "$value"
21 + }
22 +
23 + function 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 +
31 + function ui::divider() {
32 + local width="${1:-48}"
33 + printf " %s\n" "$(printf '─%.0s' $(seq 1 $width))"
34 + }
35 +
36 + function 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 +
44 + function 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 +
56 + function ui::vis_len_multi() {
57 + # Get visible lengths of multiple strings in one Python call
58 + # Returns newline-separated integers
59 + python3 -c "
60 + import sys, re
61 + ansi = re.compile(r'\x1b\[[0-9;]*m')
62 + for 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.
70 + function 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
82 + function 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 +
91 + function ui::pad_status() {
92 + ui::pad "${1:-}" "${2:-25}"
93 + }
94 +
95 + function 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)
110 + function 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)
130 + function 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 +
140 + function ui::sort_rows() {
141 + local field="${1:-1}"
142 + sort -t'|' -k"${field},${field}V"
143 + }
144 +
145 + function 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 +
162 + function 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 +
169 + function 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 +
176 + function 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
187 + function 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 +
196 + function 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 + }
上一頁 下一頁