A cross-browser extension (Chrome MV3 + Firefox MV3) that rewrites articles and blog posts on any website to match your personal writing style preferences.
- Style dimensions - five sliders (length, imagery, warmth, formality, simplicity), integer −2 to +2
- Templates - TED Talk, Bible, Personal Letter, Academic, Tabloid
- Style library - save and switch between multiple named styles
- Style extraction - analyze the writing style of any page and import it as a new style
- Auto-mode - automatically detects and rewrites articles on page load
- Manual trigger - rewrite the current page from the popup at any time
- Diff view - side-by-side original vs. rewrite with word-level highlighting; accept or reject
- Known knowledge - optional user profile injected into the prompt so the LLM skips obvious context
- API key setup - popup shows an inline key entry screen when no key is configured
- Provider abstraction - OpenAI in V1, architecture ready for Claude (V2) and Ollama (V3)
┌──────────────────────────────────────────────────────────┐
│ Content Script Background Popup / Options
│ (DOM, Diff View) ◄──► (LLM calls) ◄──► (Svelte UI)
└──────────────────────────────────────────────────────────┘
│
LLMProvider interface
│ │
OpenAI Claude / Ollama (V2/V3)
Rewrite flow:
- Content script scores the page with Readability; if it qualifies, a floating button appears
- User clicks the button (or Auto-mode fires on load) → content script sends
REWRITE_SEGMENTmessages to background - Background calls
openaiProvider.streamRewrite()and streams tokens back via a named port - Content script renders the live Diff View (word-level diff via
jsdiff) - User accepts (DOM is patched in place) or rejects (original restored)
Style extraction flow:
- User clicks "Stil extrahieren" in the popup
- Popup sends
GET_PAGE_SAMPLESto the content script → up to 3000 chars of page text - Popup sends
EXTRACT_STYLEto background → background calls LLM (non-streaming) with extraction prompt - Result (
dimensions+customInstructions) shown inExtractPanel; user can apply to current style or save as new
entrypoints/
background.ts - service worker; LLM streaming, port-based messaging, style extraction
content/
index.ts - message listeners (TRIGGER_REWRITE, GET_PAGE_SAMPLES), auto-rewrite trigger
articleDetector.ts - Readability-based page scoring
autoRewriteOrchestrator.ts - segment iteration, streaming coordination
diffRenderer.ts - word-level diff rendering
diffViewInjector.ts - injects the DiffView overlay into the page
domSegmenter.ts - splits article DOM into rewritable segments
domSurgeon.ts - applies accepted rewrites back to the DOM
segmentClassifier.ts - filters out headings, nav, code, link-dense text
popup/
App.svelte - style picker, dimension sliders, toggles, trigger + extract buttons
ExtractPanel.svelte - shows extracted style with apply / save-as-new actions
options/
App.svelte - 4-tab settings page with vertical sidebar nav
StyleEditorDialog.svelte - modal for creating/editing styles
tabs/
ApiTab.svelte - provider selection, API keys, model picker
StylesTab.svelte - style library list (create, edit, delete, set default)
AutoModeTab.svelte - enable/disable, min-word-count, domain exclusions
KnowledgeTab.svelte - user profile text
src/
fidelity/
checker.ts - post-rewrite fidelity check (entity preservation)
entityExtractor.ts - regex-based number/date/name/quote extraction
llm/
promptBuilder.ts - assembles system + user prompt from style + settings
streamParser.ts - SSE → token stream parser
styleExtractor.ts - LLM prompt + response parser for style extraction
openaiProvider.ts - OpenAI chat completions (streaming)
claudeProvider.ts - stub (V2)
ollamaProvider.ts - stub (V3)
providerRegistry.ts - selects the active provider from settings
messaging/
types.ts - discriminated union of all extension messages
client.ts - sendMessage / openPort helpers for extension pages
storage/
schema.ts - Zod schemas: StyleConfig (5 dims, −2…+2), Settings, StoredState
storageAdapter.ts - getState / setState / updateSettings + subscribers
migrations.ts - schema version migrations (v3)
style-engine/
dimensions.ts - maps integer values (−2…+2) to German prompt fragments
presets.ts - few-shot examples for each template
library.ts - CRUD for the style library (saveStyle, deleteStyle, createStyle)
ui/
dims.ts - DIMS array shared between popup and options for slider rendering
app.css - Tailwind + DaisyUI theme
components/
DiffView.svelte
ToggleSwitch.svelte
Requirements: Bun, Node ≥ 20
bun install
# Chrome (hot-reload)
bun run dev:chrome
# Firefox
bun run dev:firefox
# Production builds
bun run build:chrome # Chrome MV3 → .output/chrome-mv3/
bun run build:firefox # Firefox MV3 → .output/firefox-mv3/
bun run build # Both
# Packaged zips (for store submission)
bun run zip
bun run zip:firefoxChrome: chrome://extensions → Enable Developer mode → Load unpacked → select .output/chrome-mv3/
Firefox: about:debugging → This Firefox → Load Temporary Add-on → select .output/firefox-mv3/manifest.json
bun run test # Vitest unit tests
bun run test:e2e # Playwright E2E tests (requires a build first)
bun run check # svelte-check + tsc --noEmit
bun run lint # ESLintUnit test coverage: storage adapter, migrations, prompt builder, stream parser, segment classifier, fidelity checker, entity extractor, style library, dimension mapping, provider registry.
E2E tests load the built extension into a real Chromium instance. Fixture HTML pages in tests/fixtures/html/ are served by a local server during E2E runs.
Open the Options page (gear icon in the popup) to configure:
| Tab | Settings |
|---|---|
| API & Anbieter | Provider (OpenAI / Claude / Ollama), API key, model selection |
| Style-Bibliothek | Create, edit, delete, and set the default style |
| Auto-Modus | Enable/disable, minimum word count, per-domain exclusions |
| Bekanntes Wissen | User profile text injected into every prompt (max 2000 chars) |
Five sliders, integer −2 to +2:
| Dimension | −2 | 0 (neutral) | +2 |
|---|---|---|---|
| length | Extrem kompakt | Original | Sehr ausführlich (~1.5×) |
| imagery | Rein sachlich | Neutral | Sehr bildhaft |
| warmth | Kalt/distanziert | Neutral | Warm/empathisch |
| formality | Umgangssprachlich | Standard | Akademisch |
| simplicity | Komplex/Fachsprache | Neutral | Sehr einfach |
Der "Seite umformulieren"-Button erscheint nicht / ist ausgegraut
- Die Seite hat zu wenig Text oder wird von der Erweiterung nicht als Artikel erkannt (Readability-Score zu niedrig). Betrifft typischerweise Startseiten, Social-Media-Feeds, Shop-Seiten und reine Linklisten.
- Die Seite ist eine Browser-interne Seite (
chrome://,about:,moz-extension://) - dort kann kein Content Script ausgefuhrt werden. - Die Seite ist ein PDF oder eine lokale Datei.
Auto-Modus startet nicht
- Auto-Modus ist deaktiviert (Popup-Toggle oder Options - Auto-Modus-Tab).
- Die Domain ist in der Ausschlussliste (Options - Auto-Modus-Tab).
- Der Artikel unterschreitet die eingestellte Mindestwortanzahl.
- Die Seite ladt Inhalte dynamisch nach - die Erkennung lauft beim ersten Seitenaufbau. Tab neu laden kann helfen.
Umformulierung schlagt fehl oder zeigt einen Fehler
- API-Key fehlt oder ist ungultig: Key in der Popup-Einrichtungsmaske oder unter Options - API & Anbieter prufen.
- Rate-Limit des Anbieters erreicht: Kurz warten und erneut versuchen.
- Netzwerkfehler oder Timeout (10s): Verbindung prufen, Tab neu laden.
- Ollama oder Claude als Provider ausgewahlt: Diese sind noch nicht implementiert (V2/V3). Zu OpenAI wechseln.
Diff-View erscheint nicht, obwohl die Umformulierung lauft
- Manche Seiten blockieren Shadow DOM oder haben aggressive Content-Security-Policies. In diesem Fall erscheint der Diff nicht, aber der Text wird intern verarbeitet.
- Tab neu laden und erneut versuchen.
Einige Absatze fehlen im Diff
- Uberschriften, Navigationselemente, Code-Blocke und linkdichte Absatze werden vom Segment-Classifier bewusst herausgefiltert und nicht umformuliert.
- Sehr kurze Absatze (unter ca. 20 Worter) werden ebenfalls ubersprungen.
Fidelity-Warnung: "Umformulierung enthalt mogliche Fehler"
- Die Erweiterung pruft nach der Umformulierung, ob Zahlen, Daten, Eigennamen und Zitate erhalten geblieben sind. Bei hohem Schweregrad wird die Umformulierung blockiert. Das ist ein Schutzmechanismus gegen sachliche Verfalsschungen durch das KI-Modell.
| Tool | Role |
|---|---|
| WXT | Extension framework (Vite-based, cross-browser) |
| Svelte 5 (Runes) | UI for popup and options pages |
| Tailwind CSS v4 | Utility styles |
| DaisyUI v5 | Component library (buttons, inputs, toggles, alerts) |
| Zod | Runtime schema validation for storage |
| @mozilla/readability | Article detection and extraction |
| diff (jsdiff) | Word-level diff for the rewrite overlay |
| Vitest | Unit tests |
| Playwright | E2E tests |
| Bun | Package manager and script runner |