ray-so-code-snippet
ray.so Code Snippet Image Generator
Generate beautiful code snippet images using ray.so and save them locally.
Requirements
- The user MUST provide the code snippet, either directly or by pointing to a file/selection in context
- MUST ask the user for ALL styling parameters before generating, presenting ALL available options
- MUST use
agent-browserfor screenshot capture (check availability first)
Workflow
Step 1: Verify agent-browser Availability
Before proceeding, verify that agent-browser is available:
which agent-browser
If agent-browser is not found in the PATH, inform the user that this skill requires agent-browser and cannot proceed without it.
Step 2: Fetch Available Options
Fetch the current themes and languages from ray.so's GitHub repository using curl:
# Fetch and parse available themes
curl -s "https://raw.githubusercontent.com/raycast/ray-so/main/app/(navigation)/(code)/store/themes.ts" | grep -oE 'id:\s*"[^"]+"' | sed 's/id:\s*"//;s/"//' | sort -u
# Fetch and parse available languages
curl -s "https://raw.githubusercontent.com/raycast/ray-so/main/app/(navigation)/(code)/util/languages.ts" | grep -oE '^[[:space:]]*"?[a-zA-Z0-9+#-]+"?\s*:\s*\{' | sed 's/[[:space:]]*"//g;s/".*//;s/:.*//' | sort -u
Step 3: Ask User for ALL Parameters
MUST use AskUserQuestion to ask for EVERY parameter, presenting ALL available options. Ask for parameters in this order:
3.1 Theme Selection
Present ALL available themes. In the question, list every theme fetched from step 2. Example:
Question: "Which theme would you like?"
Description: "Available themes: [list ALL themes from curl output]"
Options (pick 4 popular ones for quick select):
- breeze (default, purple gradient)
- midnight (cyan-blue)
- vercel (minimalist dark)
- sunset (warm orange)
Note: User can select "Other" to type any theme from the full list
3.2 Language Selection
Infer the language when possible. Skip this question if:
- The user explicitly specified a language
- The code comes from a file with a clear extension (e.g.,
.py→ python,.js→ javascript,.ts→ typescript,.rs→ rust,.go→ go, etc.) - The syntax is unmistakably identifiable (e.g.,
def/import→ python,func/package→ go,fn/let mut→ rust)
Only ask this question if the language cannot be confidently inferred:
Question: "Which language for syntax highlighting?"
Description: "Available languages: [list ALL languages from curl output]"
Options:
- auto (auto-detect)
- javascript
- python
- typescript
Note: User can select "Other" to type any language from the full list
3.3 Dark/Light Mode
Question: "Dark or light mode?"
Options:
- Dark mode (default)
- Light mode
3.4 Background
Question: "Show the gradient background?"
Options:
- Yes, show background (default)
- No, transparent/minimal background
3.5 Padding
Question: "How much padding around the code?"
Options:
- 16 (compact)
- 32 (small)
- 64 (medium, default)
- 128 (large)
3.6 Line Numbers
Question: "Show line numbers?"
Options:
- No (default)
- Yes
3.7 Title
Question: "Add a title above the code? (e.g., filename)"
Options:
- No title (default)
- Yes, add title
If yes, ask for the title text.
Note: Do NOT ask about output path/filename. Save to the current working directory with a sensible filename (e.g., rayso-snippet.png, or based on the title if provided like fibonacci.png). Only use a different path if the user explicitly specifies one in their original request.
Step 4: Build the ray.so URL
CRITICAL: ALL parameters must be in the URL hash (after #), NOT in the query string.
Build the URL using shell commands:
# 1. Base64 encode the code
CODE_BASE64=$(echo -n 'YOUR_CODE_HERE' | base64)
# 2. URL encode the base64 string
CODE_ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$CODE_BASE64'))")
# 3. Build the URL with ALL parameters in the hash
# Format: https://ray.so/#param1=value1¶m2=value2&code=ENCODED_CODE
# Do NOT include width parameter - let ray.so auto-size to fit content
URL="https://ray.so/#theme=THEME&padding=PADDING&background=BACKGROUND&darkMode=DARKMODE&language=LANGUAGE&code=${CODE_ENCODED}"
# Add optional parameters if needed:
# If lineNumbers: add "&lineNumbers=true" before &code=
# If title: add "&title=URL_ENCODED_TITLE" before &code=
URL Hash Parameters:
| Parameter | Values | Default |
|---|---|---|
| theme | Any theme from list | breeze |
| padding | 16, 32, 64, 128 | 64 |
| background | true, false | true |
| darkMode | true, false | true |
| language | Any language from list, or "auto" | auto |
| lineNumbers | true, false | false |
| title | URL-encoded string | (none) |
| width | Number (pixels) | auto |
| code | Base64-encoded, then URL-encoded | (required) |
Note on width: Do NOT include the width parameter unless you specifically need a fixed width. Without it, ray.so auto-sizes the frame to fit the code content, avoiding unnecessary empty space.
Example URL construction:
# For code: for i in range(23):\n print(i)
# Theme: midnight, Padding: 64, Dark mode: true, Background: true, Language: python, Title: test.py
CODE='for i in range(23):
print(i)'
CODE_BASE64=$(echo -n "$CODE" | base64)
CODE_ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$CODE_BASE64'))")
TITLE_ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('test.py'))")
URL="https://ray.so/#theme=midnight&padding=64&background=true&darkMode=true&language=python&title=${TITLE_ENCODED}&code=${CODE_ENCODED}"
echo "$URL"
Step 5: Capture High-Quality Image with agent-browser
MUST use agent-browser (verified in Step 1). This approach uses the html-to-image library (same as ray.so's internal export) with high pixelRatio for crisp, sharp text rendering.
IMPORTANT: Always use a unique session name with --session to avoid stale session issues.
# Generate unique session name
SESSION="rayso-$(date +%s)"
# 1. Set viewport
agent-browser --session $SESSION set viewport 1400 900
# 2. Open the URL
agent-browser --session $SESSION open "$URL"
# 3. Wait for the page to fully render
agent-browser --session $SESSION wait --load networkidle
agent-browser --session $SESSION wait 3000
# 4. Load html-to-image library (same library ray.so uses internally)
agent-browser --session $SESSION eval 'new Promise((r,e)=>{const s=document.createElement("script");s.src="https://cdn.jsdelivr.net/npm/html-to-image@1.11.11/dist/html-to-image.js";s.onload=r;s.onerror=e;document.head.appendChild(s)})'
# 5. Capture at 4x resolution using html-to-image (produces crisp text)
agent-browser --session $SESSION eval 'htmlToImage.toPng(document.querySelector("#frame > div"),{pixelRatio:4,skipAutoScale:true})' > /tmp/rayso-dataurl-$SESSION.txt
# 6. Close the browser
agent-browser --session $SESSION close
# 7. Convert data URL to PNG file
DATAURL=$(cat /tmp/rayso-dataurl-$SESSION.txt | tr -d '"' | tr -d '\n')
echo "$DATAURL" | sed 's/data:image\/png;base64,//' | base64 -d > /path/to/output.png
# 8. Clean up temp file
rm /tmp/rayso-dataurl-$SESSION.txt
Critical notes:
- Uses
html-to-imagelibrary which is what ray.so uses for its own export feature pixelRatio: 4produces high-DPI images with crisp, sharp text (4x native resolution)- The data URL is captured directly from the library, not from a screenshot
- No ImageMagick required - pure browser-based rendering at high resolution
- Output is correctly sized with no extra whitespace
Step 6: Confirm Output and STOP
Report the saved file location to the user. The task is complete - do not perform any additional checks, explorations, or verifications after the screenshot is saved.
Complete Example
User: "Create a code snippet image of this Python function"
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
-
Check
which agent-browser- confirmed available -
Fetch themes and languages:
curl -s "https://raw.githubusercontent.com/raycast/ray-so/main/app/(navigation)/(code)/store/themes.ts" | grep -oE 'id:\s*"[^"]+"' | sed 's/id:\s*"//;s/"//' | sort -u
-
Ask user for parameters via AskUserQuestion:
- Theme: user selects "midnight"
- Language: inferred as python from
defsyntax - not asked - Dark mode: user selects "Dark mode"
- Background: user selects "Yes"
- Padding: user selects "64"
- Line numbers: user selects "No"
- Title: user selects "No title"
-
Build URL (all params in hash, no width for auto-sizing):
CODE='def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)'
CODE_BASE64=$(echo -n "$CODE" | base64)
CODE_ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$CODE_BASE64'))")
URL="https://ray.so/#theme=midnight&padding=64&background=true&darkMode=true&language=python&code=${CODE_ENCODED}"
- Capture high-quality image:
SESSION="rayso-$(date +%s)"
agent-browser --session $SESSION set viewport 1400 900
agent-browser --session $SESSION open "$URL"
agent-browser --session $SESSION wait --load networkidle
agent-browser --session $SESSION wait 3000
# Load html-to-image library
agent-browser --session $SESSION eval 'new Promise((r,e)=>{const s=document.createElement("script");s.src="https://cdn.jsdelivr.net/npm/html-to-image@1.11.11/dist/html-to-image.js";s.onload=r;s.onerror=e;document.head.appendChild(s)})'
# Capture at 4x resolution
agent-browser --session $SESSION eval 'htmlToImage.toPng(document.querySelector("#frame > div"),{pixelRatio:4,skipAutoScale:true})' > /tmp/rayso-dataurl-$SESSION.txt
agent-browser --session $SESSION close
# Save as PNG
DATAURL=$(cat /tmp/rayso-dataurl-$SESSION.txt | tr -d '"' | tr -d '\n')
echo "$DATAURL" | sed 's/data:image\/png;base64,//' | base64 -d > ./fibonacci.png
rm /tmp/rayso-dataurl-$SESSION.txt
- Report: "Saved code snippet image to ./fibonacci.png"
Image Resolution and Quality
This skill uses the html-to-image library with pixelRatio: 4 to produce high-quality images with crisp, sharp text. This is the same rendering approach that ray.so uses for its built-in export feature.
Output quality:
- Default: 4x native resolution (frame auto-sizes to content, then rendered at 4x)
- Text is rendered at high DPI, not upscaled from low resolution
- Gradient backgrounds and all CSS styling are preserved
- No unnecessary empty space (frame auto-sizes to fit code)
Adjusting resolution:
- For smaller files: Change
pixelRatio:4topixelRatio:2in the eval command - For maximum quality: Use
pixelRatio:6(same as ray.so's "6x" export option)
Forcing a specific width:
- Only add
&width=NUMBERto the URL if you need a fixed width (e.g., for consistent sizing across multiple images)
Troubleshooting
- If agent-browser is not available: Inform the user and do not proceed
- If curl fails to fetch themes/languages, use these common defaults:
- Themes: breeze, midnight, candy, crimson, falcon, meadow, raindrop, sunset, vercel, supabase, tailwind
- Languages: auto, javascript, typescript, python, rust, go, java, ruby, swift, kotlin, css, html, json, yaml, bash
- If parameters aren't applied: Ensure ALL parameters are in the URL hash (after #), not the query string
- If title isn't showing: The title parameter must be in the hash:
#title=filename.py&code=... - If html-to-image fails to load: Check network connectivity; the library loads from jsdelivr CDN
- If capture returns empty: The frame selector
#frame > divmay have changed; inspect the page structure - For very long code snippets, ray.so may truncate; consider splitting into multiple images
- If the page doesn't load properly, increase the wait time (try 4000ms or more)
- If you get a blank page: Use a fresh unique session name with
--sessionflag - If data URL is malformed: Ensure quotes and newlines are stripped before base64 decoding