#!/usr/bin/env bash set -o pipefail # ============================================================ # Hastebin / pste.us uploader # # Dependencies: # curl jq gum fzf qrencode # # Clipboard: # wl-copy / xclip / xsel / pbcopy # ============================================================ UPLOAD_URL="https://pste.us/documents" BASE_URL="https://pste.us" # Catppuccin-ish colors BLUE="#89b4fa" GREEN="#a6e3a1" RED="#f38ba8" YELLOW="#f9e2af" CYAN="#89dceb" MAUVE="#cba6f7" TEXT="#cdd6f4" DIM="#6c7086" TMP_RESPONSE="" TMP_PROGRESS="" # ------------------------------------------------------------ # Cleanup # ------------------------------------------------------------ cleanup() { [[ -n "$TMP_RESPONSE" && -f "$TMP_RESPONSE" ]] && rm -f "$TMP_RESPONSE" [[ -n "$TMP_PROGRESS" && -f "$TMP_PROGRESS" ]] && rm -f "$TMP_PROGRESS" } trap cleanup EXIT trap 'exit 130' INT TERM # ------------------------------------------------------------ # Helpers # ------------------------------------------------------------ error() { gum style \ --foreground "$RED" \ --border double \ --border-foreground "$RED" \ --padding "0 2" \ "✗ $1" exit 1 } # ------------------------------------------------------------ # Dependencies # ------------------------------------------------------------ for cmd in curl jq gum fzf; do command -v "$cmd" &>/dev/null || \ error "Missing dependency: $cmd" done # ------------------------------------------------------------ # Arguments # ------------------------------------------------------------ FILE_PATH="${1:-}" while [[ $# -gt 0 ]]; do case "$1" in -h|--help) cat <<'EOF' Usage: hastebin [FILE] Examples: hastebin hastebin file.txt EOF exit 0 ;; -*) error "Unknown option: $1" ;; *) FILE_PATH="$1" shift ;; esac done # ------------------------------------------------------------ # File picker # ------------------------------------------------------------ if [[ -z "$FILE_PATH" ]]; then FILE_PATH=$( find . -maxdepth 1 -type f -printf '%f\n' | fzf \ --prompt="  Select file to upload: " \ --border \ --reverse \ --header="Use ↑/↓ to select • Enter to confirm" \ --pointer="➜" \ --color="bg+:#1e1e2e,bg:#181825,spinner:#f5c2e7,hl:#f38ba8,fg:#cdd6f4,header:#89b4fa,info:#f9e2af,pointer:#a6e3a1,marker:#f5c2e7,fg+:#cdd6f4,prompt:#89b4fa,hl+:#f38a8a" \ --preview='bat --style=numbers --color=always {}' ) [[ -n "$FILE_PATH" ]] || exit 0 fi # ------------------------------------------------------------ # Validate # ------------------------------------------------------------ [[ -f "$FILE_PATH" ]] || error "File does not exist: $FILE_PATH" [[ -r "$FILE_PATH" ]] || error "File is not readable: $FILE_PATH" # ------------------------------------------------------------ # File information # ------------------------------------------------------------ FILE_NAME="$(basename "$FILE_PATH")" FILE_BYTES=$(stat -c%s "$FILE_PATH" 2>/dev/null || stat -f%z "$FILE_PATH") if command -v numfmt &>/dev/null; then FILE_SIZE=$(numfmt --to=iec --suffix=B "$FILE_BYTES") else FILE_SIZE=$(du -h "$FILE_PATH" | cut -f1) fi FILE_TYPE=$(file -b "$FILE_PATH" 2>/dev/null | cut -d',' -f1) # Keep type short enough for the terminal if (( ${#FILE_TYPE} > 34 )); then FILE_TYPE="${FILE_TYPE:0:31}..." fi # ------------------------------------------------------------ # Temporary files # ------------------------------------------------------------ TMP_RESPONSE=$(mktemp) TMP_PROGRESS=$(mktemp) # ------------------------------------------------------------ # Header # ------------------------------------------------------------ clear gum style \ --foreground "$BLUE" \ --border double \ --border-foreground "$BLUE" \ --align center \ --width 58 \ --padding "0 1" \ " HASTEBIN" echo gum style \ --foreground "$TEXT" \ --border rounded \ --border-foreground "$DIM" \ --padding "0 1" \ " $FILE_NAME • $FILE_SIZE • $FILE_TYPE" echo # ------------------------------------------------------------ # Upload # ------------------------------------------------------------ printf ' ' gum style \ --foreground "$CYAN" \ --padding "0 0" \ "󰑓 Uploading..." echo # Start upload curl \ -fS \ --progress-bar \ -X POST \ -H "Content-Type: text/plain" \ --data-binary @"$FILE_PATH" \ "$UPLOAD_URL" \ >"$TMP_RESPONSE" \ 2>"$TMP_PROGRESS" & CURL_PID=$! LAST_PERCENT=-1 # ------------------------------------------------------------ # In-place progress bar # ------------------------------------------------------------ while kill -0 "$CURL_PID" 2>/dev/null; do CURRENT_PERCENT=$( tr '\r' '\n' < "$TMP_PROGRESS" 2>/dev/null | grep -oE '[0-9]{1,3}(\.[0-9]+)?%' | tail -1 | tr -d '%' | cut -d. -f1 ) [[ -n "$CURRENT_PERCENT" ]] || CURRENT_PERCENT=0 (( CURRENT_PERCENT > 100 )) && CURRENT_PERCENT=100 (( CURRENT_PERCENT < 0 )) && CURRENT_PERCENT=0 BAR_WIDTH=38 FILLED=$(( CURRENT_PERCENT * BAR_WIDTH / 100 )) EMPTY=$(( BAR_WIDTH - FILLED )) FILLED_BAR=$(printf '%*s' "$FILLED" '' | tr ' ' '━') EMPTY_BAR=$(printf '%*s' "$EMPTY" '' | tr ' ' '─') # Move to beginning of line and erase it. printf '\r\033[2K' printf ' \033[38;2;166;227;161m%s\033[0m \033[38;2;108;112;134m%s\033[0m \033[38;2;205;214;244m%3d%%\033[0m' \ "$FILLED_BAR" \ "$EMPTY_BAR" \ "$CURRENT_PERCENT" LAST_PERCENT="$CURRENT_PERCENT" sleep 0.08 done wait "$CURL_PID" CURL_STATUS=$? # Finish progress line printf '\r\033[2K' if [[ "$CURL_STATUS" -ne 0 ]]; then error "Upload failed." fi # ------------------------------------------------------------ # Parse response # ------------------------------------------------------------ HASTE_KEY=$(jq -r '.key // empty' "$TMP_RESPONSE" 2>/dev/null) [[ -n "$HASTE_KEY" ]] || error "Invalid response from server." URL="$BASE_URL/$HASTE_KEY" # ------------------------------------------------------------ # Copy URL # ------------------------------------------------------------ COPIED=false if command -v wl-copy &>/dev/null; then printf '%s' "$URL" | wl-copy COPIED=true elif command -v xclip &>/dev/null; then printf '%s' "$URL" | xclip -selection clipboard COPIED=true elif command -v xsel &>/dev/null; then printf '%s' "$URL" | xsel --clipboard --input COPIED=true elif command -v pbcopy &>/dev/null; then printf '%s' "$URL" | pbcopy COPIED=true fi # ------------------------------------------------------------ # QR code # ------------------------------------------------------------ QR="" if command -v qrencode &>/dev/null; then QR=$( qrencode \ -t ANSIUTF8 \ -m 0 \ "$URL" ) fi # ------------------------------------------------------------ # Final panel # ------------------------------------------------------------ echo if [[ -n "$QR" ]]; then gum style \ --border double \ --border-foreground "$GREEN" \ --padding "0 1" \ --align center \ --width 58 \ "✓ UPLOAD COMPLETE" \ "" \ " $FILE_NAME • $FILE_SIZE" \ "" \ "$URL" \ "" \ "$QR" else gum style \ --border double \ --border-foreground "$GREEN" \ --padding "0 2" \ --align center \ --width 58 \ "✓ UPLOAD COMPLETE" \ "" \ " $FILE_NAME • $FILE_SIZE" \ "" \ "$URL" fi # ------------------------------------------------------------ # Clipboard status # ------------------------------------------------------------ echo if [[ "$COPIED" == true ]]; then gum style \ --foreground "$GREEN" \ --align center \ --width 58 \ "󰆏 URL copied to clipboard" else gum style \ --foreground "$YELLOW" \ --align center \ --width 58 \ "⚠ Clipboard utility not found" fi echo