Skip to main content

Documentation 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.

Dispel keeps two separate layers of style storage. An active draft is a temporary, in-progress session for a hostname: it accumulates one cssPatch per prompt turn and exists only while you are actively working. A prompt history entry is a permanent record saved to local storage when you explicitly save a draft — it carries a name, the compiled CSS, and a URL, and it is reapplied automatically every time you visit that page. Understanding the distinction helps you work confidently without worrying about losing unsaved work or accidentally persisting unfinished changes.

Active drafts

Every time you submit a prompt on a new hostname, Dispel creates an IActiveDraft record keyed by hostname in local:activeDrafts. Each subsequent prompt on the same hostname appends a new IDraftTurn to the draft’s turns array.
// From src/utils/schemas/storage.ts

export const DraftTurnSchema = z.object({
  cssPatch: z.string(),   // complete replacement CSS output for this turn
  id: z.string(),         // crypto.randomUUID()
  prompt: z.string(),     // the text of the prompt that produced this patch
  timestamp: z.number(),  // Date.now()
});

export type IDraftTurn = z.infer<typeof DraftTurnSchema>;

export const ActiveDraftSchema = z.object({
  createdAt: z.number(),          // when the draft was first created
  hostname: z.string(),           // e.g. "github.com"
  turns: z.array(DraftTurnSchema),
  updatedAt: z.number(),          // updated on every new turn
  url: z.string(),                // the full URL of the tab at draft creation
});

export type IActiveDraft = z.infer<typeof ActiveDraftSchema>;
The compiled CSS shown on the page at any point is all cssPatch values joined together — draft.turns.map(t => t.cssPatch).join("\n") — which means every turn’s output is additive and the latest turn’s stylesheet supersedes any conflicting rules from earlier turns. The full compiled CSS is also passed back to the model as CURRENT STYLES context on each new prompt, so the model always outputs a complete replacement sheet rather than a partial diff.
Active drafts are stored in local:activeDrafts (extension local storage). They persist across browser sessions until you save or discard them, so your in-progress work survives a browser restart.

Saving a draft

When you are happy with the result, click Save in the side panel. A dialog asks you to give the style change a name — this becomes the prompt field on the saved entry and the title shown in the prompt history list. The saveDraftWorkflow executes the following steps in order:
1

Read the active draft

Reads the current IActiveDraft for the hostname from storage.
2

Extract the last CSS patch

Calls getLastCssPatch(draft) — takes draft.turns.at(-1)?.cssPatch — as the canonical CSS to save. This is the most recent complete stylesheet produced by the session.
3

Create a PromptEntry

Constructs a new IPromptEntry with a fresh crypto.randomUUID() id, your chosen title as the prompt, the CSS, the tab’s URL, the current timestamp, and applied: true.
4

Prepend to prompt history

Prepends the new entry to promptHistoryStorage so it appears at the top of the history list.
5

Remove the active draft

Deletes the draft from local:activeDrafts for this hostname — the session is now finalised.
6

Reload saved styles

Calls reloadSavedStyles on the current tab so the page is immediately styled by the newly saved entry rather than the draft.

Discarding a draft

To throw away the current session without saving, click Discard. The discardDraftWorkflow first calls reloadSavedStyles to revert the page to whatever was previously saved, then removes the draft from local:activeDrafts.
Discarding a draft cannot be undone. All prompt turns in the draft are permanently deleted and the CSS is reverted to the previously saved state. If you want to preserve even part of the session, save first and then edit the entry later.

Prompt history

Saved styles are stored as an array of IPromptEntry items in promptHistoryStorage. The schema is:
// From src/utils/schemas/storage.ts

export const PromptEntrySchema = z.object({
  applied:   z.boolean(),  // whether this entry is currently active on the page
  css:       z.string(),   // the compiled CSS for this entry
  id:        z.string(),   // crypto.randomUUID()
  prompt:    z.string(),   // the name you gave it when saving
  timestamp: z.number(),   // Date.now() at save time
  url:       z.string(),   // the URL of the tab when the entry was saved
});

export type IPromptEntry = z.infer<typeof PromptEntrySchema>;
The history list in the side panel’s Prompt Store tab renders each entry as a collapsible PromptItem. From there you can:

Toggle on / off

Click Unapply to hide the styles from the page without deleting the entry (applied flips to false). Click Apply to re-enable it. Only entries where applied !== false are compiled into the page on load.

Inspect the CSS

Expand an entry to see a syntax-highlighted code block of the saved CSS. Useful for debugging which rule is responsible for a particular style.

Delete permanently

Click the trash icon to remove an entry from history entirely. This is irreversible.

Per-URL filtering

getEntriesForHostname filters history to entries whose url hostname matches the current tab’s hostname, so the store always shows only styles relevant to the page you are on.

Style reapplication on page load

When you navigate to a page, the Dispel content script sends a REFRESH_STYLES message to the background service worker. The background looks up all IPromptEntry items for the current hostname where applied !== false, compiles their CSS in chronological order, and injects it into the page — typically before the first paint. This is how Dispel achieves per-site style memory without requiring any page-side code changes.

External CSS warning

After every prompt turn, Dispel scans the generated CSS for external resource references using detectExternalSources. The detector looks for four patterns:
// From src/utils/external-css-detector.ts

const PATTERNS = [
  { regex: /url\(\s*(["']?)(.*?)\1\s*\)/gi, groupIndex: 2 },          // url(...)
  { regex: /@import\s+url\(\s*(["'])(.*?)\1\s*\)/gi, groupIndex: 2 }, // @import url("...")
  { regex: /@import\s+(["'])(.*?)\1/gi, groupIndex: 2 },              // @import "..."
  { regex: /@import\s+url\(\s*([^"'\s)]+)\s*\)/gi, groupIndex: 1 },   // @import url(unquoted)
];
Data URIs (data:) and fragment-only references (#) are excluded. If any external URL is found, Dispel immediately reverts the page to its previously saved styles and shows a warning dialog listing the detected URLs. The dialog asks you to either:
  • Accept — re-apply the CSS with the external references and save the turn anyway (the warning is dismissed for the rest of the session).
  • Reject — dismiss the dialog; the revert has already been applied and the draft turn is discarded.
The system prompt instructs the model to avoid external resources unless you explicitly request them. The warning dialog is a safety net for cases where the model includes a remote font or CDN asset despite that instruction.

Build docs developers (and LLMs) love