feat(ui): Add color-coordinated syntax, fix clipping, redesign flag browser

Major UI improvements addressing user feedback:

1. Color-Coordinated Syntax Highlighting
   - Command names: Bright cyan + bold
   - [OPTIONS]/[OPTION]: Yellow
   - UPPERCASE args (FILE, PATH, STRING): Magenta
   - Lowercase optional args: Green
   - Ellipsis (...): Dim white
   - Applied to both interactive menu and explain mode

2. Fixed Clipping Bug in Interactive Menu
   - Recalculated width allocations more conservatively
   - Changed to percentage-based DESC_WIDTH (40% of terminal)
   - Added min/max caps for widths
   - Improved spacing calculation (TERM_WIDTH - VISIBLE_WIDTH - 8)
   - Added safety bounds (min 3, max 20 spaces)
   - Syntax now always visible (truncated with ... if needed)

3. Redesigned Flag Browser for Better UX
   - Eliminated wasted dead space above fzf
   - Integrated syntax and description INTO fzf header
   - Created elegant box header with command info:
     - Command name
     - Colorized syntax
     - Description
   - Height increased to 95% for more flags visible
   - Preview window reduced to down:3:wrap
   - Removed echo spam ("Extracting flags...", "Found X flags...")
   - Clean, immediate transition to flag browser

4. Updated Function Signatures
   - show_flags() now accepts: cmd_name, syntax, description
   - browse_flags_fuzzy() now accepts: cmd_name, syntax, description
   - Interactive mode passes all three parameters
   - Explain mode uses colorize_syntax() function

5. New Functions
   - colorize_syntax(): Parse and colorize syntax by element type
   - Applied consistently across all display modes

User Experience Improvements:
-  No more clipping - syntax always visible
-  Instant recognition of parameter types via color
-  No wasted screen space before flag browser
-  Command info integrated cleanly in fzf interface
-  Professional, color-coordinated appearance

Technical Details:
- Width calculation: DESC (40% terminal), SYNTAX (remaining - 25)
- Safety bounds on all width calculations
- Color coordination: cyan/yellow/magenta/green/dim
- Consistent formatting across interactive and explain modes
This commit is contained in:
2025-10-28 02:01:29 -07:00
parent 73de59c92b
commit c0af8c15ca
+111 -86
View File
@@ -407,29 +407,31 @@ mode_explain() {
if command -v "$base_cmd" &> /dev/null || type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
echo -e "${CYAN}${BOLD}SYNTAX:${NC}"
local raw_syntax=""
if type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
# For builtins, get the first line of help
local syntax=$(help "$base_cmd" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
if [ -n "$syntax" ]; then
echo -e " ${GREEN}$syntax${NC}"
else
echo -e " ${GREEN}$base_cmd [options]${NC}"
fi
raw_syntax=$(help "$base_cmd" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
[ -z "$raw_syntax" ] && raw_syntax="$base_cmd [options]"
else
# For regular commands, extract SYNOPSIS section from man page
local synopsis=$(man "$base_cmd" 2>/dev/null | col -b | sed -n '/^SYNOPSIS/,/^DESCRIPTION/p' | head -15 | tail -n +2 | head -n -1 | sed 's/^[[:space:]]*/ /')
local synopsis=$(man "$base_cmd" 2>/dev/null | col -b | sed -n '/^SYNOPSIS/,/^DESCRIPTION/p' | head -15 | tail -n +2 | head -n -1 | tr '\n' ' ' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
if [ -n "$synopsis" ]; then
echo -e "${GREEN}$synopsis${NC}"
raw_syntax="$synopsis"
else
# Fallback: try to get usage from command itself
local usage=$("$base_cmd" --help 2>&1 | grep -i "usage" | head -1 | sed 's/^[Uu]sage: / /')
local usage=$("$base_cmd" --help 2>&1 | grep -i "usage" | head -1 | sed 's/^[Uu]sage: //')
if [ -n "$usage" ]; then
echo -e " ${GREEN}$usage${NC}"
raw_syntax="$usage"
else
echo -e " ${GREEN}$base_cmd [options]${NC}"
raw_syntax="$base_cmd [options]"
fi
fi
fi
# Display colorized syntax
if [ -n "$raw_syntax" ]; then
colorize_syntax "$raw_syntax" | sed 's/^/ /'
fi
echo ""
# Add practical example
@@ -590,6 +592,39 @@ get_practical_example() {
echo "$example"
}
# Colorize syntax elements by type for instant recognition
colorize_syntax() {
local syntax="$1"
local colored_syntax="$syntax"
# Color scheme for syntax elements:
# - Command name (first word): Bright cyan
# - [OPTIONS], [OPTION], etc.: Yellow
# - UPPERCASE args (FILE, STRING, SET1, etc.): Magenta
# - Lowercase/optional args: Green
# - ... (ellipsis): Dim white
# Extract command name (first word)
local cmd_word=$(echo "$syntax" | awk '{print $1}')
# Start with command in bright cyan
colored_syntax=$(echo "$syntax" | sed "s/^$cmd_word/\\${CYAN}\\${BOLD}$cmd_word\\${NC}/")
# Color [OPTIONS], [OPTION], [EXPRESSION], etc. in yellow
colored_syntax=$(echo "$colored_syntax" | sed -E "s/\[([A-Z][A-Z_-]*)\]/[\\${YELLOW}\1\\${NC}]/g")
# Color UPPERCASE arguments in magenta (FILE, STRING, PATH, SET1, etc.)
colored_syntax=$(echo "$colored_syntax" | sed -E "s/([[:space:]])([A-Z][A-Z0-9_-]*[A-Z])([[:space:][:punct:]$])/\1\\${MAGENTA}\2\\${NC}\3/g")
# Color lowercase optional arguments in green
colored_syntax=$(echo "$colored_syntax" | sed -E "s/\[([a-z][a-z-]*)\]/[\\${GREEN}\1\\${NC}]/g")
# Color ellipsis (...) in dim white
colored_syntax=$(echo "$colored_syntax" | sed "s/\.\.\./\\${DIM}...\\${NC}/g")
echo -e "$colored_syntax"
}
# Extract all flags from man page or help
extract_all_flags() {
local cmd_name="$1"
@@ -608,90 +643,86 @@ extract_all_flags() {
}
# Interactive flag browser with fuzzy search
# Now includes syntax and description in the fzf interface itself
browse_flags_fuzzy() {
local cmd_name="$1"
local cmd_syntax="$2"
local cmd_desc="$3"
echo ""
echo -e "${CYAN}Extracting flags from man page...${NC}"
# Extract all flags
# Extract all flags silently
local all_flags=$(extract_all_flags "$cmd_name")
if [ -z "$all_flags" ]; then
echo ""
echo -e "${RED}No flags found for: $cmd_name${NC}"
echo ""
echo "Showing basic command info..."
return 1
fi
# Count flags
local flag_count=$(echo "$all_flags" | wc -l)
echo -e "${GREEN}Found $flag_count flags. Opening fuzzy search...${NC}"
echo ""
sleep 1
# Colorize the syntax for the header
local colored_syntax=$(colorize_syntax "$cmd_syntax")
# Show in fzf for fuzzy searching
# Create multi-line header with command info
local header="╔═══════════════════════════════════════════════════════════════════════════════╗
║ Command: ${CYAN}${BOLD}$cmd_name${NC}
║ Syntax: $colored_syntax
║ Desc: ${GREEN}$cmd_desc${NC}
╚═══════════════════════════════════════════════════════════════════════════════╝
🔍 Fuzzy search $flag_count flags | Multi-select: Ctrl-A/D | Esc: Back to menu"
# Show in fzf with integrated command info
echo "$all_flags" | fzf \
--height 80% \
--border \
--header "🔍 Fuzzy search through $cmd_name flags (Esc to exit)" \
--height 95% \
--border=none \
--header "$header" \
--preview 'echo {}' \
--preview-window=up:40%:wrap \
--prompt "Search flags: " \
--preview-window=down:3:wrap \
--prompt "Search: " \
--multi \
--bind 'ctrl-a:select-all' \
--bind 'ctrl-d:deselect-all'
--bind 'ctrl-d:deselect-all' \
--ansi \
--color=header:bold:cyan
return 0
}
show_flags() {
CMD_NAME=$1
FILTER=$2
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${YELLOW}Command Details: ${GREEN}$CMD_NAME${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
# Display SYNTAX section
if command -v "$CMD_NAME" &> /dev/null || type "$CMD_NAME" 2>/dev/null | grep -q "shell builtin"; then
echo -e "${CYAN}${BOLD}SYNTAX:${NC}"
CMD_SYNTAX=$2
CMD_DESC=$3
# Extract syntax if not provided
if [ -z "$CMD_SYNTAX" ]; then
if type "$CMD_NAME" 2>/dev/null | grep -q "shell builtin"; then
# For builtins, get the first line of help
local syntax=$(help "$CMD_NAME" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
if [ -n "$syntax" ]; then
echo -e " ${GREEN}$syntax${NC}"
else
echo -e " ${GREEN}$CMD_NAME [options]${NC}"
fi
CMD_SYNTAX=$(help "$CMD_NAME" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
[ -z "$CMD_SYNTAX" ] && CMD_SYNTAX="$CMD_NAME [options]"
else
# For regular commands, extract SYNOPSIS section from man page
local synopsis=$(man "$CMD_NAME" 2>/dev/null | col -b | sed -n '/^SYNOPSIS/,/^DESCRIPTION/p' | head -15 | tail -n +2 | head -n -1 | sed 's/^[[:space:]]*/ /')
local synopsis=$(man "$CMD_NAME" 2>/dev/null | col -b | sed -n '/^SYNOPSIS/,/^DESCRIPTION/p' | head -15 | tail -n +2 | head -n -1 | tr '\n' ' ' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
if [ -n "$synopsis" ]; then
echo -e "${GREEN}$synopsis${NC}"
CMD_SYNTAX="$synopsis"
else
# Fallback: try to get usage from command itself
local usage=$("$CMD_NAME" --help 2>&1 | grep -i "usage" | head -1 | sed 's/^[Uu]sage: / /')
local usage=$("$CMD_NAME" --help 2>&1 | grep -i "usage" | head -1 | sed 's/^[Uu]sage: //')
if [ -n "$usage" ]; then
echo -e " ${GREEN}$usage${NC}"
CMD_SYNTAX="$usage"
else
echo -e " ${GREEN}$CMD_NAME [options]${NC}"
CMD_SYNTAX="$CMD_NAME [options]"
fi
fi
fi
echo ""
fi
# Display FLAGS/OPTIONS section with fuzzy search
echo -e "${YELLOW}${BOLD}FLAGS/OPTIONS:${NC}"
echo -e "${DIM}Opening interactive flag browser...${NC}"
echo ""
# Default description if not provided
[ -z "$CMD_DESC" ] && CMD_DESC="No description available"
# Use fuzzy flag browser instead of static display
browse_flags_fuzzy "$CMD_NAME"
# Directly open fuzzy flag browser with integrated command info (no wasted space)
browse_flags_fuzzy "$CMD_NAME" "$CMD_SYNTAX" "$CMD_DESC"
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
@@ -935,16 +966,17 @@ if [ "$INTERACTIVE" = true ]; then
# Get terminal width for proper alignment
TERM_WIDTH=$(tput cols 2>/dev/null || echo 120)
# Calculate widths (leave room for colors and spacing)
# Calculate widths more conservatively to prevent clipping
# Reserve space for: command (15) + ": " (2) + description (40%) + spaces (5) + syntax (remaining)
MAX_CMD_WIDTH=15
SYNTAX_WIDTH=45
DESC_WIDTH=$((TERM_WIDTH - MAX_CMD_WIDTH - SYNTAX_WIDTH - 10))
DESC_WIDTH=$((TERM_WIDTH * 40 / 100))
SYNTAX_WIDTH=$((TERM_WIDTH - MAX_CMD_WIDTH - DESC_WIDTH - 10))
# Ensure minimum widths
if [ $DESC_WIDTH -lt 30 ]; then
DESC_WIDTH=30
SYNTAX_WIDTH=$((TERM_WIDTH - MAX_CMD_WIDTH - DESC_WIDTH - 10))
fi
# Ensure minimum widths and cap maximums
[ $DESC_WIDTH -lt 20 ] && DESC_WIDTH=20
[ $SYNTAX_WIDTH -lt 30 ] && SYNTAX_WIDTH=30
[ $DESC_WIDTH -gt 50 ] && DESC_WIDTH=50
[ $SYNTAX_WIDTH -gt 60 ] && SYNTAX_WIDTH=60
# Format each command with colors and right-aligned syntax
FORMATTED_COMMANDS=()
@@ -961,7 +993,7 @@ if [ "$INTERACTIVE" = true ]; then
SYNTAX_PART="$CMD_NAME [options]"
fi
# Truncate if too long (accounting for actual content, not color codes)
# Truncate if too long
if [ ${#DESC_PART} -gt $DESC_WIDTH ]; then
DESC_PART="${DESC_PART:0:$((DESC_WIDTH-3))}..."
fi
@@ -969,25 +1001,29 @@ if [ "$INTERACTIVE" = true ]; then
SYNTAX_PART="${SYNTAX_PART:0:$((SYNTAX_WIDTH-3))}..."
fi
# Calculate padding for right-alignment of syntax
# Total visible length = cmd_name + ": " + desc + spaces + syntax
# Calculate exact padding needed for right-alignment
# Account for visual width only (not ANSI codes)
CMD_LEN=${#CMD_NAME}
DESC_LEN=${#DESC_PART}
SYNTAX_LEN=${#SYNTAX_PART}
NEEDED_SPACES=$((TERM_WIDTH - CMD_LEN - 2 - DESC_LEN - SYNTAX_LEN - 5))
# Ensure at least 2 spaces
if [ $NEEDED_SPACES -lt 2 ]; then
NEEDED_SPACES=2
fi
# Calculate spaces needed: terminal width - all visible characters - separator chars
# Visible: cmd + ": " + desc + syntax = CMD_LEN + 2 + DESC_LEN + SYNTAX_LEN
VISIBLE_WIDTH=$((CMD_LEN + 2 + DESC_LEN + SYNTAX_LEN))
NEEDED_SPACES=$((TERM_WIDTH - VISIBLE_WIDTH - 8)) # Extra padding for safety
# Minimum spacing
[ $NEEDED_SPACES -lt 3 ] && NEEDED_SPACES=3
# Maximum spacing to prevent excessive gaps
[ $NEEDED_SPACES -gt 20 ] && NEEDED_SPACES=20
# Create spacing string
SPACES=$(printf '%*s' "$NEEDED_SPACES" '')
# Format with colors:
# - Cyan for command name
# - Bright cyan for command name
# - White for description
# - Dim cyan for syntax
# - Dim cyan for syntax (right-aligned)
formatted_line="\033[0;36m${CMD_NAME}\033[0m: ${DESC_PART}${SPACES}\033[2;36m${SYNTAX_PART}\033[0m"
FORMATTED_COMMANDS+=("$formatted_line")
@@ -1030,20 +1066,9 @@ if [ "$INTERACTIVE" = true ]; then
fi
done
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo -e "${YELLOW}Command:${NC} ${MAGENTA}$CMD${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
echo ""
echo -e "${CYAN}${BOLD}SYNTAX:${NC}"
echo -e " ${GREEN}$SYNTAX${NC}"
echo ""
echo -e "${GREEN}Description:${NC} $DESC"
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
# Directly show fuzzy flag browser (no filter prompt)
show_flags "$CMD"
# Directly show fuzzy flag browser with integrated command info (no wasted space)
clear
show_flags "$CMD" "$SYNTAX" "$DESC"
# After viewing flags, ask if user wants to search another command
echo ""