DEV Community

tommy
tommy

Posted on

What If Your Clipboard Knew What It Contained? — Building a Chrome Extension for Auto-Detection

"Is This JSON? Base64? URL Encoded?"

Someone drops an API response in Slack. I copy it. Open a JSON formatter in the browser. Paste. Formatted. Done.

Five minutes later, a Base64 string shows up in another channel. I open a Base64 decoder — wait, this isn't Base64. There's %3D in it. It's URL encoded. I switch to a URL decoder.

I do this about 10 times a day.

The moment I copy the data, its format is already determined. But I'm the one figuring out what it is, and I'm the one picking the right tool. This "mental routing" is quietly exhausting.

"What if the clipboard content was auto-detected, matched to the right tool, and opened — all automatically?"

So I built it.


Chrome extension PureMark Detect is available now.

How It Works in 30 Seconds

Two entry points:

  1. Popup — Click the extension icon -> clipboard is auto-read -> detected format is displayed
  2. Right-click — Select text -> "Open in PureMark" -> straight to the right tool

In both cases, the detected data is converted into a "recipe URL." The data is Base64-encoded into the URL hash, and the tool auto-restores it on the other end. No data is ever sent to a server.

Detected Format Destination
JSON json.puremark.app
Base64 base64.puremark.app
URL Encoded url.puremark.app
Unified Diff diff.puremark.app
Unix Timestamp timestamp.puremark.app

It started with three formats: JSON, Base64, and URL Encoded. After launch, I added detectors for Diff Checker and Timestamp Converter. When all five tools finally had "copy and it auto-routes" working, the product matched the original vision for the first time.


I Spent 3 Days on the Base64 Threshold

The detection algorithm lives in detectors.ts. JSON and URL Encoded were easy — JSON validates with JSON.parse(), URL Encoded just needs one %HH pattern. Clear-cut.

Base64 was the problem.

My first implementation set the minimum length to 20 characters. The rationale: avoid false positives — a plain English word like test is technically valid Base64.

But in practice, short API tokens and JWT segments weren't being detected. 20 characters was safe but useless.

I changed it to 4. Now false positives were a concern. I went back and forth for three days. The final solution:

function isBase64(text: string): boolean {
  const trimmed = text.trim();
  if (trimmed.length < 4) return false;
  if (!/^[A-Za-z0-9+/\s]+=*$/.test(trimmed)) return false;
  try { atob(trimmed.replace(/\s/g, '')); return true; } catch { return false; }
}
Enter fullscreen mode Exit fullscreen mode

Three-stage filtering: length check -> regex -> actual atob() decode verification. Even if the regex passes, if it can't actually be decoded, it's not Base64.

Result: even with a minimum of 4 characters, false positives dropped to near zero. The shift from "regex-only detection" to "actually decode and verify" was the breakthrough.


The Popup Was Completely White

Build -> load Chrome extension -> click icon. Blank white screen.

DevTools showed a flood of net::ERR_FILE_NOT_FOUND. CSS, JS — nothing could be found.

The cause: Vite's base config was left at the default '/'.

Chrome extensions run on the chrome-extension:// protocol. When the browser tries to load /assets/index.css with a root-relative path, it looks for / on the filesystem — which doesn't exist.

// vite.config.ts
export default defineConfig({
  base: './',  // Forget this and you get a white screen
  // ...
});
Enter fullscreen mode Exit fullscreen mode

One line. Thirty minutes to find it. The moment I figured it out, I just stared at the screen. The error message was net::ERR_FILE_NOT_FOUND — zero hints pointing toward the base config. If you're using Vite for Chrome extension development, burn this into your memory.


The Clipboard Wouldn't Read

Next pitfall. Reading the clipboard the moment the popup opens — looks simple, right?

navigator.clipboard.readText() is the "modern API," and the docs say it works in Secure Contexts (HTTPS). But in a Chrome extension popup, it fails intermittently.

The root cause is the popup's lifecycle. A popup is an independent browsing context, and focus handling differs from a regular browser window. Depending on the environment, the Clipboard API permission check doesn't pass.

The solution: fall back to the legacy execCommand('paste'):

const readClipboard = async (): Promise<string> => {
  try {
    return await navigator.clipboard.readText();
  } catch {
    const textarea = document.createElement('textarea');
    textarea.style.cssText = 'position:fixed;opacity:0';
    document.body.appendChild(textarea);
    textarea.focus();
    document.execCommand('paste');
    const text = textarea.value;
    document.body.removeChild(textarea);
    return text;
  }
};
Enter fullscreen mode Exit fullscreen mode

execCommand is deprecated, but in the constrained environment of a Chrome extension popup, it's the most reliable option. Prefer the modern API, fall back when it fails. I still think this was the right call.

Want to experience the detection algorithm yourself? Paste some JSON into json.puremark.app — it auto-formats with zero clicks.


The Day All Padding Disappeared

This one wasn't Chrome-extension-specific — it hit all five PureMark tools.

One day I checked the UI and noticed px-12 padding was having zero effect. Text was flush against the screen edges. All five tools. At once.

The culprit: a @layer base reset I'd added to the shared CSS.

/* This broke everything */
@layer base {
  * { box-sizing: border-box; margin: 0; padding: 0; }
}
Enter fullscreen mode Exit fullscreen mode

In Tailwind v4, CSS Cascade Layers priorities are managed internally. @layer base is supposed to have lower priority than utilities — but it's not that simple. * { padding: 0 } as a universal selector applies to every element and conflicts with Tailwind utility classes.

/* Don't reset padding — let Tailwind's preflight handle it */
* { box-sizing: border-box; margin: 0; }
Enter fullscreen mode Exit fullscreen mode

Lesson learned: If you're using Tailwind v4, don't put universal resets in @layer base. Tailwind's preflight provides appropriate resets already. Add your own padding: 0 and you'll silently disable utility classes across every page.


Takeaways

Before:

  • Eyeball clipboard data: "JSON? Base64? URL Encoded?"
  • Manually open the right tool and paste
  • Pick the wrong format, start over

After:

  • Click extension icon -> auto-detect -> one click to the right tool
  • Right-click menu for the same experience
  • Everything processed locally — no data leaves your machine

Three places I got stuck: Vite's base config, the Clipboard API's unreliability in extension popups, and Tailwind v4's layer conflicts. All things I assumed would "just work," and all places where the Chrome extension context bit back.

PureMark Detect — Chrome Web Store | PureMark — Zero-click simplicity for developers.


References

Top comments (0)