Creating a media upload script for Raycast

Building my Raycast tool belt

Okay, so day 2 of Raycast blog work. I haven't blogged as much because it is still not a seamless experience - so I'm using Raycast to pull out those seams.

After much back and forth with Claude, I got a functioning script and this overview.


Core Implementation

1# Key components 2TEMP_FILE=$(mktemp).png 3pngpaste "$TEMP_FILE_INITIAL" 4sips --matchTo '/ColorSync/Profiles/Display P3.icc' "$TEMP_FILE" 5curl -X POST "$SUPABASE_URL/storage/v1/object/$BUCKET_NAME/$PATH"
bash

Development Challenges

  1. Clipboard Data Handling

    • Text data instead of binary
    • Screen capture instead of clipboard
    • Color profile loss
  2. Image Quality

    • Initial washed-out colors
    • Fixed via sips color profile management
    • Display P3 profile preservation

Current Function

  1. Accepts folder path and filename
  2. Captures clipboard image via pngpaste
  3. Processes with sips for color fidelity
  4. Uploads to Supabase bucket
  5. Returns public URL

Required Setup

  • Supabase credentials in .env
  • pngpaste installation
  • Raycast scripts directory access

Limitations

  • PNG format only
  • macOS specific
  • Requires manual filename input

And just like that, we've uploaded this image to supabase.

Image generated in Midjourney with the prompt we out here --p to generate in my "personal Style" An image of a man with his eyes closed as the universe appears to explode out of his head in dramatic colors

The full workflow

  1. generate image in Midjourney
  2. copy the image to my clipboard
  3. press option + shift + u to pull up the Raycast script.
  4. Write the folder and file name to upload to and press enter
  5. copy generated link - TADA

Current state

I now have two Raycast scripts to assist in publishing blog posts.

  1. my blog post publishing script that I run via option + shift + Enter
  2. my image bucket uploader script. I've assigned this to option + Shift + u. I can now upload any image directly to bucket storage and get the url back for embedding.

Next I need to think of other macros to build!! As soon as I'm doing a multi-part process more than once in a short period of time, it's Raycast script time.

I'm still in awe of the power and simplicity of Raycast.

Full Script

1#!/bin/bash 2 3# Required parameters: 4# @raycast.schemaVersion 1 5# @raycast.title Upload to Supabase 6# @raycast.mode fullOutput 7# @raycast.packageName Supabase 8 9# Optional parameters: 10# @raycast.icon πŸ“€ 11# @raycast.argument1 { "type": "text", "placeholder": "Folder path (e.g., images/blog)", "optional": false } 12# @raycast.argument2 { "type": "text", "placeholder": "File name (without extension)", "optional": false } 13# @raycast.needsConfirmation true 14# @raycast.description Upload clipboard content to Supabase Storage 15 16echo "πŸ” Debug: Script starting" 17 18# Source environment variables 19if [ -f "$(dirname "$0")/.env" ]; then 20 echo "πŸ“ Debug: Found .env file" 21 source "$(dirname "$0")/.env" 22else 23 echo "❌ Error: .env file not found at $(dirname "$0")/.env" 24 exit 1 25fi 26 27# Check for required tools 28if ! command -v pngpaste &> /dev/null; then 29 echo "⚠️ pngpaste not found. Installing..." 30 brew install pngpaste 31fi 32 33if ! command -v magick &> /dev/null; then 34 echo "⚠️ ImageMagick not found. Installing..." 35 brew install imagemagick 36fi 37 38# Get arguments 39FOLDER_PATH=$1 40FILE_NAME=$2 41 42# Create temporary directory 43TEMP_DIR=$(mktemp -d) 44echo "πŸ“‹ Debug: Created temp directory at $TEMP_DIR" 45 46# Get clipboard info for format detection 47CLIPBOARD_INFO=$(osascript -e 'clipboard info') 48echo "πŸ“Ž Debug: Clipboard info: $CLIPBOARD_INFO" 49 50# Determine desired output format from clipboard info 51# Prioritize actual image formats over text representations 52if [[ $CLIPBOARD_INFO == *"Β«class JPEGΒ»"* || $CLIPBOARD_INFO == *"JPEG picture"* ]]; then 53 FORMAT="jpg" 54 MIME_TYPE="image/jpeg" 55 echo "πŸ“„ Debug: JPEG format detected in clipboard" 56elif [[ $CLIPBOARD_INFO == *"Β«class GIFfΒ»"* || $CLIPBOARD_INFO == *"GIF picture"* ]]; then 57 FORMAT="gif" 58 MIME_TYPE="image/gif" 59 echo "πŸ“„ Debug: GIF format detected in clipboard" 60elif [[ $CLIPBOARD_INFO == *"TIFF"* ]]; then 61 FORMAT="tiff" 62 MIME_TYPE="image/tiff" 63 echo "πŸ“„ Debug: TIFF format detected in clipboard" 64else 65 FORMAT="png" 66 MIME_TYPE="image/png" 67 echo "πŸ“„ Debug: Defaulting to PNG format" 68fi 69 70# Set up temp file paths 71TEMP_PNG="$TEMP_DIR/initial.png" 72TEMP_FILE="$TEMP_DIR/image.$FORMAT" 73 74# First capture with pngpaste 75if pngpaste "$TEMP_PNG"; then 76 echo "βœ… Successfully captured clipboard to PNG" 77 78 # Verify the captured PNG 79 if ! magick identify "$TEMP_PNG" &>/dev/null; then 80 echo "❌ Invalid image captured" 81 rm -rf "$TEMP_DIR" 82 exit 1 83 fi 84 85 if [ "$FORMAT" = "png" ]; then 86 echo "πŸ“„ Using PNG format directly" 87 mv "$TEMP_PNG" "$TEMP_FILE" 88 else 89 echo "πŸ”„ Converting from PNG to $FORMAT" 90 if magick "$TEMP_PNG" -quality 100 "$TEMP_FILE"; then 91 # Verify conversion worked 92 ACTUAL_FORMAT=$(magick identify -format "%m" "$TEMP_FILE") 93 echo "πŸ“„ Debug: Actual output format: $ACTUAL_FORMAT" 94 95 if [[ $ACTUAL_FORMAT != *"$FORMAT"* ]]; then 96 echo "⚠️ Format conversion failed, falling back to PNG" 97 FORMAT="png" 98 MIME_TYPE="image/png" 99 mv "$TEMP_PNG" "$TEMP_FILE" 100 else 101 echo "βœ… Successfully converted to $FORMAT" 102 fi 103 else 104 echo "⚠️ Conversion failed, falling back to PNG" 105 FORMAT="png" 106 MIME_TYPE="image/png" 107 mv "$TEMP_PNG" "$TEMP_FILE" 108 fi 109 fi 110else 111 echo "❌ Failed to capture image from clipboard" 112 rm -rf "$TEMP_DIR" 113 exit 1 114fi 115 116CLIPBOARD_SIZE=$(wc -c < "$TEMP_FILE" | tr -d ' ') 117echo "πŸ“€ Debug: File size: $CLIPBOARD_SIZE bytes" 118 119# Remove leading/trailing slashes from folder path 120FOLDER_PATH=$(echo $FOLDER_PATH | sed 's:^/::' | sed 's:/$::') 121FULL_PATH="${FOLDER_PATH}/${FILE_NAME}.${FORMAT}" 122 123echo "πŸ“‚ Debug: Target path will be: $FULL_PATH" 124 125# Check if file has content 126if [ "$CLIPBOARD_SIZE" -eq 0 ]; then 127 echo "❌ Error: No content was saved!" 128 rm -rf "$TEMP_DIR" 129 exit 1 130fi 131 132echo "πŸš€ Debug: Starting upload to Supabase" 133echo "πŸ“€ Uploading to: $FULL_PATH" 134echo "πŸ“ Using bucket: $BUCKET_NAME" 135 136# Upload to Supabase 137RESPONSE=$(curl -s -X POST "$SUPABASE_URL/storage/v1/object/$BUCKET_NAME/$FULL_PATH" \ 138 -H "Authorization: Bearer $SUPABASE_KEY" \ 139 -H "Content-Type: $MIME_TYPE" \ 140 --data-binary "@$TEMP_FILE") 141 142# Clean up temp directory 143rm -rf "$TEMP_DIR" 144echo "🧹 Debug: Cleaned up temp directory" 145 146if [[ $RESPONSE == *"Key"* ]]; then 147 echo "βœ… Upload successful!" 148 echo "πŸ”— URL: $SUPABASE_URL/storage/v1/object/public/$BUCKET_NAME/$FULL_PATH" 149 echo "$RESPONSE" 150else 151 echo "❌ Upload failed!" 152 echo "Error: $RESPONSE" 153 exit 1 154fi
bash