Dispel is built with WXT (a web extension framework), React 19, Zustand for state management, and Tailwind CSS v4. WXT treats each file underDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/thePrnvBot/dispel-web-stylist/llms.txt
Use this file to discover all available pages before exploring further.
src/entrypoints/ as an independent extension context — background service worker, injected content script, side panel, options page, and welcome page — and handles manifest generation, hot module replacement, and browser-specific field differences automatically. This page maps the source tree to the runtime role of each piece so you can navigate the codebase with confidence.
WXT Entrypoints
Each directory undersrc/entrypoints/ corresponds to one context that WXT compiles and registers in the manifest. The five entrypoints and their runtime responsibilities are:
background
Service worker. Calls
setupSidePanel() on startup, wires the toolbar icon click (onBrowserActionClick) and keyboard command (toggle-sidebar / Ctrl+Shift+Y) to toggleSidePanel(), and opens the welcome page in a new tab on first install via onInstalled.content
Injected into every
http/https page. Runs tryApplyLocalStorageFallback() immediately at document_start, then calls applySavedStyles() once the DOM is ready. Tracks viewport HTML size on scroll/mutation (debounced), and handles the full inbound message protocol: APPLY_CSS, CLEAR_STYLES, REFRESH_STYLES, GET_PAGE_HTML, GET_PAGE_META, ACTIVATE_PICKER, and DEACTIVATE_PICKER.sidepanel
Main side-panel React app. Hosts
SidebarPromptInput (prompt entry + model selector), PromptStore (saved prompt history with toggle/edit/delete), CurrentTabBadge (active tab URL display), and the ModeToggle (dark/light/system theme). Syncs its Zustand store with chrome.storage via useSyncSidepanelStore.options
Options page React app. A three-tab layout (
Models, Prompts, Settings) rendered by shadcn Tabs. The Models tab hosts the provider configuration table and the add/edit model form. Settings contains global preferences.welcome
First-install onboarding page. Opened automatically by the background service worker the first time the extension is installed. Guides new users through adding an API key and making their first prompt.
WXT handles manifest generation, HMR, and browser-specific manifest fields (such as
browser_specific_settings for Firefox Gecko). You rarely need to edit the manifest directly — the canonical manifest configuration lives in wxt.config.ts.Component Layers
React UI components are split into three directories undersrc/components/, each with a distinct ownership model:
src/components/custom/
Project-specific components. Divided into
sidepanel/ (prompt input, prompt store, current-tab badge, debug output, draft controls) and options/ (model form, provider icon, settings fields). All new feature UI belongs here.src/components/ui/
shadcn components. Primitive, unstyled-first components such as
button, input, dialog, tabs, sonner, mode-toggle, and others. Generated by shadcn and generally not edited by hand.src/components/ai-elements/
Vendored AI chat UI primitives. Pre-built chat-oriented building blocks (message bubbles, code blocks, streaming indicators). Biome linter rules for a11y and security are relaxed for this directory because the vendored components pre-date those checks.
src/lib/
shadcn
cn() helper. The lib/utils.ts file exported by shadcn that merges Tailwind class names with clsx + tailwind-merge. Imported throughout custom and ui components.State Management
Zustand stores live insrc/stores/ and own all runtime state that spans component boundaries:
sidepanel-store.ts — main side panel state
sidepanel-store.ts — main side panel state
The primary store for the side panel. Holds the active draft (the CSS currently streaming into the page), the prompt list (saved prompt history), streaming status, any elements captured by the element picker, and the currently selected model. All side panel components read from and write to this store.
form-state/ — options page form state
form-state/ — options page form state
A sub-directory of stores that manage the controlled form state for the options page — specifically the add/edit model form fields and validation state, kept separate from the side panel store to avoid cross-context pollution.
sidepanel-store-sync.ts — chrome.storage sync
sidepanel-store-sync.ts — chrome.storage sync
Exports the
useSyncSidepanelStore hook, which is called once at the top of the side panel App. It subscribes to chrome.storage change events and merges remote changes (for example, updates made in the options page) back into the Zustand store, keeping both contexts in sync without a shared message bus.Services
src/services/ contains one file per user-initiated action. Each service orchestrates multiple utilities and storage calls to complete a workflow end-to-end:
submit-prompt.ts
The main prompt pipeline. Fetches page context (
GET_PAGE_META + GET_PAGE_HTML), creates a draft entry in storage, calls streamCSS to request CSS from the AI provider, applies each CSS chunk to the page via APPLY_CSS, and appends the completed turn to the draft.save-draft.ts
Promotes the current draft to the permanent prompt history. Writes the compiled CSS to storage, removes the draft record, and sends
REFRESH_STYLES to the content script so the page reloads from saved state.discard-draft.ts
Cancels an in-progress or completed draft. Removes the draft from storage and sends
CLEAR_STYLES followed by REFRESH_STYLES to revert the page to its last saved style state.resolve-warning.ts
Handles the external CSS source warning flow. When the content script detects that the page loads styles from an external stylesheet, the user can either dismiss the warning (keep streaming) or revert (discard the draft and restore the page).
Utilities
src/utils/ contains pure, side-effect-free helpers. The most important modules are:
llms.ts — AI provider abstraction
llms.ts — AI provider abstraction
Exports
streamChat, the function that calls an AI provider and yields CSS token by token. Also contains a model factory that selects the correct Vercel AI SDK provider package (@ai-sdk/openai, @ai-sdk/anthropic, @ai-sdk/google, @openrouter/ai-sdk-provider, or @ai-sdk/openai-compatible) based on the model’s configured provider type.system-prompt.ts — prompt construction
system-prompt.ts — prompt construction
Exports
STATIC_GUIDELINES (the invariant rules the model must follow when generating CSS) and generateSystemPrompt, which combines the static guidelines with the page’s URL, title, viewport dimensions, and any element-picker context into the final system message sent to the model.agent-tools.ts — AI tool definitions
agent-tools.ts — AI tool definitions
Defines the Vercel AI SDK tool objects passed to the model:
generateCss (the primary output tool), reportError (lets the model surface a structured error instead of invalid CSS), and createGetPageHTMLTool (a factory that wraps the GET_PAGE_HTML message round-trip so the model can request a specific element’s HTML).storage.ts — typed chrome.storage
storage.ts — typed chrome.storage
Wraps
chrome.storage.local with Zod-validated typed items. Every key stored by the extension (prompts, drafts, models, settings) has a corresponding typed item definition here, ensuring reads and writes are type-safe at compile time.messaging/ — typed message protocol
messaging/ — typed message protocol
Defines
ProtocolMap, a TypeScript map of message name → { input, output } pairs covering every message exchanged between the background, content, and side panel contexts. Also exports sendRuntimeMessage and createMessageRouter helpers built on @webext-core/messaging.html-extractor.ts — viewport HTML sanitization
html-extractor.ts — viewport HTML sanitization
Exports
extractViewportHTML and measureViewportHTML. The extractor walks the DOM, removes <script>, hidden elements, and most attributes, and returns a compact HTML snapshot of the visible viewport that is safe and small enough to include in an AI prompt.Platform Splits
Browser differences between Chrome and Firefox (primarily in how the side panel API works) are handled insrc/platform/:
@/platform/sidepanel import alias at build time via a Vite resolve.alias in wxt.config.ts: when the build target is firefox, it points to sidepanel.firefox.ts; otherwise it points to sidepanel.chrome.ts. This means the background entrypoint imports @/platform/sidepanel without any runtime branching.