UX QUICK REFERENCE - nself-org/cli GitHub Wiki
Quick reference for implementing consistent UX across nself commands.
# Source the UX library in your command file
source "$CLI_SCRIPT_DIR/../lib/utils/ux-standards.sh" 2>/dev/null || true# Missing file
ux_error_file_not_found ".env" "Run 'nself init' to create it"
# Docker not running
ux_error_docker_not_running
# Config missing
ux_error_config_missing
# Port in use
ux_error_port_in_use 5432 "postgres"
# Service failed
ux_error_service_failed "postgres" "Port already in use"
# Invalid input
ux_error_invalid_input "$port" "valid port (1-65535)" "3000, 8080, 5432"# At the start of your command function:
ux_validate_docker || return 1
ux_validate_file_exists "docker-compose.yml" "Run 'nself build'" || return 1
# Validate specific inputs
ux_validate_required "$arg" "argument name" "example value" || return 1
ux_validate_port "$port" || return 1
ux_validate_env "$environment" || return 1# Multi-step operations
ux_progress_init
ux_progress_add "Validating configuration"
ux_progress_add "Building Docker images"
ux_progress_add "Starting services"
# Execute each step
ux_progress_update 0 "running"
# do validation
ux_progress_update 0 "done" "Config valid"
ux_progress_update 1 "running"
# build images
ux_progress_update 1 "done" "Images built"
# Or use spinner for single long operation
ux_spinner_start "Downloading large file"
# download operation
ux_spinner_stop "" "Download complete"show_help() {
ux_help_header "nself mycommand" "Short description"
ux_help_section "Usage"
printf " nself mycommand [OPTIONS]\n\n"
ux_help_section "Options"
ux_help_option "-f, --force" "Force operation"
ux_help_option "-v, --verbose" "Show detailed output"
printf "\n"
ux_help_section "Examples"
ux_help_example "Basic usage" "nself mycommand"
ux_help_example "Force mode" "nself mycommand --force"
ux_help_section "See Also"
printf " ${COLOR_DIM}nself help${COLOR_RESET}\n\n"
}Every error should have:
โ Problem - What went wrong (user-friendly, not technical) โ Fix - How to resolve it (actionable command or steps) โ Context - Relevant details (file paths, service names, etc.)
Example:
โ Problem: Port 5432 is already in use
Context: Service: postgres
Fix: Stop conflicting process: lsof -ti:5432 | xargs kill -9
Before doing ANY operation, validate:
- Required arguments provided
- Docker is running (if needed)
- Configuration files exist
- Port numbers are valid
- Environment names are valid
- File paths are accessible
Q: Do you know how many steps?
- Yes โ Use
ux_progress_*functions - No โ Use
ux_spinner_*functions
Q: Can you show percentage?
- Yes โ Future: Use
ux_progress_bar(not yet implemented) - No โ Use spinner or step-by-step
| Use Case | Color | Function |
|---|---|---|
| Success message | Green | ${COLOR_GREEN}โ${COLOR_RESET} Success |
| Error message | Red | ${COLOR_RED}โ${COLOR_RESET} Error |
| Warning | Yellow | ${COLOR_YELLOW}โ ${COLOR_RESET} Warning |
| Info | Blue | ${COLOR_BLUE}โน${COLOR_RESET} Info |
| Hint/Suggestion | Cyan | ${COLOR_CYAN}โ${COLOR_RESET} Tip |
| Dimmed text | Gray | ${COLOR_DIM}secondary info${COLOR_RESET} |
# Run your command with invalid input - should show helpful error
nself mycommand --invalid
# Run with --help - should show standardized help
nself mycommand --help
# Run actual operation - should show progress
nself mycommand- Errors are helpful and actionable
- Colors display correctly in terminal
- No ANSI codes when piped:
nself mycommand | cat - Progress indicators work
- Help text is clear and complete
โ Don't use echo for errors
echo "Error: File not found" # Badโ Use UX functions
ux_error_file_not_found "$file" "suggestion" # Goodโ Don't use generic errors
if [[ $? -ne 0 ]]; then
echo "Something went wrong" # Bad - not actionable
fiโ Provide context and fix
if [[ $? -ne 0 ]]; then
ux_error_service_failed "postgres" "Check logs: nself logs postgres" # Good
fiโ Don't skip validation
docker compose up # Bad - might fail with cryptic errorโ Validate first
ux_validate_docker || return 1 # Good - clear error message
docker compose up#!/usr/bin/env bash
set -euo pipefail
# Source utilities
CLI_SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
source "$CLI_SCRIPT_DIR/../lib/utils/ux-standards.sh" 2>/dev/null || true
cmd_mycommand() {
local force=false
local service=""
# Parse options
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--force)
force=true
shift
;;
-h|--help)
show_help
return 0
;;
-*)
ux_error_invalid_input "$1" "valid option" "--force, --help"
return 1
;;
*)
service="$1"
shift
;;
esac
done
# Validate inputs
ux_validate_required "$service" "service name" "postgres" || return 1
ux_validate_docker || return 1
# Show progress
ux_progress_init
ux_progress_add "Validating service exists"
ux_progress_add "Performing operation"
# Step 1: Validate
ux_progress_update 0 "running"
if ! docker ps --format "{{.Names}}" | grep -q "$service"; then
ux_progress_update 0 "error" "Service not found"
ux_error_service_not_running "$service"
return 1
fi
ux_progress_update 0 "done"
# Step 2: Operate
ux_progress_update 1 "running"
if docker restart "$service" >/dev/null 2>&1; then
ux_progress_update 1 "done"
ux_success "Service restarted" "Check status: nself status"
else
ux_progress_update 1 "error" "Restart failed"
ux_error_service_failed "$service" "See logs: nself logs $service"
return 1
fi
return 0
}
show_help() {
ux_help_header "nself mycommand" "Perform operation on service"
ux_help_section "Usage"
printf " nself mycommand [OPTIONS] <service>\n\n"
ux_help_section "Options"
ux_help_option "-f, --force" "Force operation"
ux_help_option "-h, --help" "Show help"
printf "\n"
ux_help_section "Examples"
ux_help_example "Basic usage" "nself mycommand postgres"
ux_help_example "Force mode" "nself mycommand --force postgres"
}
export -f cmd_mycommand
cmd_mycommand "$@"Before:
if [[ ! -f ".env" ]]; then
echo "Error: .env not found"
exit 1
fiAfter:
ux_validate_file_exists ".env" "Run 'nself init' to create it" || return 1Output:
โ Problem: File not found: .env
Context: Current directory: /path/to/project
Fix: Run 'nself init' to create it
Before:
if [[ "$port" -lt 1 ]] || [[ "$port" -gt 65535 ]]; then
echo "Invalid port: $port"
exit 1
fiAfter:
ux_validate_port "$port" || return 1Output:
โ Problem: Invalid input: '99999'
Context: Expected: port between 1 and 65535. Example: Common ports: 3000, 8080, 8000
Fix: Provide port between 1 and 65535
Before:
echo "Starting services..."
docker compose up -d
echo "Done"After:
ux_spinner_start "Starting services"
docker compose up -d >/dev/null 2>&1
ux_spinner_stop "" "All services started"Output:
โ Starting services...
โ All services started
Q: When should I use return 1 vs exit 1?
A: Use return 1 in functions (so tests can catch it). Use exit 1 only in the main script.
Q: Do I need to handle NO_COLOR environment variable? A: No, the UX library handles it automatically.
Q: What if the user pipes output? A: The library detects non-terminal output and adjusts. Always use the UX functions.
Q: Can I customize error messages?
A: Yes! Use ux_error for custom messages, or extend the pre-built functions.
Q: How do I test progress indicators?
A: Run in a real terminal. Automated tests should check for โ and step names in output.
- Full documentation:
/docs/development/UX-IMPROVEMENTS-V0.9.8.md - Source code:
/src/lib/utils/ux-standards.sh - Examples: See
stop.sh,start.shfor reference implementations - Testing:
/src/tests/unit/test-ux-standards.sh
Remember: Good UX = Clear errors + Progress feedback + Helpful examples
When in doubt: Ask yourself: "If I were a new user, would this error help me fix the problem?"