pageta/apps/extension/options.js
Till JS d56e007637 feat: Extraktion — Zustände trennen, reparieren, und der Weg an harte Wände
Setzt die Reihenfolge aus docs/EXTRAKTION.md um.

**Qualitätssignal statt Vermutung.** `articleQuality()` unterscheidet vier
Zustände: ok · unextracted (kein Text, aber keine Wand → Server-Versuch
lohnt) · blocked (Quelle hat die Zustimmungsseite geliefert → Server kann
das prinzipiell nicht lösen) · thin. Vorher beantwortete eine einzige
Funktion zwei verschiedene Fragen und war deshalb an beiden Aufrufstellen
ein Stück falsch — die eigenen Tests haben das aufgedeckt. Jetzt getrennt:
`titleIsJustHost` für „ist der Titel unbrauchbar", `articleQuality` für
„was ist mit dem Artikel los".

**Neu extrahieren.** Der Web-Client emittiert jetzt `ArticleEnriched` —
einzeln im Reader, als Stapel in der Liste („N ohne Text neu holen",
seriell, weil jeder Versuch ein Fetch auf eine fremde Seite ist). Das Event
lag samt nicht-destruktivem Reducer schon im Schema, wurde aber nur von
Native genutzt. Kein Schema-Bump nötig.

**Wand-Erkennung ohne Raten.** `redirectedToWall()` prüft das Redirect-Ziel:
wer eine Artikel-URL anfragt und auf /sonstiges/zustimmung/ landet, hat
keinen Artikel bekommen — unabhängig von der Wortzahl. Die alte
Schwelle (300 Wörter) lag bei Golem mit 254 knapp richtig, hätte eine
ausführlichere Consent-Seite aber durchgelassen. Dazu ein
Metadaten-Fallback: hinter der Wand wenigstens og:title/og:description
retten, damit die Karte nicht „golem.de" heisst.

**Browser-Erweiterung** (apps/extension, MV3, Chrome + Firefox). Ein Klick
speichert die gerenderte Seite mitsamt Sitzung — der einzige Weg an harte
Wände. Nutzt denselben /lese-liste/add/receive-Transport wie das
Bookmarklet, damit es einen gepflegten Eingang gibt statt zweier. Bewusst
ohne API-Schlüssel, ohne dauerhafte Leseerlaubnis (activeTab), ohne
Telemetrie; ein check.mjs wacht darüber, dass die Berechtigungen nicht
stillschweigend wachsen.

**Und ein ehrlicheres Schild.** Auf den Karten stand bisher „Cookie-Wand"
auch dort, wo gar keine gemeldet war — genau das zeigte der Screenshot vom
27.07. Jetzt „Quelle blockiert" vs. „Ohne Text", mit je einer Aktion, die
zum Zustand passt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 00:58:47 +02:00

58 lines
1.8 KiB
JavaScript

/**
* Einstellungen der Pageta-Erweiterung: nur die Instanz-Adresse.
*
* Bewusst das einzige, was hier einstellbar ist. Alles andere (Anmeldung,
* Darstellung, KI-Quelle) gehört in die App, nicht in die Erweiterung —
* sonst gibt es zwei Orte für dieselbe Wahrheit.
*/
const api = typeof browser !== 'undefined' ? browser : chrome;
const DEFAULT_INSTANCE = 'https://app.pageta.com';
const field = document.getElementById('instance');
const status = document.getElementById('status');
function say(text, ok = true) {
status.textContent = text;
status.style.color = ok ? '' : 'var(--fg)';
}
api.storage.sync.get('instance').then((stored) => {
field.value = stored?.instance ?? '';
});
document.getElementById('save').addEventListener('click', async () => {
const raw = field.value.trim();
if (!raw) {
await api.storage.sync.remove('instance');
say(`Gespeichert — es gilt wieder ${DEFAULT_INSTANCE}.`);
return;
}
let origin;
try {
origin = new URL(raw).origin;
} catch {
say('Das ist keine vollständige Adresse. Beispiel: https://pageta.example', false);
return;
}
if (!origin.startsWith('https://') && !origin.startsWith('http://localhost')) {
say('Nur https — oder http://localhost für die eigene Entwicklung.', false);
return;
}
// Für eine fremde Instanz braucht die Erweiterung die Erlaubnis, dorthin
// zu senden. Wir fragen sie hier, damit der Klick später nicht still
// scheitert.
const pattern = `${origin}/`;
const granted = await api.permissions.request({ origins: [pattern] }).catch(() => false);
if (!granted) {
say('Ohne die Erlaubnis für diese Adresse kann die Erweiterung dort nicht speichern.', false);
return;
}
await api.storage.sync.set({ instance: origin });
say(`Gespeichert — Artikel gehen jetzt an ${origin}.`);
});