I Was Spending 25 Minutes a Day on Image Uploads

Mar 28, 2026

I write every day. Sometimes twice. For a while that meant I was also designing, naming, uploading, and formatting images six times a day, and none of that was writing. It was just tax on the thing I actually wanted to do, and I had been paying it so long I stopped noticing how much it cost.

Then I sat down and fixed it properly, and what used to take 25 minutes a day now takes about a minute.

This is the story of how that happened, and what I built to make it work.

What the workflow looked like before

Every time I wanted to add an image to a blog post or diary entry, the sequence went roughly like this. I took a screenshot with Shottr, which auto-named the file something like SCR-20260327-stlp.jpeg. Then I opened Finder, hunted for the file, renamed it to something descriptive, dragged it into my upload flow, waited for it to land on R2, copied the URL, switched back to Zed, formatted it as a markdown image tag, and finally pasted it into the post.

That is eight steps for one image, and I design most screenshots in Figma first. So, it took even longer. Multiply that by three images per post and two posts a day and you have a genuinely absurd amount of context switching baked into what should be a frictionless publishing habit.

The worst part is that none of those steps required any real thinking. They were pure mechanical overhead, the kind that quietly drains you without feeling dramatic enough to fix.

The first unlock: better images automatically

Before fixing the upload problem I had already solved something adjacent. Every post needed an OG image and a banner, and manually designing those was its own time sink. So I built a dynamic OG image system that automatically tiles screenshots with a clean background and proper aesthetics, which means every image I capture already looks post-ready without any design work on my end.

The MDX component that handles this lives at github.com/egeuysall/www in the mdx-components file if you want to see how it works. The short version is that the component takes a raw screenshot and outputs something that looks intentional, which is exactly the kind of invisible infrastructure that makes daily publishing sustainable.

But even with better-looking images, I was still doing the upload dance manually. That was the next thing to kill.

Building p

The tool is called p. You run it, it uploads, it copies the markdown to your clipboard, it opens Zed, and it deletes the source file. That is the entire interface.

The core of how it works is worth walking through because each piece is solving a specific part of the old friction.

Grabbing the latest file automatically

The first thing p does when you run it without an argument is reach into your screenshots folder and grab the most recently modified file. You never type a filename.

LATEST_FILE="$(ls -1t "$SCREENSHOT_DIR" 2>/dev/null | head -n 1 || true)"
FILE="$SCREENSHOT_DIR/$LATEST_FILE"

This eliminates the single most annoying step in the old flow. The screenshot you just took is always the one that gets uploaded.

Renaming with a persistent counter

Instead of preserving the ugly Shottr filename, p renames the file to post-N.png where N is one higher than the last upload. It tracks this with a simple counter file at ~/.p-counter.

LAST_NUM=0
if [ -f "$COUNTER_FILE" ]; then
  LAST_NUM="$(cat "$COUNTER_FILE" 2>/dev/null || echo 0)"
fi
NEXT_NUM=$((LAST_NUM + 1))
RENAMED_FILE="post-$NEXT_NUM.png"

This means every file on your CDN has a clean, predictable name. post-47.png is infinitely easier to reference or debug than SCR-20260327-stlp.jpeg.

Uploading to R2

The actual upload is one line using rclone, which handles the R2 connection. The file gets copied to a temp directory under the renamed path before upload so the original is never touched until the upload succeeds.

rclone copy "$TMP_FILE" "r2:photos/content" --quiet

Clipboard and Zed

After the upload, p formats the URL as a markdown image tag and copies it straight to your clipboard using pbcopy, then opens Zed. By the time your editor is focused, the paste is ready.

CLIPBOARD_MD="![\"$ALT_TEXT\"]($URL)"
printf '%s' "$CLIPBOARD_MD" | pbcopy
open -a "Zed" >/dev/null 2>&1 || true

The whole thing runs in under two seconds on a good connection. You screenshot, you type p, you paste. That is the entire workflow.

The full script is available at this gist if you want to use it or adapt it for your own CDN setup.

What actually changed

The obvious thing that changed is time. 25 minutes a day down to about one is real, and over a year that is roughly 150 hours back.

But the less obvious thing is that removing friction from image uploads changed how I write. When adding an image costs nothing, you add more of them, and more images means more proof, more specificity, and more of the kind of content that actually shows something rather than just describing it. The quality ceiling went up without any extra effort because the bottleneck was never creativity, it was the cost of inserting a screenshot.

"Screenshot 2026-03-28"

This is the pattern worth generalizing. Every repeated manual step in your daily workflow is a tax on the thing you actually want to do. Most of them feel too small to fix, which is exactly why they compound into something significant. A tool like p is not impressive engineering. It is 60 lines of bash that eliminates a decision class entirely, and that is worth more than a complicated system that still requires you to think.

Find the thing you do most. Remove everything around it that is not the thing itself. Then repeat that for every loop in your workflow until all that is left is the actual work.

That is the whole principle.