cleanup: ui::pad_mb removal, watch alignment fixes, endpoint cache fallback
- ui::rule::list_row: inline padding math replaces ui::pad_mb (major perf gain)
- ui:⌚:fw_row/wg_row: drop ui::pad_mb for fw/wg labels (always 2 chars)
- watch: endpoint fallback via monitor::get_cached_endpoint
- watch: _poll_handshakes sorts by ts descending (most recent first)
- watch: empty endpoint uses - not — (avoids multi-byte padding issues)
- ui.sh: UTF-8 extra byte constants (_UI_EMDASH_EXTRA, _UI_ARROW_EXTRA, _UI_BULLET_EXTRA)
This commit is contained in:
parent
5c2e16e358
commit
3058750c3d
9 changed files with 98 additions and 76 deletions
|
|
@ -107,13 +107,16 @@ function cmd::watch::run() {
|
||||||
function cmd::watch::_poll_handshakes() {
|
function cmd::watch::_poll_handshakes() {
|
||||||
local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}"
|
local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}"
|
||||||
local w_client="${4:-20}" w_dest="${5:-18}"
|
local w_client="${4:-20}" w_dest="${5:-18}"
|
||||||
|
|
||||||
|
# Collect rows with sort key before printing
|
||||||
|
local -a rows=()
|
||||||
|
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
local public_key ts
|
local public_key ts
|
||||||
public_key=$(echo "$line" | awk '{print $1}')
|
public_key=$(echo "$line" | awk '{print $1}')
|
||||||
ts=$(echo "$line" | awk '{print $2}')
|
ts=$(echo "$line" | awk '{print $2}')
|
||||||
[[ -z "$ts" || "$ts" == "0" ]] && continue
|
[[ -z "$ts" || "$ts" == "0" ]] && continue
|
||||||
|
|
||||||
local client_name=""
|
local client_name=""
|
||||||
for conf in "$(ctx::clients)"/*.conf; do
|
for conf in "$(ctx::clients)"/*.conf; do
|
||||||
[[ -f "$conf" ]] || continue
|
[[ -f "$conf" ]] || continue
|
||||||
|
|
@ -128,34 +131,48 @@ function cmd::watch::_poll_handshakes() {
|
||||||
done
|
done
|
||||||
[[ -z "$client_name" ]] && continue
|
[[ -z "$client_name" ]] && continue
|
||||||
[[ -n "$filter_name" && "$client_name" != "$filter_name" ]] && continue
|
[[ -n "$filter_name" && "$client_name" != "$filter_name" ]] && continue
|
||||||
|
|
||||||
local safe_key
|
local safe_key
|
||||||
safe_key=$(echo "$public_key" | md5sum | cut -d' ' -f1)
|
safe_key=$(echo "$public_key" | md5sum | cut -d' ' -f1)
|
||||||
local prev_ts_file="/tmp/wgctl_hs_${safe_key}"
|
local prev_ts_file="/tmp/wgctl_hs_${safe_key}"
|
||||||
local prev_ts="0"
|
local prev_ts="0"
|
||||||
[[ -f "$prev_ts_file" ]] && prev_ts=$(cat "$prev_ts_file")
|
[[ -f "$prev_ts_file" ]] && prev_ts=$(cat "$prev_ts_file")
|
||||||
[[ "$ts" == "$prev_ts" ]] && continue
|
[[ "$ts" == "$prev_ts" ]] && continue
|
||||||
|
|
||||||
local gap=$(( ts - ${prev_ts:-0} ))
|
local gap=$(( ts - ${prev_ts:-0} ))
|
||||||
echo "$ts" > "$prev_ts_file"
|
echo "$ts" > "$prev_ts_file"
|
||||||
(( gap < ${WG_HANDSHAKE_CHECK_TIME_SEC:-300} )) && continue
|
(( gap < ${WG_HANDSHAKE_CHECK_TIME_SEC:-300} )) && continue
|
||||||
|
|
||||||
local ts_fmt
|
local ts_fmt
|
||||||
ts_fmt=$(fmt::datetime_short "$ts")
|
ts_fmt=$(fmt::datetime_short "$ts")
|
||||||
|
|
||||||
|
# Resolve endpoint — try wg show first, fall back to endpoint cache
|
||||||
local endpoint
|
local endpoint
|
||||||
endpoint=$(monitor::endpoint_for_key "$public_key")
|
endpoint=$(monitor::endpoint_for_key "$public_key")
|
||||||
|
|
||||||
# Resolve endpoint
|
if [[ -z "$endpoint" ]]; then
|
||||||
|
endpoint=$(monitor::get_cached_endpoint "$client_name")
|
||||||
|
fi
|
||||||
|
|
||||||
local endpoint_display
|
local endpoint_display
|
||||||
endpoint_display=$(resolve::ip "${endpoint:-}")
|
endpoint_display=$(resolve::ip "${endpoint:-}")
|
||||||
[[ -z "$endpoint_display" ]] && endpoint_display="${endpoint:-—}"
|
[[ -z "$endpoint_display" ]] && endpoint_display="${endpoint:--}"
|
||||||
|
|
||||||
ui::watch::wg_row "$ts_fmt" "$client_name" "$endpoint_display" "handshake" \
|
# Build row with ts prefix for sorting
|
||||||
"$w_client" "$w_dest"
|
local row
|
||||||
|
row=$(ui::watch::wg_row "$ts_fmt" "$client_name" "$endpoint_display" "handshake" \
|
||||||
|
"$w_client" "$w_dest")
|
||||||
|
rows+=("${ts}|${row}")
|
||||||
|
|
||||||
done < <(wg show "$(config::interface)" latest-handshakes 2>/dev/null)
|
done < <(wg show "$(config::interface)" latest-handshakes 2>/dev/null)
|
||||||
|
|
||||||
|
# Sort by ts descending (most recent first) and print
|
||||||
|
if [[ ${#rows[@]} -gt 0 ]]; then
|
||||||
|
printf '%s\n' "${rows[@]}" | sort -t'|' -k1,1rn | while IFS= read -r entry; do
|
||||||
|
printf "%s\n" "${entry#*|}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# Event Tailer
|
# Event Tailer
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
@ -164,7 +181,7 @@ function cmd::watch::_tail_events() {
|
||||||
local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}"
|
local filter_name="${1:-}" filter_type="${2:-}" filter_peers="${3:-}"
|
||||||
local blocked_only="${4:-false}" restricted_only="${5:-false}" allowed_only="${6:-false}"
|
local blocked_only="${4:-false}" restricted_only="${5:-false}" allowed_only="${6:-false}"
|
||||||
local w_client="${7:-20}" w_dest="${8:-18}"
|
local w_client="${7:-20}" w_dest="${8:-18}"
|
||||||
|
|
||||||
# Build ip->name map
|
# Build ip->name map
|
||||||
declare -A ip_to_name=()
|
declare -A ip_to_name=()
|
||||||
while IFS= read -r conf; do
|
while IFS= read -r conf; do
|
||||||
|
|
@ -174,41 +191,41 @@ function cmd::watch::_tail_events() {
|
||||||
ip=$(grep "^Address" "$conf" 2>/dev/null | awk '{print $3}' | cut -d'/' -f1)
|
ip=$(grep "^Address" "$conf" 2>/dev/null | awk '{print $3}' | cut -d'/' -f1)
|
||||||
[[ -n "$ip" && -n "$cname" ]] && ip_to_name["$ip"]="$cname"
|
[[ -n "$ip" && -n "$cname" ]] && ip_to_name["$ip"]="$cname"
|
||||||
done < <(find "$(ctx::clients)" -name "*.conf" 2>/dev/null)
|
done < <(find "$(ctx::clients)" -name "*.conf" 2>/dev/null)
|
||||||
|
|
||||||
declare -A _WATCH_LAST_FW=()
|
declare -A _WATCH_LAST_FW=()
|
||||||
declare -A _WATCH_LAST_WG=()
|
declare -A _WATCH_LAST_WG=()
|
||||||
|
|
||||||
local source_file
|
local source_file
|
||||||
source_file=$(mktemp)
|
source_file=$(mktemp)
|
||||||
echo "wg" > "$source_file"
|
echo "wg" > "$source_file"
|
||||||
trap "rm -f '$source_file'" EXIT
|
trap "rm -f '$source_file'" EXIT
|
||||||
|
|
||||||
tail -f "$(ctx::events_log)" "$(ctx::fw_events_log)" 2>/dev/null \
|
tail -f "$(ctx::events_log)" "$(ctx::fw_events_log)" 2>/dev/null \
|
||||||
| while IFS= read -r line; do
|
| while IFS= read -r line; do
|
||||||
[[ -z "$line" ]] && continue
|
[[ -z "$line" ]] && continue
|
||||||
|
|
||||||
if [[ "$line" == "==> "* ]]; then
|
if [[ "$line" == "==> "* ]]; then
|
||||||
[[ "$line" == *"fw_events"* ]] && echo "fw" > "$source_file" || echo "wg" > "$source_file"
|
[[ "$line" == *"fw_events"* ]] && echo "fw" > "$source_file" || echo "wg" > "$source_file"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local source
|
local source
|
||||||
source=$(cat "$source_file")
|
source=$(cat "$source_file")
|
||||||
|
|
||||||
if [[ "$source" == "fw" ]]; then
|
if [[ "$source" == "fw" ]]; then
|
||||||
$allowed_only && continue
|
$allowed_only && continue
|
||||||
|
|
||||||
local fw_data
|
local fw_data
|
||||||
fw_data=$(python3 "$(ctx::json_helper)" parse_fw_event "$line" 2>/dev/null) || continue
|
fw_data=$(python3 "$(ctx::json_helper)" parse_fw_event "$line" 2>/dev/null) || continue
|
||||||
[[ -z "$fw_data" ]] && continue
|
[[ -z "$fw_data" ]] && continue
|
||||||
|
|
||||||
local ts src_ip dest_ip dest_port proto
|
local ts src_ip dest_ip dest_port proto
|
||||||
IFS='|' read -r ts src_ip dest_ip dest_port proto <<< "$fw_data"
|
IFS='|' read -r ts src_ip dest_ip dest_port proto <<< "$fw_data"
|
||||||
[[ -z "$src_ip" ]] && continue
|
[[ -z "$src_ip" ]] && continue
|
||||||
|
|
||||||
local client="${ip_to_name[$src_ip]:-$src_ip}"
|
local client="${ip_to_name[$src_ip]:-$src_ip}"
|
||||||
[[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue
|
[[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue
|
||||||
|
|
||||||
local fw_key="${src_ip}:${dest_ip}:${dest_port}:${proto}"
|
local fw_key="${src_ip}:${dest_ip}:${dest_port}:${proto}"
|
||||||
local now; now=$(date +%s)
|
local now; now=$(date +%s)
|
||||||
local window=30
|
local window=30
|
||||||
|
|
@ -217,52 +234,53 @@ function cmd::watch::_tail_events() {
|
||||||
local last="${_WATCH_LAST_FW[$fw_key]:-0}"
|
local last="${_WATCH_LAST_FW[$fw_key]:-0}"
|
||||||
(( now - last < window )) && continue
|
(( now - last < window )) && continue
|
||||||
_WATCH_LAST_FW["$fw_key"]="$now"
|
_WATCH_LAST_FW["$fw_key"]="$now"
|
||||||
|
|
||||||
local ts_fmt
|
local ts_fmt
|
||||||
ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)")
|
ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)")
|
||||||
|
|
||||||
local dest_display
|
local dest_display
|
||||||
dest_display=$(resolve::dest "$dest_ip" "$dest_port" "$proto")
|
dest_display=$(resolve::dest "$dest_ip" "$dest_port" "$proto")
|
||||||
|
|
||||||
ui::watch::fw_row "$ts_fmt" "$client" "$dest_display" "$w_client" "$w_dest"
|
ui::watch::fw_row "$ts_fmt" "$client" "$dest_display" "$w_client" "$w_dest"
|
||||||
|
|
||||||
else
|
else
|
||||||
$restricted_only && continue
|
$restricted_only && continue
|
||||||
|
|
||||||
local ev_data
|
local ev_data
|
||||||
ev_data=$(python3 "$(ctx::json_helper)" parse_event "$line" 2>/dev/null) || continue
|
ev_data=$(python3 "$(ctx::json_helper)" parse_event "$line" 2>/dev/null) || continue
|
||||||
[[ -z "$ev_data" ]] && continue
|
[[ -z "$ev_data" ]] && continue
|
||||||
|
|
||||||
local ts client endpoint event
|
local ts client endpoint event
|
||||||
IFS='|' read -r ts client endpoint event <<< "$ev_data"
|
IFS='|' read -r ts client endpoint event <<< "$ev_data"
|
||||||
[[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue
|
[[ -n "$filter_name" && "$client" != "$filter_name" ]] && continue
|
||||||
$blocked_only && [[ "$event" != "attempt" ]] && continue
|
$blocked_only && [[ "$event" != "attempt" ]] && continue
|
||||||
$allowed_only && [[ "$event" != "handshake" ]] && continue
|
$allowed_only && [[ "$event" != "handshake" ]] && continue
|
||||||
|
|
||||||
local wg_key="${client}:${endpoint}:${event}"
|
local wg_key="${client}:${endpoint}:${event}"
|
||||||
local now; now=$(date +%s)
|
local now; now=$(date +%s)
|
||||||
local last="${_WATCH_LAST_WG[$wg_key]:-0}"
|
local last="${_WATCH_LAST_WG[$wg_key]:-0}"
|
||||||
|
|
||||||
# Handshakes — only show if gap > 5min (new session)
|
|
||||||
# Attempts — shorter window (30s) since each attempt is meaningful
|
|
||||||
local window=30
|
local window=30
|
||||||
[[ "$event" == "handshake" ]] && window="${WG_HANDSHAKE_CHECK_TIME_SEC:-300}"
|
[[ "$event" == "handshake" ]] && window="${WG_HANDSHAKE_CHECK_TIME_SEC:-300}"
|
||||||
|
|
||||||
(( now - last < window )) && continue
|
(( now - last < window )) && continue
|
||||||
_WATCH_LAST_WG["$wg_key"]="$now"
|
_WATCH_LAST_WG["$wg_key"]="$now"
|
||||||
|
|
||||||
local ts_fmt
|
local ts_fmt
|
||||||
ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)")
|
ts_fmt=$(fmt::datetime_short "$(json::iso_to_ts "$ts" 2>/dev/null || echo 0)")
|
||||||
|
|
||||||
# Resolve endpoint
|
# Resolve endpoint — fall back to endpoint cache if empty
|
||||||
local endpoint_display
|
local endpoint_resolved
|
||||||
endpoint_display=$(resolve::ip "${endpoint:-}")
|
endpoint_resolved=$(resolve::ip "${endpoint:-}")
|
||||||
[[ -z "$endpoint_display" ]] && endpoint_display="${endpoint:-—}"
|
if [[ -z "$endpoint_resolved" && -n "$endpoint" ]]; then
|
||||||
|
endpoint_resolved="$endpoint"
|
||||||
ui::watch::wg_row "$ts_fmt" "$client" "$endpoint_display" "$event" \
|
fi
|
||||||
|
[[ -z "$endpoint_resolved" ]] && endpoint_resolved="-"
|
||||||
|
|
||||||
|
ui::watch::wg_row "$ts_fmt" "$client" "$endpoint_resolved" "$event" \
|
||||||
"$w_client" "$w_dest"
|
"$w_client" "$w_dest"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
rm -f "$source_file"
|
rm -f "$source_file"
|
||||||
}
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -3,6 +3,12 @@
|
||||||
UI_ROW_WIDTH=${UI_ROW_WIDTH:-20}
|
UI_ROW_WIDTH=${UI_ROW_WIDTH:-20}
|
||||||
UI_SECTION_WIDTH=${UI_SECTION_WIDTH:-44}
|
UI_SECTION_WIDTH=${UI_SECTION_WIDTH:-44}
|
||||||
|
|
||||||
|
# UTF-8 multi-byte character extras (bash ${#} counts bytes, not chars)
|
||||||
|
# extra = byte_length - visible_char_length
|
||||||
|
_UI_EMDASH_EXTRA=2 # — (em dash) 3 bytes, 1 visible
|
||||||
|
_UI_ARROW_EXTRA=2 # → (right arrow) 3 bytes, 1 visible
|
||||||
|
_UI_BULLET_EXTRA=1 # · (middle dot) 2 bytes, 1 visible
|
||||||
|
|
||||||
function ui::row() {
|
function ui::row() {
|
||||||
local label="$1" value="$2" width="${3:-$UI_ROW_WIDTH}"
|
local label="$1" value="$2" width="${3:-$UI_ROW_WIDTH}"
|
||||||
printf " %-${width}s %s\n" "${label}:" "$value"
|
printf " %-${width}s %s\n" "${label}:" "$value"
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ function monitor::cache_endpoint() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function monitor::get_cached_endpoint() {
|
function monitor::get_cached_endpoint() {
|
||||||
local client="$1"
|
local client="${1:-}"
|
||||||
json::get "$ENDPOINT_CACHE" "$client"
|
json::get "$ENDPOINT_CACHE" "$client"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,17 +94,16 @@ _UI_WATCH_WG_COLOR="\033[1;32m"
|
||||||
function ui::watch::fw_row() {
|
function ui::watch::fw_row() {
|
||||||
local ts="${1:-}" client="${2:-}" dest_display="${3:-}" \
|
local ts="${1:-}" client="${2:-}" dest_display="${3:-}" \
|
||||||
w_client="${4:-20}" w_dest="${5:-18}"
|
w_client="${4:-20}" w_dest="${5:-18}"
|
||||||
|
|
||||||
local ts_pad
|
# "fw" is always 2 visible chars — no padding needed
|
||||||
|
local src="${_UI_WATCH_FW_COLOR}fw\033[0m"
|
||||||
|
|
||||||
|
local ts_pad client_pad dest_pad_n
|
||||||
ts_pad=$(printf "%-11s" "$ts")
|
ts_pad=$(printf "%-11s" "$ts")
|
||||||
|
|
||||||
local src
|
|
||||||
src=$(ui::pad_mb "${_UI_WATCH_FW_COLOR}fw\033[0m" 2)
|
|
||||||
local client_pad dest_pad_n
|
|
||||||
client_pad=$(printf "%-${w_client}s" "$client")
|
client_pad=$(printf "%-${w_client}s" "$client")
|
||||||
dest_pad_n=$(( w_dest - ${#dest_display} ))
|
dest_pad_n=$(( w_dest - ${#dest_display} ))
|
||||||
[[ $dest_pad_n -lt 0 ]] && dest_pad_n=0
|
[[ $dest_pad_n -lt 0 ]] && dest_pad_n=0
|
||||||
# echo "DEBUG fw: ts_bytes=${#ts} src_bytes=${#src} client='$client'(${#client}) client_pad_bytes=${#client_pad}" >&2
|
|
||||||
printf " %s %b %s \033[1;31m→\033[0m %s%*s \033[1;31mdrop\033[0m\n" \
|
printf " %s %b %s \033[1;31m→\033[0m %s%*s \033[1;31mdrop\033[0m\n" \
|
||||||
"$ts_pad" "$src" "$client_pad" "$dest_display" "$dest_pad_n" ""
|
"$ts_pad" "$src" "$client_pad" "$dest_display" "$dest_pad_n" ""
|
||||||
}
|
}
|
||||||
|
|
@ -112,35 +111,31 @@ function ui::watch::fw_row() {
|
||||||
function ui::watch::wg_row() {
|
function ui::watch::wg_row() {
|
||||||
local ts="${1:-}" client="${2:-}" endpoint="${3:-}" event="${4:-}" \
|
local ts="${1:-}" client="${2:-}" endpoint="${3:-}" event="${4:-}" \
|
||||||
w_client="${5:-20}" w_endpoint="${6:-18}"
|
w_client="${5:-20}" w_endpoint="${6:-18}"
|
||||||
|
|
||||||
local ts_pad
|
|
||||||
ts_pad=$(printf "%-11s" "$ts")
|
|
||||||
|
|
||||||
local event_color
|
local event_color
|
||||||
case "$event" in
|
case "$event" in
|
||||||
handshake) event_color="\033[1;32m" ;;
|
handshake) event_color="\033[1;32m" ;;
|
||||||
attempt) event_color="\033[1;31m" ;;
|
attempt) event_color="\033[1;31m" ;;
|
||||||
*) event_color="\033[0;37m" ;;
|
*) event_color="\033[0;37m" ;;
|
||||||
esac
|
esac
|
||||||
local src
|
|
||||||
src=$(ui::pad_mb "${_UI_WATCH_WG_COLOR}wg\033[0m" 2)
|
# "wg" is always 2 visible chars — no padding needed
|
||||||
|
local src="${_UI_WATCH_WG_COLOR}wg\033[0m"
|
||||||
case "$event" in
|
|
||||||
handshake) src="\033[1;32m" ;; # green
|
# Use "-" not "—" to avoid multi-byte padding issues
|
||||||
attempt) src="\033[1;31m" ;; # red
|
local endpoint_display="${endpoint:--}"
|
||||||
*) src="\033[0;37m" ;; # gray
|
|
||||||
esac
|
local ts_pad client_pad endpoint_pad_n
|
||||||
local src_colored="${src}wg\033[0m"
|
ts_pad=$(printf "%-11s" "$ts")
|
||||||
|
|
||||||
local client_pad endpoint_pad_n
|
|
||||||
client_pad=$(printf "%-${w_client}s" "$client")
|
client_pad=$(printf "%-${w_client}s" "$client")
|
||||||
endpoint_pad_n=$(( w_endpoint - ${#endpoint} ))
|
endpoint_pad_n=$(( w_endpoint - ${#endpoint_display} ))
|
||||||
[[ $endpoint_pad_n -lt 0 ]] && endpoint_pad_n=0
|
[[ $endpoint_pad_n -lt 0 ]] && endpoint_pad_n=0
|
||||||
# echo "DEBUG wg: ts_bytes=${#ts} src_bytes=${#src} client='$client'(${#client}) client_pad_bytes=${#client_pad}" >&2
|
|
||||||
printf " %s %b %s %s%*s %b%s\033[0m\n" \
|
printf " %s %b %s %s%*s %b%s\033[0m\n" \
|
||||||
"$ts_pad" "$src_colored" "$client_pad" "$endpoint" "$endpoint_pad_n" "" \
|
"$ts_pad" "$src" "$client_pad" "$endpoint_display" "$endpoint_pad_n" "" \
|
||||||
"$event_color" "$event"
|
"$event_color" "$event"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function ui::watch::header_table() {
|
function ui::watch::header_table() {
|
||||||
printf "\n %-20s %-8s %-22s %-28s %-14s %s\n" \
|
printf "\n %-20s %-8s %-22s %-28s %-14s %s\n" \
|
||||||
|
|
|
||||||
|
|
@ -331,20 +331,23 @@ function ui::rule::list_row() {
|
||||||
extends_indicator=" \033[2m↳ ${extends_display}\033[0m"
|
extends_indicator=" \033[2m↳ ${extends_display}\033[0m"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Allows column — green +N if >0, dim +0 if zero
|
# Allows — green +N, padded to 5 visible chars
|
||||||
|
# Append spaces after reset code so printf doesn't miscount
|
||||||
local allows_str
|
local allows_str
|
||||||
if [[ "$n_allows" -gt 0 ]]; then
|
if [[ "$n_allows" -gt 0 ]]; then
|
||||||
allows_str=$(ui::pad_mb "\033[1;32m+${n_allows}\033[0m" 5)
|
printf -v allows_str "\033[1;32m+%s\033[0m" "$n_allows"
|
||||||
|
allows_str="${allows_str}$(printf '%*s' "$(( 4 - ${#n_allows} ))" '')"
|
||||||
else
|
else
|
||||||
allows_str=$(ui::pad_mb "\033[2m+0\033[0m" 5)
|
printf -v allows_str "\033[2m+0\033[0m " # "+0" = 2 visible + 3 spaces = 5
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Blocks column — red -N if >0, dim -0 if zero
|
# Blocks — red -N, padded to 5 visible chars
|
||||||
local blocks_str
|
local blocks_str
|
||||||
if [[ "$n_blocks" -gt 0 ]]; then
|
if [[ "$n_blocks" -gt 0 ]]; then
|
||||||
blocks_str=$(ui::pad_mb "\033[1;31m-${n_blocks}\033[0m" 5)
|
printf -v blocks_str "\033[1;31m-%s\033[0m" "$n_blocks"
|
||||||
|
blocks_str="${blocks_str}$(printf '%*s' "$(( 4 - ${#n_blocks} ))" '')"
|
||||||
else
|
else
|
||||||
blocks_str=$(ui::pad_mb "\033[2m-0\033[0m" 5)
|
printf -v blocks_str "\033[2m-0\033[0m " # "-0" = 2 visible + 3 spaces = 5
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Peers — dim if zero
|
# Peers — dim if zero
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue