diff --git a/zsh/functions/btrfs-helpers.zsh b/zsh/functions/btrfs-helpers.zsh index 21ac512..ce23542 100644 --- a/zsh/functions/btrfs-helpers.zsh +++ b/zsh/functions/btrfs-helpers.zsh @@ -59,9 +59,7 @@ btrfs-usage() { _btrfs_check || return 1 local mount="${1:-$BTRFS_DEFAULT_MOUNT}" - df_print_func_header "Btrfs Filesystem Usage:" - #echo -e "${DF_BLUE}Btrfs Filesystem Usage: ${DF_YELLOW}${mount}${DF_NC}" - echo "" + df_print_func_name "Btrfs Filesystem Usage: ${mount}" sudo btrfs filesystem usage "$mount" -h } @@ -71,9 +69,7 @@ btrfs-subs() { _btrfs_check || return 1 local mount="${1:-$BTRFS_DEFAULT_MOUNT}" - df_print_func_name "Btrfs Subvolumes" - #echo -e "${DF_BLUE}Btrfs Subvolumes" - echo "" + df_print_func_name "Btrfs Subvolumes" echo -e "${DF_CYAN}Subvolume List:${DF_NC}" sudo btrfs subvolume list "$mount" | while read -r line; do @@ -93,7 +89,8 @@ btrfs-balance() { local mount="${1:-$BTRFS_DEFAULT_MOUNT}" local usage="${2:-50}" # Default: rebalance chunks with <50% usage - echo -e "${DF_BLUE}==>${DF_NC} Starting btrfs balance on ${mount}" + df_print_func_name "Btrfs Balance" + echo -e "${DF_YELLOW}⚠${DF_NC} This may take a while and use significant I/O" echo "" @@ -116,6 +113,8 @@ btrfs-balance-status() { _btrfs_check || return 1 local mount="${1:-$BTRFS_DEFAULT_MOUNT}" + df_print_func_name "Btrfs Balance Status" + sudo btrfs balance status "$mount" } @@ -132,9 +131,8 @@ btrfs-balance-cancel() { btrfs-scrub() { _btrfs_check || return 1 local mount="${1:-$BTRFS_DEFAULT_MOUNT}" - df_print_func_name "Btrfs Scrub" - #echo -e "${DF_BLUE}Btrfs Scrub" - echo "" + + df_print_func_name "Btrfs Scrub" # Check if scrub is already running local status=$(sudo btrfs scrub status "$mount" 2>/dev/null) @@ -165,6 +163,8 @@ btrfs-scrub-status() { _btrfs_check || return 1 local mount="${1:-$BTRFS_DEFAULT_MOUNT}" + df_print_func_name "Btrfs Scrub Status" + sudo btrfs scrub status "$mount" } @@ -187,15 +187,16 @@ btrfs-defrag() { return 1 fi - echo -e "${DF_BLUE}==>${DF_NC} Defragmenting: $target" + df_print_func_name "Btrfs Defragment" if [[ -d "$target" ]]; then - echo -e "${DF_YELLOW}⚠${DF_NC} Recursive defrag on directory" + echo -e "${DF_YELLOW}⚠${DF_NC} Recursive defrag on directory: $target" read -q "REPLY?Continue? [y/N]: "; echo [[ ! "$REPLY" =~ ^[Yy]$ ]] && return 0 sudo btrfs filesystem defragment -r -v "$target" else + echo -e "${DF_BLUE}==>${DF_NC} Defragmenting: $target" sudo btrfs filesystem defragment -v "$target" fi @@ -213,9 +214,7 @@ btrfs-compress() { return 1 fi - df_print_func_header "Btrfs Compression Statistics" - #echo -e "${DF_BLUE}Btrfs Compression Statistics${FD_NC}" - echo "" + df_print_func_name "Btrfs Compression Statistics" sudo compsize "$target" } @@ -228,10 +227,10 @@ btrfs-compress() { btrfs-info() { _btrfs_check || return 1 local mount="${1:-$BTRFS_DEFAULT_MOUNT}" - df_print_func_name "Btrfs Filesystem Information" - #echo -e "${DF_BLUE}Btrfs Filesystem Information${DF_NC}" - echo -e "\n${DF_CYAN}Filesystem Show:${DF_NC}" + df_print_func_name "Btrfs Filesystem Information" + + echo -e "${DF_CYAN}Filesystem Show:${DF_NC}" sudo btrfs filesystem show "$mount" echo -e "\n${DF_CYAN}Filesystem df:${DF_NC}" @@ -249,8 +248,6 @@ btrfs-health() { local mount="${1:-$BTRFS_DEFAULT_MOUNT}" df_print_func_name "Btrfs Health Check" - #echo -e "${DF_BLUE}Btrfs Health Check${DF_NC}" - echo "" local issues=0 @@ -322,15 +319,22 @@ btrfs-snap-usage() { _btrfs_check || return 1 df_print_func_name "Snapshot Disk Space Usage" - #echo -e "${DF_BLUE}Snapshot Space Usage${DF_NC}" - echo "" if [[ -d "/.snapshots" ]]; then echo -e "${DF_CYAN}Snapshot Directory:${DF_NC}" - sudo du -sh /.snapshots 2>/dev/null || echo " Unable to calculate" + # Use timeout to prevent hanging, and run in background with wait + local size + size=$(timeout 10 sudo du -sh /.snapshots 2>/dev/null | cut -f1) + if [[ -n "$size" ]]; then + echo " $size" + else + echo " Unable to calculate (timeout or error)" + fi - echo -e "\n${DF_CYAN}Individual Snapshots:${DF_NC}" - sudo du -sh /.snapshots/*/ 2>/dev/null | sort -h | tail -10 | sed 's/^/ /' + echo -e "\n${DF_CYAN}Individual Snapshots (top 10 by size):${DF_NC}" + # List snapshots with timeout protection + timeout 30 sudo du -sh /.snapshots/*/ 2>/dev/null | sort -h | tail -10 | sed 's/^/ /' || \ + echo " Unable to list snapshots" else echo -e "${DF_YELLOW}⚠${DF_NC} No /.snapshots directory found" fi @@ -348,8 +352,7 @@ btrfs-maintain() { local mount="${1:-$BTRFS_DEFAULT_MOUNT}" df_print_func_name "Btrfs Maintenance Routine" - #echo -e "${DF_BLUE}Btrfs Maintenance Routine${DF_NC}" - echo "" + echo "This will perform:" echo " 1. Health check" echo " 2. Balance (low usage chunks)" @@ -391,7 +394,6 @@ alias btrc='btrfs-compress' btrfs-help() { df_print_func_name "Btrfs Helper Commands" - #echo -e "${DF_BLUE}Btrfs Helper Commands${DF_NC}" cat << 'EOF' diff --git a/zsh/functions/command-palette.zsh b/zsh/functions/command-palette.zsh index 4d710f3..129fba5 100644 --- a/zsh/functions/command-palette.zsh +++ b/zsh/functions/command-palette.zsh @@ -231,10 +231,10 @@ bookmark() { case "$bm_name" in list|ls) + df_print_func_name "Bookmarks" if [[ -s "$PALETTE_BOOKMARKS_FILE" ]]; then - echo "Bookmarks:" while IFS='|' read -r stored_name stored_path || [[ -n "$stored_name" ]]; do - [[ -n "$stored_name" ]] && echo " $stored_name → $stored_path" + [[ -n "$stored_name" ]] && echo -e " ${DF_GREEN}●${DF_NC} $stored_name → $stored_path" done < "$PALETTE_BOOKMARKS_FILE" else echo "No bookmarks yet" @@ -251,7 +251,7 @@ bookmark() { local temp_file="${PALETTE_BOOKMARKS_FILE}.tmp.$$" grep -v "^${to_delete}|" "$PALETTE_BOOKMARKS_FILE" > "$temp_file" 2>/dev/null || true mv -f "$temp_file" "$PALETTE_BOOKMARKS_FILE" - echo "Deleted: $to_delete" + echo -e "${DF_GREEN}✓${DF_NC} Deleted: $to_delete" else echo "No bookmarks to delete" fi @@ -265,7 +265,7 @@ bookmark() { fi # Add new bookmark echo "${bm_name}|${bm_path}" >> "$PALETTE_BOOKMARKS_FILE" - echo "Bookmarked: $bm_name → $bm_path" + echo -e "${DF_GREEN}✓${DF_NC} Bookmarked: $bm_name → $bm_path" ;; esac } diff --git a/zsh/functions/password-manager.zsh b/zsh/functions/password-manager.zsh index 70d6701..6ebc91b 100644 --- a/zsh/functions/password-manager.zsh +++ b/zsh/functions/password-manager.zsh @@ -77,6 +77,7 @@ pw() { case "$cmd" in list|ls|l) + df_print_func_name "LastPass Vault" _lp_list ;; @@ -96,6 +97,7 @@ pw() { search|find|s) local query="$1" [[ -z "$query" ]] && { echo "Usage: pw search "; return 1; } + df_print_func_name "LastPass Search: $query" _lp_search "$query" ;; @@ -110,21 +112,20 @@ pw() { echo -n "$value" | xclip -selection clipboard 2>/dev/null || \ echo -n "$value" | xsel --clipboard 2>/dev/null || \ { echo "Could not copy to clipboard"; return 1; } - echo "Copied to clipboard" + echo -e "${DF_GREEN}✓${DF_NC} Copied to clipboard" else - echo "Item not found or empty" + echo -e "${DF_RED}✗${DF_NC} Item not found or empty" return 1 fi ;; lock) lpass logout -f 2>/dev/null - echo "Logged out of LastPass" + echo -e "${DF_GREEN}✓${DF_NC} Logged out of LastPass" ;; help|--help|-h|*) - echo -e "${DF_BLUE}Password Manager CLI (LastPass)${DF_NC}" - echo + df_print_func_name "Password Manager CLI" echo "Usage: pw [args]" echo echo "Commands:" @@ -190,7 +191,7 @@ if command -v fzf &>/dev/null; then if [[ -n "$otp" ]]; then echo -n "$otp" | xclip -selection clipboard 2>/dev/null || \ echo -n "$otp" | xsel --clipboard 2>/dev/null - echo "OTP copied: $otp" + echo -e "${DF_GREEN}✓${DF_NC} OTP copied: $otp" fi fi } diff --git a/zsh/functions/python-templates.zsh b/zsh/functions/python-templates.zsh index cc5cee1..c464203 100644 --- a/zsh/functions/python-templates.zsh +++ b/zsh/functions/python-templates.zsh @@ -10,11 +10,16 @@ # py-fastapi # Create FastAPI project # py-data # Create data science project # py-cli # Create CLI tool project -# -# Add to .zshrc: -# source ~/.dotfiles/zsh/functions/python-templates.zsh # ============================================================================ +# Source shared colors (with fallback) +source "${0:A:h}/../lib/colors.zsh" 2>/dev/null || \ +source "$HOME/.dotfiles/zsh/lib/colors.zsh" 2>/dev/null || { + typeset -g DF_GREEN=$'\033[0;32m' DF_BLUE=$'\033[0;34m' + typeset -g DF_YELLOW=$'\033[1;33m' DF_CYAN=$'\033[0;36m' + typeset -g DF_RED=$'\033[0;31m' DF_NC=$'\033[0m' +} + # ============================================================================ # Configuration # ============================================================================ @@ -25,37 +30,30 @@ typeset -g PY_TEMPLATE_VENV_NAME="${PY_TEMPLATE_VENV_NAME:-venv}" typeset -g PY_TEMPLATE_USE_POETRY="${PY_TEMPLATE_USE_POETRY:-false}" typeset -g PY_TEMPLATE_GIT_INIT="${PY_TEMPLATE_GIT_INIT:-true}" -# Colors -typeset -g PY_GREEN=$'\033[0;32m' -typeset -g PY_BLUE=$'\033[0;34m' -typeset -g PY_YELLOW=$'\033[1;33m' -typeset -g PY_CYAN=$'\033[0;36m' -typeset -g PY_NC=$'\033[0m' - # ============================================================================ # Helper Functions # ============================================================================ _py_print_step() { - echo -e "${PY_BLUE}==>${PY_NC} $1" + echo -e "${DF_BLUE}==>${DF_NC} $1" } _py_print_success() { - echo -e "${PY_GREEN}✓${PY_NC} $1" + echo -e "${DF_GREEN}✓${DF_NC} $1" } _py_print_info() { - echo -e "${PY_CYAN}ℹ${PY_NC} $1" + echo -e "${DF_CYAN}ℹ${DF_NC} $1" } _py_check_project_name() { local name="$1" if [[ -z "$name" ]]; then - echo -e "${PY_YELLOW}⚠${PY_NC} Project name required" + echo -e "${DF_YELLOW}⚠${DF_NC} Project name required" return 1 fi if [[ -d "$name" ]]; then - echo -e "${PY_YELLOW}⚠${PY_NC} Directory '$name' already exists" + echo -e "${DF_YELLOW}⚠${DF_NC} Directory '$name' already exists" return 1 fi return 0 @@ -63,7 +61,6 @@ _py_check_project_name() { _py_create_venv() { local project_dir="$1" - _py_print_step "Creating virtual environment" if [[ "$PY_TEMPLATE_USE_POETRY" == "true" ]] && command -v poetry &>/dev/null; then @@ -79,172 +76,35 @@ _py_create_venv() { _py_create_gitignore() { local project_dir="$1" - cat > "$project_dir/.gitignore" << 'EOF' -# Python __pycache__/ *.py[cod] *$py.class *.so .Python build/ -develop-eggs/ dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ *.egg-info/ -.installed.cfg -*.egg - -# Virtual Environment venv/ env/ -ENV/ .venv - -# IDEs .vscode/ .idea/ *.swp -*.swo -*~ -.DS_Store - -# Testing .pytest_cache/ .coverage htmlcov/ -.tox/ -.hypothesis/ - -# Environment variables .env -.env.local -.env.*.local - -# Logs *.log -logs/ - -# Database *.db -*.sqlite *.sqlite3 - -# Distribution / packaging -.Python -build/ -dist/ -*.egg-info/ - -# Poetry -poetry.lock - -# Jupyter Notebook -.ipynb_checkpoints -*.ipynb - -# MyPy .mypy_cache/ -.dmypy.json -dmypy.json - -# Type checking -.pyre/ -.pytype/ EOF - _py_print_success "Created .gitignore" } -_py_create_readme() { - local project_name="$1" - local project_dir="$2" - local description="$3" - - cat > "$project_dir/README.md" << EOF -# $project_name - -$description - -## Setup - -### Using venv - -\`\`\`bash -# Create virtual environment -python3 -m venv venv - -# Activate virtual environment -source venv/bin/activate # Linux/Mac -# or -venv\\Scripts\\activate # Windows - -# Install dependencies -pip install -r requirements.txt -\`\`\` - -### Using Poetry (alternative) - -\`\`\`bash -poetry install -poetry shell -\`\`\` - -## Development - -\`\`\`bash -# Activate virtual environment -source venv/bin/activate - -# Run the application -python main.py -\`\`\` - -## Testing - -\`\`\`bash -# Install dev dependencies -pip install pytest pytest-cov - -# Run tests -pytest - -# Run tests with coverage -pytest --cov=src tests/ -\`\`\` - -## Project Structure - -\`\`\` -$project_name/ -├── src/ # Source code -├── tests/ # Test files -├── docs/ # Documentation -├── .gitignore -├── README.md -├── requirements.txt -└── setup.py -\`\`\` - -## License - -MIT -EOF - - _py_print_success "Created README.md" -} - _py_init_git() { local project_dir="$1" - if [[ "$PY_TEMPLATE_GIT_INIT" == "true" ]]; then cd "$project_dir" git init @@ -257,12 +117,9 @@ _py_init_git() { _py_show_next_steps() { local project_name="$1" local has_venv="$2" - - echo - echo -e "${PY_CYAN}Next steps:${PY_NC}" echo + echo -e "${DF_CYAN}Next steps:${DF_NC}" echo " cd $project_name" - if [[ "$has_venv" == "true" ]]; then if [[ "$PY_TEMPLATE_USE_POETRY" == "true" ]]; then echo " poetry shell" @@ -270,7 +127,6 @@ _py_show_next_steps() { echo " source $PY_TEMPLATE_VENV_NAME/bin/activate" fi fi - echo " # Start coding!" echo } @@ -281,87 +137,34 @@ _py_show_next_steps() { py-new() { local project_name="$1" - local project_type="${2:-basic}" - _py_check_project_name "$project_name" || return 1 - echo -e "${PY_BLUE}╔════════════════════════════════════════════════════════════╗${PY_NC}" - echo -e "${PY_BLUE}║${PY_NC} Creating Python Project: $project_name" - echo -e "${PY_BLUE}╚════════════════════════════════════════════════════════════╝${PY_NC}" - echo + df_print_func_name "Python Project: $project_name" - # Create project structure _py_print_step "Creating project structure" mkdir -p "$project_name"/{src,tests,docs} - - # Create __init__.py files touch "$project_name/src/__init__.py" touch "$project_name/tests/__init__.py" - # Create main.py cat > "$project_name/src/main.py" << 'EOF' #!/usr/bin/env python3 -""" -Main module for the application. -""" +"""Main module.""" def main(): - """Main entry point.""" print("Hello from Python!") if __name__ == "__main__": main() EOF - # Create basic test - cat > "$project_name/tests/test_main.py" << 'EOF' -"""Tests for main module.""" -import pytest -from src.main import main - -def test_main(): - """Test main function runs without error.""" - main() -EOF - - # Create requirements.txt cat > "$project_name/requirements.txt" << 'EOF' # Production dependencies - -# Development dependencies (uncomment as needed) -# pytest>=7.0.0 -# pytest-cov>=4.0.0 -# black>=23.0.0 -# flake8>=6.0.0 -# mypy>=1.0.0 -# pylint>=2.17.0 -EOF - - # Create setup.py - cat > "$project_name/setup.py" << EOF -from setuptools import setup, find_packages - -setup( - name="$project_name", - version="0.1.0", - packages=find_packages(), - install_requires=[], - python_requires=">=3.8", -) +# Development: pytest, black, flake8, mypy EOF _py_print_success "Project structure created" - - # Create virtual environment _py_create_venv "$project_name" - - # Create .gitignore _py_create_gitignore "$project_name" - - # Create README - _py_create_readme "$project_name" "$project_name" "A Python project" - - # Initialize git _py_init_git "$project_name" echo @@ -375,133 +178,32 @@ EOF py-django() { local project_name="$1" - _py_check_project_name "$project_name" || return 1 - echo -e "${PY_BLUE}╔════════════════════════════════════════════════════════════╗${PY_NC}" - echo -e "${PY_BLUE}║${PY_NC} Creating Django Project: $project_name" - echo -e "${PY_BLUE}╚════════════════════════════════════════════════════════════╝${PY_NC}" - echo + df_print_func_name "Django Project: $project_name" - # Create project directory mkdir -p "$project_name" - - # Create virtual environment first _py_create_venv "$project_name" - # Install Django _py_print_step "Installing Django" cd "$project_name" + "$PY_TEMPLATE_VENV_NAME/bin/pip" install django - if [[ "$PY_TEMPLATE_USE_POETRY" == "true" ]]; then - poetry add django - else - "$PY_TEMPLATE_VENV_NAME/bin/pip" install django - fi - - # Create Django project _py_print_step "Creating Django project structure" + "$PY_TEMPLATE_VENV_NAME/bin/django-admin" startproject config . - if [[ "$PY_TEMPLATE_USE_POETRY" == "true" ]]; then - poetry run django-admin startproject config . - else - "$PY_TEMPLATE_VENV_NAME/bin/django-admin" startproject config . - fi - - # Create requirements.txt cat > "requirements.txt" << 'EOF' Django>=4.2.0 python-decouple>=3.8 -psycopg2-binary>=2.9.0 # PostgreSQL adapter -django-environ>=0.11.0 - -# Development -django-debug-toolbar>=4.2.0 -django-extensions>=3.2.0 EOF - # Create .env.example - cat > ".env.example" << 'EOF' -SECRET_KEY=your-secret-key-here -DEBUG=True -ALLOWED_HOSTS=localhost,127.0.0.1 - -# Database -DATABASE_URL=sqlite:///db.sqlite3 -# DATABASE_URL=postgresql://user:password@localhost:5432/dbname -EOF - - # Create README - cat > "README.md" << EOF -# $project_name - -A Django web application. - -## Setup - -\`\`\`bash -# Create and activate virtual environment -python3 -m venv venv -source venv/bin/activate - -# Install dependencies -pip install -r requirements.txt - -# Copy environment file -cp .env.example .env -# Edit .env with your settings - -# Run migrations -python manage.py migrate - -# Create superuser -python manage.py createsuperuser - -# Run development server -python manage.py runserver -\`\`\` - -## Development - -\`\`\`bash -# Create a new app -python manage.py startapp myapp - -# Make migrations -python manage.py makemigrations - -# Run tests -python manage.py test - -# Collect static files -python manage.py collectstatic -\`\`\` - -## Project Structure - -\`\`\` -$project_name/ -├── config/ # Django settings -├── apps/ # Django apps -├── static/ # Static files -├── templates/ # HTML templates -├── media/ # Uploaded files -├── manage.py -└── requirements.txt -\`\`\` -EOF - - # Create directories mkdir -p apps static templates media - _py_create_gitignore "." _py_init_git "." - cd .. echo _py_print_success "Django project '$project_name' created!" - _py_print_info "Don't forget to set SECRET_KEY in .env" _py_show_next_steps "$project_name" "true" } @@ -511,71 +213,41 @@ EOF py-flask() { local project_name="$1" - _py_check_project_name "$project_name" || return 1 - echo -e "${PY_BLUE}╔════════════════════════════════════════════════════════════╗${PY_NC}" - echo -e "${PY_BLUE}║${PY_NC} Creating Flask Project: $project_name" - echo -e "${PY_BLUE}╚════════════════════════════════════════════════════════════╝${PY_NC}" - echo + df_print_func_name "Flask Project: $project_name" - # Create project structure - mkdir -p "$project_name"/{app/{templates,static/{css,js,img}},tests} - - # Create virtual environment + mkdir -p "$project_name"/{app/{templates,static/{css,js}},tests} _py_create_venv "$project_name" cd "$project_name" - - # Install Flask _py_print_step "Installing Flask" - if [[ "$PY_TEMPLATE_USE_POETRY" == "true" ]]; then - poetry add flask - else - "$PY_TEMPLATE_VENV_NAME/bin/pip" install flask - fi + "$PY_TEMPLATE_VENV_NAME/bin/pip" install flask - # Create app/__init__.py cat > "app/__init__.py" << 'EOF' -"""Flask application factory.""" from flask import Flask def create_app(config=None): - """Create and configure the Flask application.""" app = Flask(__name__) - if config: app.config.from_object(config) - - # Register blueprints from app.routes import main app.register_blueprint(main) - return app EOF - # Create app/routes.py cat > "app/routes.py" << 'EOF' -"""Application routes.""" from flask import Blueprint, render_template main = Blueprint('main', __name__) @main.route('/') def index(): - """Home page.""" return render_template('index.html') - -@main.route('/api/health') -def health(): - """Health check endpoint.""" - return {'status': 'healthy'} EOF - # Create app.py cat > "app.py" << 'EOF' #!/usr/bin/env python3 -"""Flask application entry point.""" from app import create_app app = create_app() @@ -585,132 +257,19 @@ if __name__ == '__main__': EOF chmod +x app.py - # Create base template - cat > "app/templates/base.html" << 'EOF' - - - - - - {% block title %}Flask App{% endblock %} - - - -
- {% block content %}{% endblock %} -
- - - -EOF - - # Create index template cat > "app/templates/index.html" << 'EOF' -{% extends "base.html" %} - -{% block title %}Home{% endblock %} - -{% block content %} -

Welcome to Flask!

-

Your app is running.

-{% endblock %} + +Flask App +

Welcome to Flask!

EOF - # Create basic CSS - cat > "app/static/css/style.css" << 'EOF' -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; - line-height: 1.6; - padding: 2rem; -} - -main { - max-width: 1200px; - margin: 0 auto; -} -EOF - - # Create basic JS - cat > "app/static/js/main.js" << 'EOF' -console.log('Flask app loaded'); -EOF - - # Create requirements.txt cat > "requirements.txt" << 'EOF' Flask>=3.0.0 python-decouple>=3.8 - -# Development -flask-debugtoolbar>=0.14.0 -EOF - - # Create .env.example - cat > ".env.example" << 'EOF' -FLASK_APP=app.py -FLASK_ENV=development -SECRET_KEY=your-secret-key-here -EOF - - # Create README - cat > "README.md" << EOF -# $project_name - -A Flask web application. - -## Setup - -\`\`\`bash -# Create and activate virtual environment -python3 -m venv venv -source venv/bin/activate - -# Install dependencies -pip install -r requirements.txt - -# Copy environment file -cp .env.example .env - -# Run the application -python app.py -\`\`\` - -Visit: http://localhost:5000 - -## Development - -\`\`\`bash -# Run with auto-reload -export FLASK_ENV=development -python app.py - -# Run tests -pytest -\`\`\` - -## Project Structure - -\`\`\` -$project_name/ -├── app/ -│ ├── __init__.py -│ ├── routes.py -│ ├── templates/ -│ └── static/ -├── tests/ -├── app.py -└── requirements.txt -\`\`\` EOF _py_create_gitignore "." _py_init_git "." - cd .. echo @@ -724,216 +283,63 @@ EOF py-fastapi() { local project_name="$1" - _py_check_project_name "$project_name" || return 1 - echo -e "${PY_BLUE}╔════════════════════════════════════════════════════════════╗${PY_NC}" - echo -e "${PY_BLUE}║${PY_NC} Creating FastAPI Project: $project_name" - echo -e "${PY_BLUE}╚════════════════════════════════════════════════════════════╝${PY_NC}" - echo + df_print_func_name "FastAPI Project: $project_name" - # Create project structure - mkdir -p "$project_name"/{app/{api,models,schemas,services},tests} - - # Create virtual environment + mkdir -p "$project_name"/{app/{api,models,schemas},tests} _py_create_venv "$project_name" cd "$project_name" + _py_print_step "Installing FastAPI" + "$PY_TEMPLATE_VENV_NAME/bin/pip" install fastapi uvicorn[standard] pydantic - # Install FastAPI - _py_print_step "Installing FastAPI and dependencies" - if [[ "$PY_TEMPLATE_USE_POETRY" == "true" ]]; then - poetry add fastapi uvicorn pydantic - else - "$PY_TEMPLATE_VENV_NAME/bin/pip" install fastapi uvicorn[standard] pydantic - fi - - # Create app/__init__.py touch "app/__init__.py" - # Create main.py cat > "app/main.py" << 'EOF' -"""FastAPI application.""" from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from app.api import router -app = FastAPI( - title="My API", - description="FastAPI application", - version="0.1.0" -) +app = FastAPI(title="My API", version="0.1.0") -# CORS app.add_middleware( CORSMiddleware, allow_origins=["*"], - allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) -# Include routers -app.include_router(router, prefix="/api") - @app.get("/") def root(): - """Root endpoint.""" return {"message": "Welcome to FastAPI"} @app.get("/health") def health(): - """Health check.""" return {"status": "healthy"} EOF - # Create api/__init__.py - cat > "app/api/__init__.py" << 'EOF' -"""API routes.""" -from fastapi import APIRouter + cat > "run.py" << 'EOF' +#!/usr/bin/env python3 +import uvicorn -router = APIRouter() - -@router.get("/items") -def list_items(): - """List all items.""" - return {"items": []} - -@router.get("/items/{item_id}") -def get_item(item_id: int): - """Get a specific item.""" - return {"id": item_id, "name": f"Item {item_id}"} +if __name__ == "__main__": + uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True) EOF + chmod +x run.py - # Create schemas/__init__.py - cat > "app/schemas/__init__.py" << 'EOF' -"""Pydantic schemas.""" -from pydantic import BaseModel - -class ItemBase(BaseModel): - """Base item schema.""" - name: str - description: str | None = None - -class ItemCreate(ItemBase): - """Schema for creating items.""" - pass - -class Item(ItemBase): - """Full item schema.""" - id: int - - class Config: - from_attributes = True -EOF - - # Create models, services __init__.py - touch "app/models/__init__.py" - touch "app/services/__init__.py" - - # Create requirements.txt cat > "requirements.txt" << 'EOF' fastapi>=0.104.0 uvicorn[standard]>=0.24.0 pydantic>=2.5.0 -python-decouple>=3.8 - -# Optional -# sqlalchemy>=2.0.0 -# alembic>=1.12.0 -# python-jose[cryptography]>=3.3.0 -# passlib[bcrypt]>=1.7.4 - -# Development -pytest>=7.4.0 -httpx>=0.25.0 -EOF - - # Create run.py - cat > "run.py" << 'EOF' -#!/usr/bin/env python3 -"""Run the FastAPI application.""" -import uvicorn - -if __name__ == "__main__": - uvicorn.run( - "app.main:app", - host="0.0.0.0", - port=8000, - reload=True - ) -EOF - chmod +x run.py - - # Create .env.example - cat > ".env.example" << 'EOF' -API_KEY=your-api-key -DATABASE_URL=sqlite:///./app.db -SECRET_KEY=your-secret-key -EOF - - # Create README - cat > "README.md" << EOF -# $project_name - -A FastAPI application. - -## Setup - -\`\`\`bash -# Create and activate virtual environment -python3 -m venv venv -source venv/bin/activate - -# Install dependencies -pip install -r requirements.txt - -# Copy environment file -cp .env.example .env - -# Run the application -python run.py -\`\`\` - -Visit: -- API: http://localhost:8000 -- Docs: http://localhost:8000/docs -- ReDoc: http://localhost:8000/redoc - -## Development - -\`\`\`bash -# Run with auto-reload -uvicorn app.main:app --reload - -# Run tests -pytest -\`\`\` - -## Project Structure - -\`\`\` -$project_name/ -├── app/ -│ ├── api/ # API routes -│ ├── models/ # Database models -│ ├── schemas/ # Pydantic schemas -│ ├── services/ # Business logic -│ └── main.py -├── tests/ -├── run.py -└── requirements.txt -\`\`\` EOF _py_create_gitignore "." _py_init_git "." - cd .. echo _py_print_success "FastAPI project '$project_name' created!" - _py_print_info "Docs will be at: http://localhost:8000/docs" + _py_print_info "Docs at: http://localhost:8000/docs" _py_show_next_steps "$project_name" "true" } @@ -943,263 +349,43 @@ EOF py-data() { local project_name="$1" - _py_check_project_name "$project_name" || return 1 - echo -e "${PY_BLUE}╔════════════════════════════════════════════════════════════╗${PY_NC}" - echo -e "${PY_BLUE}║${PY_NC} Creating Data Science Project: $project_name" - echo -e "${PY_BLUE}╚════════════════════════════════════════════════════════════╝${PY_NC}" - echo + df_print_func_name "Data Science Project: $project_name" - # Create project structure - mkdir -p "$project_name"/{data/{raw,processed,external},notebooks,src,models,reports/{figures}} - - # Create virtual environment + mkdir -p "$project_name"/{data/{raw,processed},notebooks,src,models,reports/figures} _py_create_venv "$project_name" cd "$project_name" - - # Install data science packages _py_print_step "Installing data science packages" - if [[ "$PY_TEMPLATE_USE_POETRY" == "true" ]]; then - poetry add pandas numpy matplotlib seaborn jupyter - else - "$PY_TEMPLATE_VENV_NAME/bin/pip" install pandas numpy matplotlib seaborn jupyter notebook - fi + "$PY_TEMPLATE_VENV_NAME/bin/pip" install pandas numpy matplotlib seaborn jupyter + + touch "src/__init__.py" + touch data/raw/.gitkeep data/processed/.gitkeep - # Create requirements.txt cat > "requirements.txt" << 'EOF' -# Core pandas>=2.1.0 numpy>=1.24.0 matplotlib>=3.8.0 seaborn>=0.13.0 - -# Machine Learning -scikit-learn>=1.3.0 -# tensorflow>=2.14.0 -# torch>=2.1.0 - -# Jupyter jupyter>=1.0.0 -notebook>=7.0.0 -ipykernel>=6.25.0 - -# Utilities -python-decouple>=3.8 - -# Development -pytest>=7.4.0 -black>=23.9.0 -pylint>=2.17.0 -EOF - - # Create starter notebook - cat > "notebooks/01_exploration.ipynb" << 'EOF' -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Data Exploration\n", - "\n", - "Initial data exploration and analysis." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import seaborn as sns\n", - "\n", - "%matplotlib inline\n", - "sns.set_style('whitegrid')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Load data\n", - "# df = pd.read_csv('../data/raw/data.csv')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} -EOF - - # Create src/__init__.py - touch "src/__init__.py" - - # Create example processing script - cat > "src/process_data.py" << 'EOF' -#!/usr/bin/env python3 -"""Data processing utilities.""" -import pandas as pd - -def load_raw_data(filepath): - """Load raw data from file.""" - return pd.read_csv(filepath) - -def clean_data(df): - """Clean and preprocess data.""" - # Remove duplicates - df = df.drop_duplicates() - - # Handle missing values - df = df.dropna() - - return df - -def main(): - """Main processing pipeline.""" - # Load data - df = load_raw_data('data/raw/data.csv') - - # Clean data - df = clean_data(df) - - # Save processed data - df.to_csv('data/processed/data_clean.csv', index=False) - print(f"Processed {len(df)} rows") - -if __name__ == '__main__': - main() -EOF - - # Create README - cat > "README.md" << EOF -# $project_name - -A data science project. - -## Setup - -\`\`\`bash -# Create and activate virtual environment -python3 -m venv venv -source venv/bin/activate - -# Install dependencies -pip install -r requirements.txt - -# Install Jupyter kernel -python -m ipykernel install --user --name=$project_name -\`\`\` - -## Usage - -\`\`\`bash -# Start Jupyter Notebook -jupyter notebook - -# Run processing script -python src/process_data.py -\`\`\` - -## Project Structure - -\`\`\` -$project_name/ -├── data/ -│ ├── raw/ # Original, immutable data -│ ├── processed/ # Cleaned, processed data -│ └── external/ # External data sources -├── notebooks/ # Jupyter notebooks -├── src/ # Source code -├── models/ # Trained models -├── reports/ # Analysis reports -│ └── figures/ # Generated graphics -└── requirements.txt -\`\`\` - -## Data - -Place your raw data files in \`data/raw/\`. - -## Notebooks - -1. \`01_exploration.ipynb\` - Initial data exploration -2. Add more notebooks as needed - -## Guidelines - -- Keep raw data immutable -- Document your analysis in notebooks -- Extract reusable code to \`src/\` -- Save processed data to \`data/processed/\` -- Save figures to \`reports/figures/\` -EOF - - # Create data README - cat > "data/README.md" << 'EOF' -# Data Directory - -## Structure - -- `raw/` - Original, immutable data dump -- `processed/` - Cleaned and processed data -- `external/` - Data from third party sources - -## Guidelines - -- Never modify files in `raw/` -- Document data sources -- Include data dictionaries where applicable +scikit-learn>=1.3.0 EOF _py_create_gitignore "." - - # Update gitignore for data science cat >> ".gitignore" << 'EOF' - -# Data Science specific *.pkl *.h5 -*.hdf5 *.parquet - -# Data files (comment out if you want to track them) data/raw/* data/processed/* !data/raw/.gitkeep !data/processed/.gitkeep - -# Model files models/*.pkl -models/*.h5 - -# Jupyter .ipynb_checkpoints - -# Large files -*.csv -*.tsv -*.dat EOF - # Create .gitkeep files - touch data/raw/.gitkeep data/processed/.gitkeep data/external/.gitkeep - _py_init_git "." - cd .. echo @@ -1214,40 +400,23 @@ EOF py-cli() { local project_name="$1" - _py_check_project_name "$project_name" || return 1 - echo -e "${PY_BLUE}╔════════════════════════════════════════════════════════════╗${PY_NC}" - echo -e "${PY_BLUE}║${PY_NC} Creating CLI Tool Project: $project_name" - echo -e "${PY_BLUE}╚════════════════════════════════════════════════════════════╝${PY_NC}" - echo + df_print_func_name "CLI Tool Project: $project_name" - # Create project structure mkdir -p "$project_name"/{src/$project_name,tests} - - # Create virtual environment _py_create_venv "$project_name" cd "$project_name" + _py_print_step "Installing click" + "$PY_TEMPLATE_VENV_NAME/bin/pip" install click - # Install click - _py_print_step "Installing click for CLI" - if [[ "$PY_TEMPLATE_USE_POETRY" == "true" ]]; then - poetry add click - else - "$PY_TEMPLATE_VENV_NAME/bin/pip" install click - fi - - # Create package __init__.py cat > "src/$project_name/__init__.py" << 'EOF' -"""CLI tool package.""" __version__ = "0.1.0" EOF - # Create cli.py cat > "src/$project_name/cli.py" << 'EOF' #!/usr/bin/env python3 -"""Command-line interface.""" import click @click.group() @@ -1258,153 +427,33 @@ def cli(): @cli.command() @click.argument('name', default='World') -@click.option('--greeting', default='Hello', help='Greeting to use') -def greet(name, greeting): +def greet(name): """Greet someone.""" - click.echo(f"{greeting}, {name}!") - -@cli.command() -@click.option('--count', default=1, help='Number of times to repeat') -@click.argument('message') -def repeat(message, count): - """Repeat a message.""" - for _ in range(count): - click.echo(message) - -if __name__ == '__main__': - cli() -EOF - chmod +x "src/$project_name/cli.py" - - # Create __main__.py - cat > "src/$project_name/__main__.py" << EOF -"""Allow running as python -m $project_name""" -from $project_name.cli import cli + click.echo(f"Hello, {name}!") if __name__ == '__main__': cli() EOF - # Create setup.py cat > "setup.py" << EOF from setuptools import setup, find_packages -with open("README.md", "r", encoding="utf-8") as fh: - long_description = fh.read() - setup( name="$project_name", version="0.1.0", - author="Your Name", - author_email="you@example.com", - description="A command-line tool", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/yourusername/$project_name", packages=find_packages(where="src"), package_dir={"": "src"}, - classifiers=[ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - ], - python_requires=">=3.8", - install_requires=[ - "click>=8.0.0", - ], - entry_points={ - "console_scripts": [ - "$project_name=$project_name.cli:cli", - ], - }, + install_requires=["click>=8.0.0"], + entry_points={"console_scripts": ["$project_name=$project_name.cli:cli"]}, ) EOF - # Create requirements.txt cat > "requirements.txt" << 'EOF' click>=8.1.0 - -# Development -pytest>=7.4.0 -black>=23.9.0 -EOF - - # Create README - cat > "README.md" << EOF -# $project_name - -A command-line tool built with Python and Click. - -## Installation - -\`\`\`bash -# Development installation -pip install -e . - -# Or install from source -pip install . -\`\`\` - -## Usage - -\`\`\`bash -# Show help -$project_name --help - -# Example commands -$project_name greet -$project_name greet Alice -$project_name greet --greeting "Hi" Bob -$project_name repeat "Hello" --count 3 -\`\`\` - -## Development - -\`\`\`bash -# Create virtual environment -python3 -m venv venv -source venv/bin/activate - -# Install in development mode -pip install -e . - -# Run tests -pytest - -# Format code -black src/ -\`\`\` - -## Project Structure - -\`\`\` -$project_name/ -├── src/ -│ └── $project_name/ -│ ├── __init__.py -│ ├── __main__.py -│ └── cli.py -├── tests/ -├── setup.py -└── requirements.txt -\`\`\` - -## Adding Commands - -Add new commands to \`src/$project_name/cli.py\`: - -\`\`\`python -@cli.command() -@click.argument('arg') -def mycommand(arg): - """Description of command.""" - click.echo(f"Running with: {arg}") -\`\`\` EOF _py_create_gitignore "." _py_init_git "." - cd .. echo @@ -1437,10 +486,3 @@ venv() { return 1 fi } - -# ============================================================================ -# Initialization Message -# ============================================================================ - -# Uncomment to show on load: -# echo "Python templates loaded. Use: py-new, py-django, py-flask, py-fastapi, py-data, py-cli" diff --git a/zsh/functions/snapper.zsh b/zsh/functions/snapper.zsh index 4992f6d..75d9237 100644 --- a/zsh/functions/snapper.zsh +++ b/zsh/functions/snapper.zsh @@ -18,9 +18,7 @@ snap-create() { local snap_config="root" local limine_conf="/boot/limine.conf" - echo -e "\n${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Snapper Snapshot Creation & Validation ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}\n" + df_print_func_name "Snapper Snapshot Creation" if [[ -z "$description" ]]; then echo -e "${DF_YELLOW}⚠${DF_NC} No description provided" @@ -90,21 +88,20 @@ snap-create() { validation_passed=false fi - echo -e "\n${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Summary ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}" - echo -e "Snapshot Number: #$snapshot_num" - echo -e "Description: \"$description\"" - echo -e "Config: $snap_config" - echo -e "Before entries: $before_entries" - echo -e "After entries: $after_entries" + echo "" + echo -e "${DF_CYAN}Summary:${DF_NC}" + echo -e " Snapshot Number: #$snapshot_num" + echo -e " Description: \"$description\"" + echo -e " Config: $snap_config" + echo -e " Before entries: $before_entries" + echo -e " After entries: $after_entries" if [[ "$validation_passed" == true ]]; then - echo -e "Status: ${DF_GREEN}✓ VALIDATED${DF_NC}" + echo -e " Status: ${DF_GREEN}✓ VALIDATED${DF_NC}" echo -e "\n${DF_GREEN}✓${DF_NC} Snapshot created and limine.conf successfully updated!" return 0 else - echo -e "Status: ${DF_RED}✗ VALIDATION FAILED${DF_NC}" + echo -e " Status: ${DF_RED}✗ VALIDATION FAILED${DF_NC}" echo -e "\n${DF_RED}✗${DF_NC} Snapshot created but limine.conf validation failed!" echo -e "${DF_YELLOW}⚠${DF_NC} Check if limine-snapper-sync service is running properly" echo -e "${DF_YELLOW}Run:${DF_NC} sudo systemctl status limine-snapper-sync.service" @@ -118,17 +115,20 @@ snap-create() { snap-list() { local count="${1:-10}" - echo -e "${DF_BLUE}Recent $count snapshots:${DF_NC}\n" + + df_print_func_name "Snapper Snapshots (last $count)" + sudo snapper -c root list | tail -n "$((count + 1))" } snap-show() { [[ -z "$1" ]] && { echo -e "${DF_RED}✗${DF_NC} Usage: snap-show "; return 1; } - echo -e "${DF_BLUE}Snapshot #$1 details:${DF_NC}\n" + df_print_func_name "Snapshot #$1 Details" + sudo snapper -c root list | grep "^\s*$1\s" - echo -e "\n${DF_BLUE}In limine.conf:${DF_NC}" + echo -e "\n${DF_CYAN}In limine.conf:${DF_NC}" if sudo grep -qP "^\\s*///$1\\s*│" /boot/limine.conf; then local entry_line=$(sudo grep -nP "^\\s*///$1\\s*│" /boot/limine.conf | head -n 1 | cut -d: -f1) [[ -n "$entry_line" ]] && sudo sed -n "${entry_line}p; $((entry_line+1))p" /boot/limine.conf @@ -143,7 +143,7 @@ snap-delete() { local snapshot_num="$1" local limine_conf="/boot/limine.conf" - echo -e "${DF_BLUE}==>${DF_NC} Deleting snapshot #$snapshot_num" + df_print_func_name "Delete Snapshot #$snapshot_num" local before_entries=$(sudo grep -cP "^\\s*///\\d+\\s*│" "$limine_conf" || echo "0") @@ -178,16 +178,14 @@ snap-delete() { snap-check-limine() { local limine_conf="/boot/limine.conf" - echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Limine Snapshot Entries ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}\n" + df_print_func_name "Limine Snapshot Entries" [[ ! -f "$limine_conf" ]] && { echo -e "${DF_RED}✗${DF_NC} Limine config not found: $limine_conf"; return 1; } local latest_snapshot=$(sudo snapper -c root list | tail -n +3 | grep -v "^\s*0\s" | tail -n 1 | awk '{print $1}') [[ -z "$latest_snapshot" ]] && { echo -e "${DF_YELLOW}⚠${DF_NC} No snapshots found in snapper"; return 1; } - echo -e "${DF_BLUE}Latest snapshot:${DF_NC} #$latest_snapshot" + echo -e "${DF_CYAN}Latest snapshot:${DF_NC} #$latest_snapshot" echo -e "${DF_BLUE}==>${DF_NC} Checking if latest snapshot is in limine.conf" @@ -199,16 +197,18 @@ snap-check-limine() { echo -e "\n${DF_BLUE}==>${DF_NC} Counting snapshot entries" local entry_count=$(sudo grep -cP "^\\s*///\\d+\\s*│" "$limine_conf" || echo "0") - echo -e "${DF_BLUE}Total snapshot entries:${DF_NC} $entry_count\n" + echo -e "${DF_CYAN}Total snapshot entries:${DF_NC} $entry_count" } snap-sync() { + df_print_func_name "Limine-Snapper-Sync" + echo -e "${DF_BLUE}==>${DF_NC} Manually triggering limine-snapper-sync..." if sudo systemctl start limine-snapper-sync.service; then echo -e "${DF_GREEN}✓${DF_NC} Service triggered successfully" sleep 2 - echo -e "\n${DF_BLUE}Service status:${DF_NC}" + echo -e "\n${DF_CYAN}Service status:${DF_NC}" sudo systemctl status limine-snapper-sync.service --no-pager -l | tail -n 10 else echo -e "${DF_RED}✗${DF_NC} Failed to trigger service" @@ -217,9 +217,7 @@ snap-sync() { } snap-validate-service() { - echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Limine-Snapper-Sync Service Validation ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}\n" + df_print_func_name "Limine-Snapper-Sync Service Validation" echo -e "${DF_BLUE}==>${DF_NC} Checking service unit" diff --git a/zsh/functions/ssh-manager.zsh b/zsh/functions/ssh-manager.zsh index cb6e7c1..4c878d4 100644 --- a/zsh/functions/ssh-manager.zsh +++ b/zsh/functions/ssh-manager.zsh @@ -82,10 +82,7 @@ ssh-save() { ssh-list() { _ssh_init_profiles - echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} SSH Connection Profiles ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}" - echo + df_print_func_name "SSH Connection Profiles" local has_profiles=false while IFS='|' read -r name connection port key options description; do @@ -205,7 +202,7 @@ ssh-sync-dotfiles() { local dotfiles_dir="${DOTFILES_DIR:-$HOME/.dotfiles}" [[ ! -d "$dotfiles_dir" ]] && { _ssh_print_error "Dotfiles directory not found"; return 1; } - _ssh_print_step "Syncing dotfiles to: $connection" + df_print_func_name "Sync Dotfiles to: $connection" local rsync_cmd="rsync -avz --exclude='.git' --exclude='*.local'" [[ -n "$port" && "$port" != "22" ]] && rsync_cmd="$rsync_cmd -e 'ssh -p $port'" diff --git a/zsh/functions/systemd-helpers.zsh b/zsh/functions/systemd-helpers.zsh index 83af163..a98064b 100644 --- a/zsh/functions/systemd-helpers.zsh +++ b/zsh/functions/systemd-helpers.zsh @@ -97,7 +97,7 @@ sclog() { sclogs() { local service="$1" local lines="${2:-50}" - [[ -z "$service" ]] && { echo "Usage: sclog [lines]"; return 1; } + [[ -z "$service" ]] && { echo "Usage: sclogs [lines]"; return 1; } sudo journalctl -xeu "$service" -n "$lines" --no-pager } @@ -108,11 +108,9 @@ sclogs() { # Show failed services (system and user) sc-failed() { - echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Failed Services ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}" + df_print_func_name "Failed Services" - echo -e "\n${DF_CYAN}System Services:${DF_NC}" + echo -e "${DF_CYAN}System Services:${DF_NC}" local sys_failed=$(systemctl --failed --no-pager --no-legend 2>/dev/null) if [[ -z "$sys_failed" ]]; then echo -e " ${DF_GREEN}✓${DF_NC} No failed system services" @@ -133,11 +131,9 @@ sc-failed() { # Show active timers sc-timers() { - echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Active Timers ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}" + df_print_func_name "Active Timers" - echo -e "\n${DF_CYAN}System Timers:${DF_NC}" + echo -e "${DF_CYAN}System Timers:${DF_NC}" systemctl list-timers --no-pager | head -20 echo -e "\n${DF_CYAN}User Timers:${DF_NC}" @@ -150,11 +146,9 @@ sc-timers() { sc-recent() { local count="${1:-15}" - echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Recent Service Activity ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}" + df_print_func_name "Recent Service Activity" - echo -e "\n${DF_CYAN}Recently Started:${DF_NC}" + echo -e "${DF_CYAN}Recently Started:${DF_NC}" systemctl list-units --type=service --state=running --no-pager --no-legend | \ head -"$count" | awk '{print " " $1}' @@ -166,11 +160,9 @@ sc-recent() { # Boot time analysis sc-boot() { - echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Boot Time Analysis ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}" + df_print_func_name "Boot Time Analysis" - echo -e "\n${DF_CYAN}Boot Summary:${DF_NC}" + echo -e "${DF_CYAN}Boot Summary:${DF_NC}" systemd-analyze echo -e "\n${DF_CYAN}Slowest Services (top 10):${DF_NC}" @@ -191,8 +183,7 @@ sc-search() { local query="$1" [[ -z "$query" ]] && { echo "Usage: sc-search "; return 1; } - echo -e "${DF_BLUE}==>${DF_NC} Searching for services matching: ${query}" - echo "" + df_print_func_name "Service Search: $query" systemctl list-unit-files --type=service --no-pager | grep -i "$query" } @@ -202,11 +193,9 @@ sc-info() { local service="$1" [[ -z "$service" ]] && { echo "Usage: sc-info "; return 1; } - echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Service Info: ${service}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}" + df_print_func_name "Service Info: $service" - echo -e "\n${DF_CYAN}Status:${DF_NC}" + echo -e "${DF_CYAN}Status:${DF_NC}" systemctl status "$service" --no-pager -l 2>/dev/null || \ sudo systemctl status "$service" --no-pager -l @@ -305,9 +294,7 @@ alias jctlerr='journalctl -p err -b' # ============================================================================ sc-help() { - echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Systemd Helper Commands ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}" + df_print_func_name "Systemd Helper Commands" cat << 'EOF' @@ -318,7 +305,7 @@ sc-help() { sce Enable and start (--now) scd Disable and stop (--now) sclog Follow journal logs (-f) - scogs Show recent logs (no follow) + sclogs Show recent logs (no follow) Status Commands: sc-failed Show failed services diff --git a/zsh/functions/tmux-workspaces.zsh b/zsh/functions/tmux-workspaces.zsh index 72819b6..af9fc17 100644 --- a/zsh/functions/tmux-workspaces.zsh +++ b/zsh/functions/tmux-workspaces.zsh @@ -100,10 +100,7 @@ EOF tw-templates() { _tw_init_templates - echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Available Tmux Templates ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}" - echo + df_print_func_name "Available Tmux Templates" for template in "$TW_TEMPLATES_DIR"/*.tmux; do [[ ! -f "$template" ]] && continue @@ -200,10 +197,7 @@ tw-attach() { tw-list() { _tw_check_tmux || return 1 - echo -e "${DF_BLUE}╔════════════════════════════════════════════════════════════╗${DF_NC}" - echo -e "${DF_BLUE}║${DF_NC} Active Tmux Workspaces ${DF_BLUE}║${DF_NC}" - echo -e "${DF_BLUE}╚════════════════════════════════════════════════════════════╝${DF_NC}" - echo + df_print_func_name "Active Tmux Workspaces" local has_workspaces=false diff --git a/zsh/lib/utils.zsh b/zsh/lib/utils.zsh new file mode 100644 index 0000000..317b175 --- /dev/null +++ b/zsh/lib/utils.zsh @@ -0,0 +1,257 @@ +# ============================================================================ +# Shared Utility Functions for Zsh Dotfiles +# ============================================================================ +# Common helper functions used across multiple function files +# +# Source this file in your .zshrc or in individual function files: +# source "$HOME/.dotfiles/zsh/lib/utils.zsh" +# +# Provides: +# - Standardized output formatting (step/success/error/warning/info) +# - Command dependency checking +# - User confirmation prompts +# - Common file/directory operations +# ============================================================================ + +# Ensure colors are loaded first +source "${0:A:h}/colors.zsh" 2>/dev/null || { + typeset -g DF_GREEN=$'\033[0;32m' DF_YELLOW=$'\033[1;33m' + typeset -g DF_RED=$'\033[0;31m' DF_BLUE=$'\033[0;34m' + typeset -g DF_CYAN=$'\033[0;36m' DF_DIM=$'\033[2m' DF_NC=$'\033[0m' +} + +# ============================================================================ +# Output Formatting Functions +# ============================================================================ +# These provide consistent, styled output across all dotfiles functions. +# Use these instead of raw echo statements for a unified look. + +# Print a step/action indicator (blue arrow) +# Usage: df_print_step "Installing packages" +df_print_step() { + echo -e "${DF_BLUE}==>${DF_NC} $1" +} + +# Print a success message (green checkmark) +# Usage: df_print_success "Installation complete" +df_print_success() { + echo -e "${DF_GREEN}✓${DF_NC} $1" +} + +# Print an error message (red X) +# Usage: df_print_error "Failed to connect" +df_print_error() { + echo -e "${DF_RED}✗${DF_NC} $1" +} + +# Print a warning message (yellow warning sign) +# Usage: df_print_warning "Config file missing, using defaults" +df_print_warning() { + echo -e "${DF_YELLOW}⚠${DF_NC} $1" +} + +# Print an info message (cyan info icon) +# Usage: df_print_info "Using cached version" +df_print_info() { + echo -e "${DF_CYAN}ℹ${DF_NC} $1" +} + +# Print a section header (cyan, for grouping output) +# Usage: df_print_section "Configuration" +df_print_section() { + echo -e "${DF_CYAN}$1:${DF_NC}" +} + +# Print a bullet point item (green dot) +# Usage: df_print_item "my-alias" "git status" +df_print_item() { + local name="$1" + local description="${2:-}" + if [[ -n "$description" ]]; then + echo -e " ${DF_GREEN}●${DF_NC} ${DF_CYAN}$name${DF_NC} - $description" + else + echo -e " ${DF_GREEN}●${DF_NC} ${DF_CYAN}$name${DF_NC}" + fi +} + +# Print indented content (for sub-items) +# Usage: df_print_indent "Additional details here" +df_print_indent() { + echo " $1" +} + +# Print a function name header (box style) - already defined in colors.zsh +# but ensure it's available +if ! typeset -f df_print_func_name &>/dev/null; then + df_print_func_name() { + local name="$1" + local width=$((${#name} + 4)) + local border=$(printf '─%.0s' $(seq 1 $width)) + echo -e "${DF_CYAN}╭${border}╮${DF_NC}" + echo -e "${DF_CYAN}│${DF_NC} $name ${DF_CYAN}│${DF_NC}" + echo -e "${DF_CYAN}╰${border}╯${DF_NC}" + } +fi + +# ============================================================================ +# Command Dependency Checking +# ============================================================================ +# Check if required commands are available before running functions. + +# Check if a command exists +# Usage: df_cmd_exists git && echo "git is installed" +df_cmd_exists() { + command -v "$1" &>/dev/null +} + +# Require a command, exit with error if missing +# Usage: df_require_cmd fzf || return 1 +# Usage: df_require_cmd compsize "compsize" || return 1 +df_require_cmd() { + local cmd="$1" + local package="${2:-$1}" # Default to command name as package name + + if ! command -v "$cmd" &>/dev/null; then + df_print_error "$cmd not installed" + echo "Install: sudo pacman -S $package" + return 1 + fi + return 0 +} + +# Require multiple commands at once +# Usage: df_require_cmds git fzf tmux || return 1 +df_require_cmds() { + local missing=() + for cmd in "$@"; do + if ! command -v "$cmd" &>/dev/null; then + missing+=("$cmd") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + df_print_error "Missing required commands: ${missing[*]}" + echo "Install: sudo pacman -S ${missing[*]}" + return 1 + fi + return 0 +} + +# ============================================================================ +# User Confirmation Prompts +# ============================================================================ +# Consistent confirmation dialogs for dangerous operations. + +# Ask for yes/no confirmation (defaults to no) +# Usage: df_confirm "Delete all files?" || return +df_confirm() { + local prompt="$1" + read -q "REPLY?$prompt [y/N]: " + echo # Newline after response + [[ "$REPLY" =~ ^[Yy]$ ]] +} + +# Ask for confirmation with a warning prefix +# Usage: df_confirm_warning "This will delete all data" || return +df_confirm_warning() { + local message="$1" + df_print_warning "$message" + df_confirm "Continue?" +} + +# ============================================================================ +# File and Directory Helpers +# ============================================================================ + +# Check if running inside a git repository +# Usage: df_in_git_repo && echo "In a git repo" +df_in_git_repo() { + git rev-parse --git-dir &>/dev/null 2>&1 +} + +# Get git root directory +# Usage: local root=$(df_git_root) +df_git_root() { + git rev-parse --show-toplevel 2>/dev/null +} + +# Ensure a directory exists, create if missing +# Usage: df_ensure_dir "$HOME/.cache/myapp" +df_ensure_dir() { + local dir="$1" + [[ ! -d "$dir" ]] && mkdir -p "$dir" +} + +# Ensure a file exists with optional default content +# Usage: df_ensure_file "$HOME/.config/myapp/config" "# Default config" +df_ensure_file() { + local file="$1" + local default_content="${2:-}" + + if [[ ! -f "$file" ]]; then + df_ensure_dir "$(dirname "$file")" + if [[ -n "$default_content" ]]; then + echo "$default_content" > "$file" + else + touch "$file" + fi + fi +} + +# ============================================================================ +# String Helpers +# ============================================================================ + +# Truncate a string to a maximum length +# Usage: df_truncate "Long string here" 10 # "Long st..." +df_truncate() { + local str="$1" + local max="${2:-50}" + + if [[ ${#str} -gt $max ]]; then + echo "${str:0:$((max-3))}..." + else + echo "$str" + fi +} + +# Pad a string to a minimum length +# Usage: df_pad "short" 10 # "short " +df_pad() { + local str="$1" + local width="${2:-20}" + printf "%-${width}s" "$str" +} + +# ============================================================================ +# FZF Helpers +# ============================================================================ + +# Standard fzf options for consistent look +df_fzf_opts() { + echo "--height=50% --layout=reverse --border=rounded" +} + +# Run fzf with standard options +# Usage: echo -e "opt1\nopt2" | df_fzf "Select > " +df_fzf() { + local prompt="${1:-Select > }" + shift + fzf $(df_fzf_opts) --prompt="$prompt" "$@" +} + +# ============================================================================ +# Environment Helpers +# ============================================================================ + +# Check if inside tmux +# Usage: df_in_tmux && echo "Inside tmux" +df_in_tmux() { + [[ -n "$TMUX" ]] +} + +# Check if root filesystem is btrfs +# Usage: df_is_btrfs && echo "Using btrfs" +df_is_btrfs() { + [[ "$(df -T / | awk 'NR==2 {print $2}')" == "btrfs" ]] +}