Dispel uses a typed runtime messaging protocol built onDocumentation 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.
@webext-core/messaging. All messages exchanged between extension components — the background service worker, content script, side panel, and options page — are defined in a single ProtocolMap interface. This central contract means TypeScript enforces both payload shapes and return types at every call site, eliminating an entire class of runtime messaging bugs that plague loosely-typed extensions.
All messages use the
@webext-core/messaging library, which provides type-safe send and receive with fully inferred payloads. You never cast to any to satisfy the message bus.The ProtocolMap
Every message Dispel can send is declared as a method signature onProtocolMap. The parameter type is the payload, and the return type is what the handler resolves with. No-argument messages use () and void returns use void.
ContentCommand and ContentEvent derived types
ContentCommand and ContentEvent derived types
Two utility types are derived from
ProtocolMap to separate commands sent to the content script from events broadcast by it:ProtocolMap cover the full lifecycle of a styling session:
ACTIVATE_PICKER
Sent from the side panel to the content script. Puts the content script into element-picker mode, overlaying a hover highlight on the page so the user can click an element to target it.
DEACTIVATE_PICKER
Sent from the side panel to the content script. Tears down the picker overlay and restores normal pointer events without selecting an element.
APPLY_CSS
Sent to the content script with
{ css: string }. The content script injects the provided CSS into a managed <style> tag, replacing any previously injected rules for an instant live preview.CLEAR_STYLES
Sent from the side panel to the content script. Removes all Dispel-injected CSS from the page, reverting it to its original appearance.
ELEMENT_PICKED
Broadcast from the content script to the side panel when the user clicks an element while picker mode is active. Carries an
IPickedElement payload with outerHTML, label, and optional id.PICKER_CANCELLED
Broadcast from the content script to the side panel when the picker is dismissed (e.g. via Escape key) without an element being selected.
GET_PAGE_HTML
Sent to the content script from the background AI agent. Accepts an optional
{ selector?: string } to scope extraction to a specific element. Returns a sanitized HTML string of the visible viewport, or the matched element’s subtree.GET_PAGE_META
Sent to the content script from the background or side panel. Returns a
PageContextResponse containing the page title, URL, and viewport dimensions including scroll offsets.REFRESH_STYLES
Sent to the content script on page load. Instructs the content script to re-read saved styles from the prompt history and re-inject them, restoring the persisted appearance after navigation.
VIEWPORT_HTML_SIZE
Broadcast from the content script after extracting viewport HTML. Carries
{ size: number } — the character count of the extracted HTML — so the side panel can display a debug indicator about the size of DOM context sent to the AI.Message Directions
Each message flows in a well-defined direction. The table below shows the originating component and the receiving component for every protocol message.| Message | From | To |
|---|---|---|
ACTIVATE_PICKER | Side panel | Content script |
APPLY_CSS | Background / side panel | Content script |
CLEAR_STYLES | Side panel | Content script |
DEACTIVATE_PICKER | Side panel | Content script |
ELEMENT_PICKED | Content script | Side panel |
GET_PAGE_HTML | Background (AI agent) | Content script |
GET_PAGE_META | Background / side panel | Content script |
PICKER_CANCELLED | Content script | Side panel |
REFRESH_STYLES | Content script | Content script |
VIEWPORT_HTML_SIZE | Content script | Side panel |
Using sendTabMessage
When the background service worker or any extension page needs to send a message to a specific browser tab’s content script, it callssendTabMessage. The helper wraps @webext-core/messaging’s sendMessage in a Result-returning function, so the caller always handles the case where no content script is listening.
TAB_NO_RECEIVER— the content script is not yet injected or has been unloaded. This is common immediately after a navigation event.TAB_MESSAGE_FAILED— the message was sent but an unexpected error occurred during delivery or handling.
sendRuntimeMessage, which has an equivalent signature but omits the tabId parameter and maps failures to MESSAGING_SEND_FAILED.
Error Codes
APP_ERROR_CODES
AppErrorCode is a union of all recognised error strings. Every AppError object carries one of these codes, enabling exhaustive pattern matching in error handlers.
APP_ERROR_CODES reference table
APP_ERROR_CODES reference table
| Code | Description |
|---|---|
MESSAGING_SEND_FAILED | A runtime message (non-tab) could not be delivered. |
TAB_GET_FAILED | browser.tabs.get() threw an unexpected error. |
TAB_QUERY_FAILED | browser.tabs.query() threw an unexpected error. |
TAB_NOT_FOUND | No tab matched the requested tab ID. |
TAB_MESSAGE_FAILED | A tab-targeted message was sent but delivery or handling failed. |
TAB_NO_RECEIVER | No content script listener exists in the target tab. |
TAB_CREATE_FAILED | browser.tabs.create() threw an unexpected error. |
SIDEBAR_TOGGLE_FAILED | The sidebar could not be toggled open or closed. |
SIDEPANEL_OPEN_FAILED | browser.sidePanel.open() failed. |
SIDEPANEL_SETUP_FAILED | Side-panel initial setup threw an error. |
OPEN_OPTIONS_PAGE_FAILED | The options page could not be opened. |
STORAGE_READ_FAILED | chrome.storage.local read threw an unexpected error. |
STORAGE_WRITE_FAILED | chrome.storage.local write (via modifyStorage) failed. |
VALIDATION_FAILED | A Zod schema parse failed on user-supplied input. |
INVALID_HOSTNAME | The active tab’s URL has no valid hostname. |
NO_ACTIVE_TAB | There is no active tab in the current window. |
MISSING_API_KEY | No API key is configured for the selected provider. |
RATE_LIMITED | The provider returned HTTP 429. |
CONTEXT_LIMIT_REACHED | The prompt exceeds the model’s context window. |
STREAM_INCOMPLETE | The model completed without calling generateCss or reportError. |
API_ERROR | A generic, unclassified provider API error. |
ELEMENT_NOT_FOUND | The AI could not find the requested element in the DOM. |
AMBIGUOUS_REQUEST | The request matches too many elements for safe targeting. |
BROWSER_ACTION_UNAVAILABLE | The browser action API is unavailable in this context. |
LLM_ERROR_CODES
LLM_ERROR_CODES is a strict subset of APP_ERROR_CODES that the AI agent may emit via its reportError tool. These codes are the only values the model is permitted to report — the Zod schema on reportError’s input enforces this constraint.
| Code | When the model should use it |
|---|---|
RATE_LIMITED | The upstream provider is rate-limiting requests. |
CONTEXT_LIMIT_REACHED | The combined prompt and page HTML exceed the model’s context window. |
STREAM_INCOMPLETE | The generation was interrupted before a terminal tool was called. |
API_ERROR | An unclassified API-level failure occurred. |
ELEMENT_NOT_FOUND | The requested element is not present in the visible viewport. |
AMBIGUOUS_REQUEST | The request cannot be safely resolved to a unique selector. |
Result Type
All fallible operations in Dispel — messaging, storage reads and writes, and AI streaming — return aResult<T, E> discriminated union rather than throwing exceptions. This pattern forces callers to handle both outcomes before accessing a value.
_tag discriminant to branch on success or failure:
map, flatMap, getOrElse, isOk, and fromPromise — are exported from @/utils/result for composing Result chains without nested if blocks.