Was maschinell geprüft ist, steht jetzt namentlich da — Manifest, Berechtigungs-Rahmen, Syntax, Sammel-Logik (12 Tests) und der Transport End-to-End gegen die Live-Instanz (POST → 303 → Einweg-Token → Abholung → zweite Abholung 404). Ungetestet bleibt genau die MV3-Verdrahtung dazwischen: action.onClicked → scripting.executeScript → fetch mit credentials. Die braucht einen echten Browser-Load; eine Automatisierung darf chrome://extensions nicht bedienen. Dafür jetzt eine Anleitung mit erwartetem Verhalten (Golem-Artikel, Cookie-Abfrage wegklicken, Symbol zeigt … dann ✓) und wo im Fehlerfall der Grund steht. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
126 lines
5.4 KiB
Markdown
126 lines
5.4 KiB
Markdown
# Pageta — Browser-Erweiterung
|
||
|
||
Ein Klick speichert die geöffnete Seite in die Lese-Liste — **mit dem Text,
|
||
den du siehst**.
|
||
|
||
## Wozu, wenn es das Bookmarklet schon gibt
|
||
|
||
Das Bookmarklet kann dasselbe, aber es muss erst in die Lesezeichenleiste
|
||
gezogen werden — für die meisten Menschen ist das die Stelle, an der sie
|
||
aufhören. Die Erweiterung ist ein Symbol in der Leiste.
|
||
|
||
Beide lösen dasselbe Problem: Quellen wie golem.de antworten auf **jede**
|
||
Anfrage ohne Cookies mit ihrer Zustimmungs-Abfrage statt mit dem Artikel —
|
||
auch dann, wenn der Abrufer sich als normaler Browser ausgibt (gemessen
|
||
2026-07-27 aus dem Produktions-Container, siehe
|
||
[`docs/EXTRAKTION.md`](../../docs/EXTRAKTION.md)). Serverseitig kommt da
|
||
nichts vorbei. Der Artikel existiert nur in deinem Tab.
|
||
|
||
## Wie sie arbeitet
|
||
|
||
```
|
||
Klick aufs Symbol
|
||
→ activeTab erlaubt EINMALIG, diesen einen Tab zu lesen
|
||
→ collectPageta() im Tab: Seite 1 + erkannte Folgeseiten, je geleichtert
|
||
→ { url, title, pages, missing }
|
||
→ POST <instanz>/lese-liste/add/receive (dieselbe Route wie das Bookmarklet)
|
||
→ 303 auf /lese-liste/add?pickup=<einweg-token> (2 Minuten gültig)
|
||
→ neuer Tab holt das Payload ab und speichert lokal-first über event-sync
|
||
```
|
||
|
||
Es gibt bewusst **einen** Eingang für Bookmarklet und Erweiterung. Zwei
|
||
Transportwege für dieselbe Sache wären zwei Wege, die auseinanderdriften.
|
||
|
||
## Mehrseitige Artikel
|
||
|
||
Ein Artikel auf drei Seiten liegt komplett hinter derselben Wand — Seite 2
|
||
holt der Server genauso wenig wie Seite 1. Also holt sie der Tab: ein
|
||
`fetch` aus der Seite heraus trägt die Sitzung mit.
|
||
|
||
Folgeseiten findet [`page-collector.js`](page-collector.js) ohne
|
||
quellenspezifische Selektoren — ein Link zählt, wenn seine Adresse sich von
|
||
der aktuellen **nur in einem Seitenzähler** unterscheidet (`-2.html`,
|
||
`?page=2`, `/seite/2/`); findet sich keine Seitenliste, folgt es der
|
||
`rel="next"`-Kette. Gedeckelt bei 20 Seiten und 6 MB, mit kurzer Pause
|
||
zwischen den Abrufen.
|
||
|
||
Diese Datei ist eine **zeichengleiche Kopie** von
|
||
`apps/web/src/lib/page-collector.js` — dieselbe Logik treibt das Bookmarklet
|
||
(dort als Quelltext in der `javascript:`-Adresse). Einen Bundler, der beide
|
||
bedient, gibt es nicht; die Erweiterung soll bewusst ohne Build auskommen.
|
||
`scripts/check.mjs` bricht ab, sobald die Kopien auseinanderlaufen.
|
||
|
||
## Was sie nicht tut
|
||
|
||
- **Keine dauerhafte Leseerlaubnis.** `activeTab` gilt für den Klick, nicht
|
||
für den Verlauf. Ohne Klick sieht die Erweiterung keine Seite.
|
||
- **Kein API-Schlüssel.** Es zählt die Anmeldung, die dein Browser ohnehin
|
||
hat. Nichts Geheimes liegt in der Erweiterung.
|
||
- **Keine Telemetrie, keine Fremd-Server, kein Hintergrund-Verkehr.**
|
||
Sie spricht ausschließlich mit deiner Pageta-Instanz, und nur wenn du
|
||
klickst.
|
||
|
||
## Lokal ausprobieren
|
||
|
||
**Chrome / Edge / Brave**
|
||
|
||
1. `chrome://extensions` → Entwicklermodus an
|
||
2. „Entpackte Erweiterung laden" → diesen Ordner wählen
|
||
3. Symbol anheften, Artikel öffnen, klicken
|
||
|
||
**Firefox** (ab 121)
|
||
|
||
1. `about:debugging#/runtime/this-firefox`
|
||
2. „Temporäres Add-on laden…" → `manifest.json` wählen
|
||
|
||
Eine andere Instanz (Selbst-Hosting) stellst du in den Erweiterungs-
|
||
Einstellungen ein; die Erlaubnis für diese Adresse wird dort abgefragt.
|
||
|
||
### Der erste Klick — was passieren soll
|
||
|
||
Nimm eine Seite mit Zustimmungs-Abfrage, an der der Server scheitert; ein
|
||
Golem-Artikel ist der verlässlichste Fall.
|
||
|
||
1. Artikel in einem Tab öffnen, Cookie-Abfrage **wegklicken** (das ist der
|
||
Punkt — die Erweiterung nutzt genau diese Zustimmung).
|
||
2. Auf das Pageta-Symbol klicken.
|
||
3. Erwartung: Symbol zeigt kurz `…`, dann `✓`; ein neuer Tab öffnet
|
||
`/lese-liste/add?pickup=…` und speichert.
|
||
4. In der Lese-Liste steht der Artikel mit **echtem Titel und Volltext** —
|
||
nicht mit „Quelle blockiert".
|
||
|
||
Wenn das Symbol `!` zeigt: Rechtsklick → „Verwaltung" → „Service Worker"
|
||
öffnet die Konsole der Erweiterung; dort steht der Grund.
|
||
|
||
Läuft der Klick auf einer internen Seite (`chrome://`, Store-Seiten) oder
|
||
auf Pageta selbst, zeigt das Symbol `–` und tut bewusst nichts.
|
||
|
||
## Paketieren
|
||
|
||
```bash
|
||
pnpm --filter @pageta/extension package # → dist/pageta-extension-<version>.zip
|
||
```
|
||
|
||
Das Zip ist das, was in den Chrome Web Store bzw. bei addons.mozilla.org
|
||
hochgeladen wird.
|
||
|
||
## Offene Punkte
|
||
|
||
- **Store-Einreichung ist nicht angestoßen.** Chrome Web Store verlangt ein
|
||
Entwicklerkonto (einmalig 5 USD) und eine Datenschutz-Begründung je
|
||
Berechtigung; Firefox verlangt Signierung. Beides ist Vereinsarbeit, keine
|
||
Programmierarbeit — bis dahin ist die Erweiterung über „entpackt laden"
|
||
nutzbar.
|
||
- **Kein Kontextmenü-Eintrag** („Link in Pageta speichern"). Braucht die
|
||
`contextMenus`-Berechtigung; erst einbauen, wenn jemand danach fragt.
|
||
- **Der Klick-Weg im Browser ist ungetestet.** Maschinell geprüft sind:
|
||
Manifest, referenzierte Dateien, Berechtigungs-Rahmen, Syntax aller
|
||
Skripte (`scripts/check.mjs`), die Sammel-Logik (12 Tests in
|
||
`apps/web/src/lib/page-collector.test.ts`) und der **Transport
|
||
End-to-End** gegen die Live-Instanz: POST → 303 → Einweg-Token →
|
||
Abholung liefert das Payload → zweite Abholung 404.
|
||
Was fehlt, ist genau die MV3-Verdrahtung dazwischen —
|
||
`action.onClicked` → `scripting.executeScript` → `fetch` mit
|
||
`credentials` cross-origin. Die braucht einen echten Browser-Load; eine
|
||
Automatisierung darf `chrome://extensions` nicht bedienen. Anleitung
|
||
oben, dauert eine Minute.
|