#!/usr/bin/env bash set -o pipefail # ============================================================ # Hastebin / pste.us uploader # # Dependencies: # curl jq gum fzf qrencode # # Optional: # wl-copy / xclip / xsel / pbcopy # xdg-open / open # # Examples: # ./haste # ./haste file.txt # ./haste file.txt --open # ./haste file.txt --no-qr # ./haste file.txt --quiet # ./haste --history # ============================================================ # ----------------------------- # Configuration # ----------------------------- UPLOAD_URL="https://pste.us/documents" BASE_URL="https://pste.us" HISTORY_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/haste" HISTORY_FILE="$HISTORY_DIR/history" # Colors PINK="#f5c2e7" BLUE="#89b4fa" GREEN="#a6e3a1" RED="#f38ba8" YELLOW="#f9e2af" CYAN="#89dceb" TEXT="#cdd6f4" MAUVE="#cba6f7" # Temporary files RESPONSE_FILE="" PROGRESS_FILE="" # ----------------------------- # Cleanup # ----------------------------- cleanup() { [[ -n "$RESPONSE_FILE" && -f "$RESPONSE_FILE" ]] && rm -f "$RESPONSE_FILE" [[ -n "$PROGRESS_FILE" && -f "$PROGRESS_FILE" ]] && rm -f "$PROGRESS_FILE" } trap cleanup EXIT trap 'exit 130' INT TERM # ----------------------------- # Helpers # ----------------------------- die() { gum style \ --foreground "$RED" \ --border double \ --border-foreground "$RED" \ --padding "1 2" \ "✗ $1" exit 1 } info() { gum style \ --foreground "$CYAN" \ --padding "0 1" \ "$1" } success() { gum style \ --foreground "$GREEN" \ --padding "0 1" \ "✓ $1" } warning() { gum style \ --foreground "$YELLOW" \ --padding "0 1" \ "⚠ $1" } # ----------------------------- # Dependency check # ----------------------------- REQUIRED_COMMANDS=( curl jq gum fzf ) for cmd in "${REQUIRED_COMMANDS[@]}"; do if ! command -v "$cmd" &>/dev/null; then die "Required command not found: $cmd" fi done # ----------------------------- # Arguments # ----------------------------- SHOW_HISTORY=false OPEN_AFTER=false SHOW_QR=true QUIET=false FILE_PATH="" while [[ $# -gt 0 ]]; do case "$1" in --history|-H) SHOW_HISTORY=true shift ;; --open|-o) OPEN_AFTER=true shift ;; --no-qr) SHOW_QR=false shift ;; --quiet|-q) QUIET=true shift ;; --help|-h) cat </dev/null; then printf '%s' "$SELECTED_URL" | wl-copy success "Copied to clipboard." elif command -v xclip &>/dev/null; then printf '%s' "$SELECTED_URL" | xclip -selection clipboard success "Copied to clipboard." elif command -v xsel &>/dev/null; then printf '%s' "$SELECTED_URL" | xsel --clipboard --input success "Copied to clipboard." elif command -v pbcopy &>/dev/null; then printf '%s' "$SELECTED_URL" | pbcopy success "Copied to clipboard." else warning "No clipboard utility found." fi fi if gum confirm "Open this URL in browser?"; then if command -v xdg-open &>/dev/null; then xdg-open "$SELECTED_URL" &>/dev/null & elif command -v open &>/dev/null; then open "$SELECTED_URL" &>/dev/null & else warning "No browser opener found." fi fi exit 0 } if [[ "$SHOW_HISTORY" == true ]]; then show_history fi # ----------------------------- # File selection # ----------------------------- 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, 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 {}' ) if [[ -z "$FILE_PATH" ]]; then warning "No file selected." exit 1 fi fi # ----------------------------- # Validate file # ----------------------------- if [[ ! -f "$FILE_PATH" ]]; then die "File does not exist: $FILE_PATH" fi if [[ ! -r "$FILE_PATH" ]]; then die "File is not readable: $FILE_PATH" fi # ----------------------------- # File information # ----------------------------- FILE_NAME="$(basename "$FILE_PATH")" if command -v numfmt &>/dev/null; then FILE_BYTES=$(stat -c%s "$FILE_PATH" 2>/dev/null || stat -f%z "$FILE_PATH") FILE_SIZE=$(numfmt --to=iec --suffix=B "$FILE_BYTES" 2>/dev/null || echo "${FILE_BYTES} B") else FILE_SIZE=$(du -h "$FILE_PATH" | cut -f1) fi FILE_TYPE=$(file -b "$FILE_PATH" 2>/dev/null || echo "Unknown") if [[ -r "$FILE_PATH" ]]; then LINE_COUNT=$(wc -l < "$FILE_PATH" 2>/dev/null || echo "N/A") else LINE_COUNT="N/A" fi # ----------------------------- # Quiet mode # ----------------------------- if [[ "$QUIET" != true ]]; then gum style \ --foreground "$BLUE" \ --border double \ --border-foreground "$BLUE" \ --align center \ --width 64 \ --padding "1 2" \ "HASTEBIN UPLOADER" \ "" \ "File: $FILE_NAME" echo gum style \ --foreground "$TEXT" \ --border rounded \ --border-foreground "$MAUVE" \ --padding "1 2" \ "File : $FILE_NAME" \ "Size : $FILE_SIZE" \ "Type : $FILE_TYPE" \ "Lines : $LINE_COUNT" echo fi # ----------------------------- # Prepare temporary files # ----------------------------- RESPONSE_FILE=$(mktemp) PROGRESS_FILE=$(mktemp) # ----------------------------- # Upload # ----------------------------- if [[ "$QUIET" == true ]]; then if ! curl -fsS \ -X POST \ -H "Content-Type: text/plain" \ --data-binary @"$FILE_PATH" \ "$UPLOAD_URL" \ >"$RESPONSE_FILE"; then die "Failed to upload the document." fi else echo gum style \ --foreground "$CYAN" \ --padding "0 1" \ "↑ Uploading $FILE_NAME" echo # Start curl in the background. # Its progress output is written to PROGRESS_FILE. curl \ -fS \ --progress-bar \ -X POST \ -H "Content-Type: text/plain" \ --data-binary @"$FILE_PATH" \ "$UPLOAD_URL" \ >"$RESPONSE_FILE" \ 2>"$PROGRESS_FILE" & CURL_PID=$! LAST_PERCENT=-1 while kill -0 "$CURL_PID" 2>/dev/null; do # curl writes percentage information using carriage returns. # Extract the most recent percentage we can find. CURRENT_PERCENT=$( tr '\r' '\n' < "$PROGRESS_FILE" 2>/dev/null | grep -oE '[0-9]{1,3}(\.[0-9]+)?%' | tail -1 | tr -d '%' | cut -d. -f1 ) if [[ -z "$CURRENT_PERCENT" ]]; then CURRENT_PERCENT=0 fi if (( CURRENT_PERCENT < 0 )); then CURRENT_PERCENT=0 fi if (( CURRENT_PERCENT > 100 )); then CURRENT_PERCENT=100 fi if [[ "$CURRENT_PERCENT" != "$LAST_PERCENT" ]]; then BAR_WIDTH=40 FILLED=$(( CURRENT_PERCENT * BAR_WIDTH / 100 )) EMPTY=$(( BAR_WIDTH - FILLED )) FILLED_BAR=$(printf '%*s' "$FILLED" '' | tr ' ' '█') EMPTY_BAR=$(printf '%*s' "$EMPTY" '' | tr ' ' '░') PROGRESS_LINE="$FILLED_BAR$EMPTY_BAR" # Move cursor to beginning of current line and redraw. printf '\r' gum style \ --foreground "$GREEN" \ --padding "0 1" \ "[$PROGRESS_LINE] ${CURRENT_PERCENT}%" LAST_PERCENT="$CURRENT_PERCENT" fi sleep 0.1 done wait "$CURL_PID" CURL_STATUS=$? echo if [[ "$CURL_STATUS" -ne 0 ]]; then echo die "Upload failed." fi gum spin \ --spinner points \ --spinner.foreground "$MAUVE" \ --title "Processing server response..." \ -- sleep 0.4 fi # ----------------------------- # Parse response # ----------------------------- HASTE_KEY=$(jq -r '.key // empty' "$RESPONSE_FILE" 2>/dev/null) if [[ -z "$HASTE_KEY" ]]; then die "Server returned an invalid response." fi URL="$BASE_URL/$HASTE_KEY" # ----------------------------- # Clipboard # ----------------------------- 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 # ----------------------------- # Save history # ----------------------------- mkdir -p "$HISTORY_DIR" TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') printf '%s|%s|%s\n' \ "$TIMESTAMP" \ "$FILE_NAME" \ "$URL" >> "$HISTORY_FILE" # ----------------------------- # Quiet output # ----------------------------- if [[ "$QUIET" == true ]]; then printf '%s\n' "$URL" exit 0 fi # ----------------------------- # QR code # ----------------------------- QR_CODE="" if [[ "$SHOW_QR" == true ]] && command -v qrencode &>/dev/null; then QR_CODE=$( qrencode \ -t ANSIUTF8 \ -m 1 \ "$URL" ) fi # ----------------------------- # Build result panel # ----------------------------- STATUS_TEXT="✓ Copied to clipboard" if [[ "$COPIED" != true ]]; then STATUS_TEXT="⚠ Clipboard utility not found" fi RESULT_TEXT="File : $FILE_NAME Size : $FILE_SIZE Type : $FILE_TYPE URL : $URL " if [[ "$COPIED" == true ]]; then RESULT_TEXT+=" $STATUS_TEXT" else RESULT_TEXT+=" $STATUS_TEXT" fi # ----------------------------- # Result # ----------------------------- echo gum style \ --foreground "$GREEN" \ --border double \ --border-foreground "$GREEN" \ --padding "1 2" \ --width 64 \ "Upload Complete" \ "" \ "$RESULT_TEXT" # ----------------------------- # QR # ----------------------------- if [[ "$SHOW_QR" == true ]]; then echo if [[ -n "$QR_CODE" ]]; then QR_PANEL=$( gum style \ --foreground "$TEXT" \ --border rounded \ --border-foreground "$MAUVE" \ --padding "1 2" \ --align center \ "Scan QR Code" \ "" \ "$QR_CODE" ) printf '%s\n' "$QR_PANEL" else warning "qrencode is not installed; QR code skipped." gum style \ --foreground "$YELLOW" \ --padding "0 1" \ "Install it with: sudo pacman -S qrencode" fi fi # ----------------------------- # Actions # ----------------------------- echo ACTION=$( gum choose \ --cursor "➜ " \ --header "What would you like to do?" \ "Open in browser" \ "Copy URL again" \ "Show URL" \ "Done" ) case "$ACTION" in "Open in browser") if command -v xdg-open &>/dev/null; then xdg-open "$URL" &>/dev/null & success "Opened in browser." elif command -v open &>/dev/null; then open "$URL" &>/dev/null & success "Opened in browser." else warning "No browser opener found." fi ;; "Copy URL again") if command -v wl-copy &>/dev/null; then printf '%s' "$URL" | wl-copy success "Copied to clipboard." elif command -v xclip &>/dev/null; then printf '%s' "$URL" | xclip -selection clipboard success "Copied to clipboard." elif command -v xsel &>/dev/null; then printf '%s' "$URL" | xsel --clipboard --input success "Copied to clipboard." elif command -v pbcopy &>/dev/null; then printf '%s' "$URL" | pbcopy success "Copied to clipboard." else warning "No clipboard utility found." fi ;; "Show URL") echo gum style \ --foreground "$GREEN" \ --border rounded \ --border-foreground "$GREEN" \ --padding "1 2" \ "$URL" ;; "Done"|"") ;; esac echo