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:
+111
-86
@@ -407,29 +407,31 @@ mode_explain() {
|
|||||||
if command -v "$base_cmd" &> /dev/null || type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
|
if command -v "$base_cmd" &> /dev/null || type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
|
||||||
echo -e "${CYAN}${BOLD}SYNTAX:${NC}"
|
echo -e "${CYAN}${BOLD}SYNTAX:${NC}"
|
||||||
|
|
||||||
|
local raw_syntax=""
|
||||||
if type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
|
if type "$base_cmd" 2>/dev/null | grep -q "shell builtin"; then
|
||||||
# For builtins, get the first line of help
|
# For builtins, get the first line of help
|
||||||
local syntax=$(help "$base_cmd" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
|
raw_syntax=$(help "$base_cmd" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
|
||||||
if [ -n "$syntax" ]; then
|
[ -z "$raw_syntax" ] && raw_syntax="$base_cmd [options]"
|
||||||
echo -e " ${GREEN}$syntax${NC}"
|
|
||||||
else
|
|
||||||
echo -e " ${GREEN}$base_cmd [options]${NC}"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
# For regular commands, extract SYNOPSIS section from man page
|
# 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
|
if [ -n "$synopsis" ]; then
|
||||||
echo -e "${GREEN}$synopsis${NC}"
|
raw_syntax="$synopsis"
|
||||||
else
|
else
|
||||||
# Fallback: try to get usage from command itself
|
# 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
|
if [ -n "$usage" ]; then
|
||||||
echo -e " ${GREEN}$usage${NC}"
|
raw_syntax="$usage"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}$base_cmd [options]${NC}"
|
raw_syntax="$base_cmd [options]"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Display colorized syntax
|
||||||
|
if [ -n "$raw_syntax" ]; then
|
||||||
|
colorize_syntax "$raw_syntax" | sed 's/^/ /'
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Add practical example
|
# Add practical example
|
||||||
@@ -590,6 +592,39 @@ get_practical_example() {
|
|||||||
echo "$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 from man page or help
|
||||||
extract_all_flags() {
|
extract_all_flags() {
|
||||||
local cmd_name="$1"
|
local cmd_name="$1"
|
||||||
@@ -608,90 +643,86 @@ extract_all_flags() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Interactive flag browser with fuzzy search
|
# Interactive flag browser with fuzzy search
|
||||||
|
# Now includes syntax and description in the fzf interface itself
|
||||||
browse_flags_fuzzy() {
|
browse_flags_fuzzy() {
|
||||||
local cmd_name="$1"
|
local cmd_name="$1"
|
||||||
|
local cmd_syntax="$2"
|
||||||
|
local cmd_desc="$3"
|
||||||
|
|
||||||
echo ""
|
# Extract all flags silently
|
||||||
echo -e "${CYAN}Extracting flags from man page...${NC}"
|
|
||||||
|
|
||||||
# Extract all flags
|
|
||||||
local all_flags=$(extract_all_flags "$cmd_name")
|
local all_flags=$(extract_all_flags "$cmd_name")
|
||||||
|
|
||||||
if [ -z "$all_flags" ]; then
|
if [ -z "$all_flags" ]; then
|
||||||
|
echo ""
|
||||||
echo -e "${RED}No flags found for: $cmd_name${NC}"
|
echo -e "${RED}No flags found for: $cmd_name${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Showing basic command info..."
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Count flags
|
# Count flags
|
||||||
local flag_count=$(echo "$all_flags" | wc -l)
|
local flag_count=$(echo "$all_flags" | wc -l)
|
||||||
|
|
||||||
echo -e "${GREEN}Found $flag_count flags. Opening fuzzy search...${NC}"
|
# Colorize the syntax for the header
|
||||||
echo ""
|
local colored_syntax=$(colorize_syntax "$cmd_syntax")
|
||||||
sleep 1
|
|
||||||
|
|
||||||
# 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 \
|
echo "$all_flags" | fzf \
|
||||||
--height 80% \
|
--height 95% \
|
||||||
--border \
|
--border=none \
|
||||||
--header "🔍 Fuzzy search through $cmd_name flags (Esc to exit)" \
|
--header "$header" \
|
||||||
--preview 'echo {}' \
|
--preview 'echo {}' \
|
||||||
--preview-window=up:40%:wrap \
|
--preview-window=down:3:wrap \
|
||||||
--prompt "Search flags: " \
|
--prompt "⚡ Search: " \
|
||||||
--multi \
|
--multi \
|
||||||
--bind 'ctrl-a:select-all' \
|
--bind 'ctrl-a:select-all' \
|
||||||
--bind 'ctrl-d:deselect-all'
|
--bind 'ctrl-d:deselect-all' \
|
||||||
|
--ansi \
|
||||||
|
--color=header:bold:cyan
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
show_flags() {
|
show_flags() {
|
||||||
CMD_NAME=$1
|
CMD_NAME=$1
|
||||||
FILTER=$2
|
CMD_SYNTAX=$2
|
||||||
|
CMD_DESC=$3
|
||||||
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}"
|
|
||||||
|
|
||||||
|
# Extract syntax if not provided
|
||||||
|
if [ -z "$CMD_SYNTAX" ]; then
|
||||||
if type "$CMD_NAME" 2>/dev/null | grep -q "shell builtin"; then
|
if type "$CMD_NAME" 2>/dev/null | grep -q "shell builtin"; then
|
||||||
# For builtins, get the first line of help
|
# For builtins, get the first line of help
|
||||||
local syntax=$(help "$CMD_NAME" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
|
CMD_SYNTAX=$(help "$CMD_NAME" 2>/dev/null | head -1 | sed 's/^[[:space:]]*//')
|
||||||
if [ -n "$syntax" ]; then
|
[ -z "$CMD_SYNTAX" ] && CMD_SYNTAX="$CMD_NAME [options]"
|
||||||
echo -e " ${GREEN}$syntax${NC}"
|
|
||||||
else
|
|
||||||
echo -e " ${GREEN}$CMD_NAME [options]${NC}"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
# For regular commands, extract SYNOPSIS section from man page
|
# 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
|
if [ -n "$synopsis" ]; then
|
||||||
echo -e "${GREEN}$synopsis${NC}"
|
CMD_SYNTAX="$synopsis"
|
||||||
else
|
else
|
||||||
# Fallback: try to get usage from command itself
|
# 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
|
if [ -n "$usage" ]; then
|
||||||
echo -e " ${GREEN}$usage${NC}"
|
CMD_SYNTAX="$usage"
|
||||||
else
|
else
|
||||||
echo -e " ${GREEN}$CMD_NAME [options]${NC}"
|
CMD_SYNTAX="$CMD_NAME [options]"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo ""
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Display FLAGS/OPTIONS section with fuzzy search
|
# Default description if not provided
|
||||||
echo -e "${YELLOW}${BOLD}FLAGS/OPTIONS:${NC}"
|
[ -z "$CMD_DESC" ] && CMD_DESC="No description available"
|
||||||
echo -e "${DIM}Opening interactive flag browser...${NC}"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Use fuzzy flag browser instead of static display
|
# Directly open fuzzy flag browser with integrated command info (no wasted space)
|
||||||
browse_flags_fuzzy "$CMD_NAME"
|
browse_flags_fuzzy "$CMD_NAME" "$CMD_SYNTAX" "$CMD_DESC"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
@@ -935,16 +966,17 @@ if [ "$INTERACTIVE" = true ]; then
|
|||||||
# Get terminal width for proper alignment
|
# Get terminal width for proper alignment
|
||||||
TERM_WIDTH=$(tput cols 2>/dev/null || echo 120)
|
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
|
MAX_CMD_WIDTH=15
|
||||||
SYNTAX_WIDTH=45
|
DESC_WIDTH=$((TERM_WIDTH * 40 / 100))
|
||||||
DESC_WIDTH=$((TERM_WIDTH - MAX_CMD_WIDTH - SYNTAX_WIDTH - 10))
|
SYNTAX_WIDTH=$((TERM_WIDTH - MAX_CMD_WIDTH - DESC_WIDTH - 10))
|
||||||
|
|
||||||
# Ensure minimum widths
|
# Ensure minimum widths and cap maximums
|
||||||
if [ $DESC_WIDTH -lt 30 ]; then
|
[ $DESC_WIDTH -lt 20 ] && DESC_WIDTH=20
|
||||||
DESC_WIDTH=30
|
[ $SYNTAX_WIDTH -lt 30 ] && SYNTAX_WIDTH=30
|
||||||
SYNTAX_WIDTH=$((TERM_WIDTH - MAX_CMD_WIDTH - DESC_WIDTH - 10))
|
[ $DESC_WIDTH -gt 50 ] && DESC_WIDTH=50
|
||||||
fi
|
[ $SYNTAX_WIDTH -gt 60 ] && SYNTAX_WIDTH=60
|
||||||
|
|
||||||
# Format each command with colors and right-aligned syntax
|
# Format each command with colors and right-aligned syntax
|
||||||
FORMATTED_COMMANDS=()
|
FORMATTED_COMMANDS=()
|
||||||
@@ -961,7 +993,7 @@ if [ "$INTERACTIVE" = true ]; then
|
|||||||
SYNTAX_PART="$CMD_NAME [options]"
|
SYNTAX_PART="$CMD_NAME [options]"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Truncate if too long (accounting for actual content, not color codes)
|
# Truncate if too long
|
||||||
if [ ${#DESC_PART} -gt $DESC_WIDTH ]; then
|
if [ ${#DESC_PART} -gt $DESC_WIDTH ]; then
|
||||||
DESC_PART="${DESC_PART:0:$((DESC_WIDTH-3))}..."
|
DESC_PART="${DESC_PART:0:$((DESC_WIDTH-3))}..."
|
||||||
fi
|
fi
|
||||||
@@ -969,25 +1001,29 @@ if [ "$INTERACTIVE" = true ]; then
|
|||||||
SYNTAX_PART="${SYNTAX_PART:0:$((SYNTAX_WIDTH-3))}..."
|
SYNTAX_PART="${SYNTAX_PART:0:$((SYNTAX_WIDTH-3))}..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Calculate padding for right-alignment of syntax
|
# Calculate exact padding needed for right-alignment
|
||||||
# Total visible length = cmd_name + ": " + desc + spaces + syntax
|
# Account for visual width only (not ANSI codes)
|
||||||
CMD_LEN=${#CMD_NAME}
|
CMD_LEN=${#CMD_NAME}
|
||||||
DESC_LEN=${#DESC_PART}
|
DESC_LEN=${#DESC_PART}
|
||||||
SYNTAX_LEN=${#SYNTAX_PART}
|
SYNTAX_LEN=${#SYNTAX_PART}
|
||||||
NEEDED_SPACES=$((TERM_WIDTH - CMD_LEN - 2 - DESC_LEN - SYNTAX_LEN - 5))
|
|
||||||
|
|
||||||
# Ensure at least 2 spaces
|
# Calculate spaces needed: terminal width - all visible characters - separator chars
|
||||||
if [ $NEEDED_SPACES -lt 2 ]; then
|
# Visible: cmd + ": " + desc + syntax = CMD_LEN + 2 + DESC_LEN + SYNTAX_LEN
|
||||||
NEEDED_SPACES=2
|
VISIBLE_WIDTH=$((CMD_LEN + 2 + DESC_LEN + SYNTAX_LEN))
|
||||||
fi
|
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
|
# Create spacing string
|
||||||
SPACES=$(printf '%*s' "$NEEDED_SPACES" '')
|
SPACES=$(printf '%*s' "$NEEDED_SPACES" '')
|
||||||
|
|
||||||
# Format with colors:
|
# Format with colors:
|
||||||
# - Cyan for command name
|
# - Bright cyan for command name
|
||||||
# - White for description
|
# - 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_line="\033[0;36m${CMD_NAME}\033[0m: ${DESC_PART}${SPACES}\033[2;36m${SYNTAX_PART}\033[0m"
|
||||||
|
|
||||||
FORMATTED_COMMANDS+=("$formatted_line")
|
FORMATTED_COMMANDS+=("$formatted_line")
|
||||||
@@ -1030,20 +1066,9 @@ if [ "$INTERACTIVE" = true ]; then
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo ""
|
# Directly show fuzzy flag browser with integrated command info (no wasted space)
|
||||||
echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}"
|
clear
|
||||||
echo -e "${YELLOW}Command:${NC} ${MAGENTA}$CMD${NC}"
|
show_flags "$CMD" "$SYNTAX" "$DESC"
|
||||||
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"
|
|
||||||
|
|
||||||
# After viewing flags, ask if user wants to search another command
|
# After viewing flags, ask if user wants to search another command
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in New Issue
Block a user