

What an insane graph!
Since Deepseek v4 Flash 0731 release, total daily usage on Opencode Go has just being going up. Since August 5th, it has not gone down even once incl. weekends.
It also just hit a new all time high of 15T per day today, up 3T since yesterday.
Absolutely WILD!
Source - https://opencode.ai/data
Built a Chrome extension where right-click menu items run real JavaScript
I'm the dev behind Menu Mod, a Chrome extension for building custom right-click menus. If you've ever wanted a right click to actually do something (hit an API, check a price, kick off a small automation) instead of just opening a search URL, that's what this is.
Scripting support just shipped in v2, and since this is a dev sub, that is the part worth showing.
What happens when a menu item's action type is set to "Run Script."?
TLDR: Each menu item can run a full JS snippet in a sandboxed Web Worker, triggered by a single right click, with a 5 minute execution budget and a 30 MB cap on whatever the script returns. So instead of writing a whole extension for one small repetitive task, you write one script and bind it to a menu item.
Manifest v3 introduced a lot of 'interesting' constraints around user defined code that I have worked hard to address:
1. CORS limitations in the worker, and a proxy around it
The sandbox runs on a null origin, so a plain fetch() inside a script hits normal CORS rules and gets blocked by anything that doesn't explicitly allow null.
To fix this, I added proxyFetch(). proxyFetch routes the request through the extension's own origin instead, which sidesteps that. It requires the user to grant a host permission first through a URL pattern in settings, Chrome prompts for approval and any host that hasn't been explicitly permitted just fails with a clear error.
In practice, this means your script can talk to basically any internal tool or third party API you actually use instead of just APIs with permissive CORS.
2. No DOM but graphics manipulation still works
Workers don't get a DOM, so no document.createElement, no Image(), no HTML or XML parsing. What they do get is OffscreenCanvas for actual 2D rendering, path drawing and pixel manipulation, plus createImageBitmap for hardware accelerated image decoding. FileReaderSync is also available, so a script can read a Blob into base64.
Practically, that means things like resizing an image, adding a watermark (Skip to 2:05 in video) or generating a quick thumbnail on right click are doable without opening any other tool.
3. Post Script Actions
A script can return a plain string for a quick notification, or one or more command objects that run after the script finishes: open a URL, copy to clipboard, download a file, show a notification or chain several of those together.
Example, a script that pulls repo info on right click:
const repo = context.selection?.trim()
const data = await fetch(`https://api.github.com/repos/${repo}`).then(res => res.json())
return [
{ action: 'showNotification', payload: { title: data.full_name, text: `${data.stargazers_count} stars, ${data.open_issues_count} open issues` } },
{ action: 'copyToClipboard', payload: { text: data.clone_url } },
{ action: 'openUrl', payload: { url: data.html_url } }
]
Highlight a repo name like react/react, right click, get a notification with stars and open issues, the clone URL copied to your clipboard and the repo page opened, all from one script tied to one menu item.
Are there any risks?
No DOM access and no direct chrome.* API access means a script can't reach into the page you right-clicked on or touch extension internals directly. It can still make network requests, import code from an allowlisted set of CDNs (jsDelivr, unpkg, esm.sh and a few others) and send data somewhere.
Basically, the same rule you'd apply to anything you paste into a browser console.
If there's a repetitive right-click-then-alt-tab thing you do every day, this is probably a five line script away from being one click/shortcut.
Chrome Web Store - https://chromewebstore.google.com/detail/menu-mod-right-click-menu/hidbgnneihkhinffhjbkkdacpgmdlcgj
Still actively building this, so if you run into rough edges or have some ideas, I'm listening.
---
A few more scripting samples
- CDN import
​
const { default: dayjs } = await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.20/+esm')
const formatted = dayjs().format('dddd, MMMM D, YYYY')
return `Today is ${formatted}`
- One click image watermarker
​
// Global Configuration & Constants
const WATERMARK_URL = 'https://cats-nine-zeta.vercel.app/cat.png'
const WATERMARK_SCALE_FACTOR = 0.1 // Target 10% of the host image's matching shortest side
const CANVAS_PADDING_FACTOR = 0.01 // 1% margin responsive to each respective side's dimension
const DEFAULT_MIME = 'application/octet-stream'
// Directory support for organized downloads
const SAVE_FILENAME = `MenuMod_Watermarks/watermarked_image-${Date.now()}.png`
const base64ToBlob = (base64, mimeType) => {
const binary = atob(base64)
const bytes = new Uint8Array(binary.length)
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i)
}
return new Blob([bytes], { type: mimeType })
}
// Note: Grant the target host in Settings -> Host Permissions.
try {
// 1. Safely extract the dynamic target URL using optional chaining to prevent crashes if context is missing
const targetUrl = context?.srcUrl || ''
// 2. Guard clause: Ensure we actually have an image to work with before hitting the network
if (!targetUrl || targetUrl.trim() === '') {
throw new Error('No valid image URL found. Try right-clicking on an image.')
}
// 3. Fetch both assets through the extension's CORS-bypassing proxy concurrently
let targetResult, watermarkResult
try {
;[targetResult, watermarkResult] = await Promise.all([proxyFetch(targetUrl, { responseType: 'arraybuffer' }), proxyFetch(WATERMARK_URL, { responseType: 'arraybuffer' })])
} catch (netErr) {
throw new Error(`Network request failed. Ensure both hosts are granted in Settings -> Security`)
}
if (!targetResult.ok) {
throw new Error(`Failed to fetch target image (Proxy status: ${targetResult.status}${targetResult.error ? ` - ${targetResult.error}` : ''})`)
}
if (!watermarkResult.ok) {
throw new Error(`Failed to fetch watermark image (Proxy status: ${watermarkResult.status}${watermarkResult.error ? ` - ${watermarkResult.error}` : ''})`)
}
// 4. Decode the Base64 bodies into Blobs concurrently (MIME pulled from response headers)
const targetMime = targetResult.headers['content-type'] || DEFAULT_MIME
const watermarkMime = watermarkResult.headers['content-type'] || DEFAULT_MIME
const [targetBlob, watermarkBlob] = await Promise.all([base64ToBlob(targetResult.body, targetMime), base64ToBlob(watermarkResult.body, watermarkMime)])
// 5. Decode binary data into hardware-accelerated ImageBitmaps in parallel
const [targetImg, rawWatermarkImg] = await Promise.all([createImageBitmap(targetBlob), createImageBitmap(watermarkBlob)])
// 6. Calculate proportional dimensions where the shortest host side scales the corresponding watermark side
let watermarkWidth, watermarkHeight
const aspectRatio = rawWatermarkImg.height / rawWatermarkImg.width // Dynamically handles any image scale
if (targetImg.width <= targetImg.height) {
watermarkWidth = targetImg.width * WATERMARK_SCALE_FACTOR
watermarkHeight = watermarkWidth * aspectRatio
} else {
watermarkHeight = targetImg.height * WATERMARK_SCALE_FACTOR
watermarkWidth = watermarkHeight / aspectRatio
}
// 7. Spin up an isolated OffscreenCanvas mapped exactly to the host image sizes
const canvas = new OffscreenCanvas(targetImg.width, targetImg.height)
const ctx = canvas.getContext('2d')
// 8. Enforce high-quality resampling filters to prevent pixelation during scaling
ctx.imageSmoothingEnabled = true
ctx.imageSmoothingQuality = 'high'
// 9. Composite the graphics: draw the base image, then calculate bottom-right coordinates
ctx.drawImage(targetImg, 0, 0)
const paddingX = targetImg.width * CANVAS_PADDING_FACTOR
const paddingY = targetImg.height * CANVAS_PADDING_FACTOR
const x = targetImg.width - watermarkWidth - paddingX
const y = targetImg.height - watermarkHeight - paddingY
// Scale and stamp the watermark asset directly onto the canvas context
ctx.drawImage(rawWatermarkImg, x, y, watermarkWidth, watermarkHeight)
// 10. Asynchronously encode the canvas pixel array into a standard PNG Blob
const finalBlob = await canvas.convertToBlob({ type: 'image/png' })
// 11. Synchronously convert the binary blob into a Base64 Data URL for message passing
const reader = new FileReaderSync()
const dataUrl = reader.readAsDataURL(finalBlob)
// 12. Return the actionable download payload out of the worker context
return {
action: 'downloadFile',
payload: {
url: dataUrl,
filename: SAVE_FILENAME
}
}
} catch (error) {
// Catch ALL errors—whether it's an undefined context, network failure, or canvas issue
throw new Error(`Watermark & Download Script Error: ${error.message}`)
}
How to create high quality screenshots and promo tiles (Fast & Free)
I'm not a designer, never have been, and I spent way too long trying to get my extension's screenshots to look like the polished ones on the top listings in Canva.
After a few days of experimenting, I built a repeatable process around Google AI Studio that gets me a full set of store assets and figured I'd share, since I've noticed a lot of people in this sub are clearly strong on the dev side but hit the same wall I did when it comes to design and presentation.
For reference, here's a preview for one of my extensions, generated entirely with AI using this exact process - https://mmshots.vercel.app & https://mmshots.vercel.app/thumbnail
Feel free to inspect the page.
Anyway, here is the actual workflow:
1. Collect reference designs first
Go look at what the top extensions in your category are doing. Save the ones you like. You'll notice pretty fast that most top assets do not use real screenshots of the app at all. They're UI mockups built to explain one feature per slide.
2. Screenshot your own extension
Grab real screenshots of your actual UI. This becomes the AI's reference so the mockups it generates actually look like your product instead of something generic/made up.
3. Feed everything into AI Studio
Upload both sets (the reference designs and your real screenshots) into AI Studio and use the PRO model (i.e Gemini 3.1 Pro High). The free tier is generous enough to do this without paying anything. If you have docs or a landing page for your extension, throw those in too. More context genuinely produces better output.
If your docs are HTML, don't paste the raw markup in. Strip it down to just the body content and remove styles, scripts, classes etc. It reduces the token count and keeps noise out of the prompt.
I use this site for that - https://elementor.com/tools/html-code-cleaner/
4. The actual prompt
Ask the model to recreate the reference screenshots using your extension's real UI, output the result as HTML and make sure it's exportable to an image using canvas. I used html-to-image for the export step.
Be explicit that your UI screenshots are just there so it understands your actual design (colors, components, layout patterns) and should not be recreated like-for-like. It tends to want to copy your screenshots pixel for pixel otherwise, instead of using them as inspiration for the mockups.
Sizes that actually matter for the Chrome Web Store:
- Screenshots: 1280x800
- Small promo tile: 440x280
- Marquee: 1400x560
If you are not so sure about the exact design you want, you can tell it to use the top features from your docs/landing page. Ask for around 10 screenshots and maybe 2-3 for the promo/marquee so that you have some options to choose from.
5. Some gotchas
- If you decide to use UI mockups, tell the model to use Tailwind v3, not v4. v3 has way more training data and examples so the output is more reliable.
- html-to-image will silently break on a few things: backdrop-filter, blur and SVGs that are defined once and reused elsewhere through a
<use>tag. For SVGs, either inline them or use external files. - I find the Gemini models aren't great at generating good copy (i.e. headlines, feature blurbs etc). For that, take the screenshots you generated, upload them to Claude or Kimi K3, give it some context on what your extension does and who it's for and ask for copy recommendations.
Finally, the output will most likely not be as polished as you'd like or even have some bugs. Instead of fighting with Gemini in AI Studio, just export the file and clean it up in your coding agent of choice. I find that to be much easier and faster.
That's the whole process. Nothing fancy, just a lot of iteration.
Side note: If you also struggle with creating good looking UIs, check out https://stitch.withgoogle.com. The free tier there is also very generous. Make sure to use the PRO model.