Inside AI Prompt Studio: How a Chrome Extension Bridges 12+ Vision LLMs
AI Prompt Studio is a Chrome extension that reverse-engineers AI image prompts from any web image using your own vision LLM API. This post dives into its message-driven MV3 architecture, Shadow DOM isolation, unified streaming layer, and component-based strategy system.
Leo WangJune 15, 2026
By Leo Wang
Published: 2026-06-15
TL;DR
AI Prompt Studio is a Chrome MV3 extension that reverse-engineers prompts from
images with a right-click and your own vision API. This post covers:
message-driven MV3 architecture → Shadow DOM floating panel → unified
streaming layer for 22 providers → component-based strategy system → video
storyboarding.
If you use AI image generation tools like Stable Diffusion or Midjourney, you
know the pain: you find a stunning reference image online, but translating its
lighting, texture, color palette, and composition into a usable prompt takes
time and skill.
AI Prompt Studio
is a Chrome Manifest V3 extension that solves this. Right-click any image on the
web, and it sends the visual to your own vision LLM API key, which
reverse-engineers a prompt. Seconds later, the result appears in a floating
panel in the bottom-right corner, ready to copy.
This post breaks down the architecture, design decisions, and engineering
challenges behind the project. The tech stack: React 19 + TypeScript 5.7 +
Vite 6 + Tailwind CSS v4 + Chrome MV3.
2. Architecture: Chrome MV3 + Monorepo
One-liner: Four independent execution contexts, all cross-context
communication goes through the Service Worker for unidirectional data flow.
2.1 The Four-Layer MV3 Structure
Manifest V3 splits the extension into four independent execution contexts:
加载图表...
Background Service Worker: Stateless message routing hub. All API calls
and storage/IndexedDB operations go through here exclusively.
Content Script: Injected into every page (all_frames: true). Renders the
floating panel inside Shadow DOM.
Popup: Toolbar popup for quick actions and recent history.
Options Page: Full settings UI (providers, models, strategies) and prompt
library.
2.2 Message Protocol Architecture
The entire extension is message-driven via a RuntimeMessage discriminated
union:
Add a message type by adding one variant. TypeScript ensures exhaustive
switch/match branches.
2.3 Monorepo Structure
AI-Prompt-Studio/
├── src/ # Extension (React + Vite + MV3)
├── website/ # Marketing site (Next.js App Router)
└── services/api/ # Platform API (Bun/Elysia + SQLite + Docker)
Key benefit: Shared type definitions in src/lib/types/ (ProviderId,
RuntimeMessage, etc.) are consumed by all three workspaces, eliminating
protocol drift.
3. Content Script & Shadow DOM Isolation
One-liner: The entire React panel lives inside Shadow DOM for style
isolation; a 6-state FSM manages its lifecycle.
3.1 Why Shadow DOM?
Content Script styles and host page CSS leak into each other. The fix: render
inside Shadow DOM.
The reducer uses a handler-per-event pattern: each event type gets its own
file under reducerHandlers/, preventing the main reducer from ballooning.
4. Unified Streaming Layer for 12+ Providers
One-liner: Three native API protocols → unified SSE parser → graceful
fallback to client-side typewriter. No static model table at runtime.
4.1 Three Protocols, One Pipeline
The extension supports 22 providers behind three distinct API protocols:
Protocol
Streaming Implementation
OpenAI Chat Completions
stream: true + SSE data: lines
Anthropic Messages
stream: true + content_block_delta / text_delta
Google Gemini
:streamGenerateContent?alt=sse endpoint
Adding a new provider is just one entry in the registry table.
4.2 SSE Parsing + Graceful Fallback
The core parser is an async generator that splits bytes into data: lines
(abbreviated):
// src/lib/api/http.tsexportasyncfunction*readSseDataChunks(resp:Response){constreader=resp.body.getReader()constdecoder=newTextDecoder()letbuf=''forawait(constchunkofiterReader(reader)){buf+=decoder.decode(chunk,{stream:true})// split by \n and yield payload after "data:"}}
Graceful fallback: Instead of maintaining a static capability table, the
runtime checks Content-Type at call time. Real SSE for text/event-stream,
otherwise parse full JSON and simulate typewriter reveal.
if(!isSseResponse(resp)){// Fallback: parse full JSON + client-side typewriterconstjson=awaitresp.json()awaitrevealBufferedExtractProgress(onProgress,final,signal)}else{// Normal SSE: accumulate chunks, throttle to 80msforawait(constdataofreadSseDataChunks(resp)){applyOpenAIStreamDelta(cfg,acc,deltaObj)}}
Typewriter simulation key logic:
// Skip animation for text ≤12 chars; max 30 frames, 1500msconstframeCount=Math.min(30,Math.max(2,Math.ceil(final.length/8)))for(leti=1;i<=frameCount;i++){emitPartial(final.slice(0,Math.floor((i/frameCount)*final.length)))awaitsleep(frameMs,signal)}
4.3 Engineering Lesson: Streaming Debug SOP
When streaming output is truncated, follow this order. Do not touch
parameters first.
Debug case: MiniMax M3 generated only 1-2 sentences. Two rounds of wrong
fixes (max_tokens doubled, then max_completion_tokens) did nothing. Root
cause: = instead of += when accumulating delta.content:
// BUGacc.answerRaw=contentPiece// only keeps the last fragment// FIXacc.answerRaw+=contentPiece// accumulates full output
MiniMax docs claim delta.content is cumulative snapshots; the actual API sends
incremental deltas. The debugging sequence:
Check data accumulation first — = vs +=
Verify delta field names — API reality vs code assumptions
Check token parameters — last thing to touch
Inspect SSE parsing — confirm no silent drops
5. Prompt Extraction Strategy System
One-liner: Each strategy tier combines 3 independent component versions.
Metadata and instruction text are physically split, saving ~26KB in Content
Script bundles.
5.1 Component-Based Architecture
Each "strategy tier" is decomposed into three component dimensions:
Dimension
Description
Versions
stylePromptSet
Instruction text per OutputStyle
v0.1.0, shared, v0.3.6, v0.4.0
sampling
Temperature + maxTokens pair
v0.1.0, shared
customJoin
User template position
v0.1.0, shared
A strategy is just metadata + three version references:
Add or remove a tier by changing only this one object. StrategyId is
auto-derived from keyof typeof STRATEGIES_INTERNAL.
5.2 Bundle Size Optimization
Content Script only needs strategy labels, not the full instruction text. These
are physically split into two files:
strategies-meta.ts: lightweight metadata → safe for Content Script
strategies.ts: full prompt text + resolvers → Service Worker / Options
Before splitting, the Content Script was ~26KB larger.
6. Video Sampling & Multi-Image Composition
One-liner: Up to 8 reference images → one prompt. Video frames with
timestamps → storyboard. GIF/WebP flattened to first frame.
Multi-image composition: First image is primary anchor, rest supplement
style/texture/lighting:
constMULTI_IMAGE_INSTRUCTION_NOTE='This message contains multiple reference images. Use the first as the primary anchor for subject, composition, and visual weight; the rest supplement style, material, lighting, and detail.'
Video storyboarding: Auto-capture frames with timestamp anchors for motion
understanding.
Animated image flattening: GIF/APNG/animated WebP reduced to first frame.
7. Engineering Practices & Lessons
One-liner: Service Workers die after 30s idle; IndexedDB is for large data
but Content Scripts can't access it directly; discriminated union messages
guarantee type safety.
7.1 Chrome Extension Pitfalls
Service Worker lifecycle: Terminated after 30s inactivity. Persistent state
must explicitly write to chrome.storage or IndexedDB — global variables are
unreliable.
chrome.storage vs IndexedDB: chrome.storage.local for small configs;
IndexedDB for large structured data. Content Scripts cannot read IndexedDB
directly — must go through Service Worker relay.
Cross-context type safety: RuntimeMessage discriminated union ensures
exhaustive type checking. All handlers registered in background/messages.ts.
7.2 AGENTS.md: Context Anchor for AI Collaboration
The project maintains an AGENTS.md file as an "AI visitor's guide",
covering module maps, message protocols, FSM events, and coding conventions — so
AI agents can grasp the architecture in minutes.
8. Conclusion
AI Prompt Studio is a "small tool, big architecture" project. The core problem
is simple — turn images into prompts — but the engineering challenges were
anything but: multi-protocol API compatibility, graceful streaming fallback,
message-driven cross-context communication, componentized strategy design, and
Content Script bundle optimization.