diora-web/tui/README.md

127 lines
6.9 KiB
Markdown
Raw Permalink Normal View History

# diora-tui
TUI: durchgehende Leseansicht mit web-kompatiblen Anchors, Fußnoten, bidirektionalem Progress-Sync Reader zeigt Bücher jetzt als eine fortlaufende Ansicht über alle Kapitel statt Kapitel für Kapitel (diora_tui/blocks.py, layout.py, book_view.py, reader_screen.py). blockIndex wird exakt wie app.js' EPUB_BLOCK_SELECTOR gezählt (p/h1-6/li/blockquote/dt/dd/figcaption + kindlose divs), inklusive linear="no"-Spine-Einträgen — app.js filtert die nicht, und Skippen hätte sowohl Fußnoten-Ziele verfehlt als auch alle folgenden Blockindizes gegen den Web-Reader verschoben. innerFraction ist eine zeilenbasierte Näherung (Terminal hat keine Pixel-Geometrie), was funktioniert, weil der Furthest-Wins-Vergleich primär nach blockIndex sortiert. Rendering nutzt Textuals Line-API (ContinuousBookView.render_line) statt eines einzelnen riesigen Static — bei großen Büchern (mehrere reale heruntergeladene Bücher haben zehntausende Blocks) hätte ein Static den Layout/Paint-Pass auf über eine Minute gebracht. Zusätzlich cached diora_tui/cache.py das (Buch, Layout)-Paar pro (Datei, Breite) auf Platte für schnelles Wiederöffnen. Text ist auf 120 Zeichen begrenzt und zentriert. Fußnoten (f-Taste, FootnoteScreen): Erkennung wie app.js' _looksLikeFootnoteLink; Ziel-Auflösung sammelt IDs aus dem ganzen Block-Teilbaum (nicht nur vom Block-Tag selbst), weil Fußnoten-Ziele häufig auf einem inneren <a> statt dem umschließenden <p> sitzen. Progress-Sync ist jetzt bidirektional, ohne Übersetzungsschicht nötig, da beide Seiten dasselbe Anchor-Format nutzen: sync zieht book_progress aus dem Snapshot in den lokalen Store (furthest-wins); der Reader schickt bei offenen server-verknüpften Büchern Updates zurück (force: false, im Hintergrund-Worker). Verschlüsselungs-Key-Beschaffung ergänzt um den Fallback localStorage.getItem(...) falls die Clipboard-API in der Konsole verweigert wird. Getestet: Blockindex-/Fußnoten-Korrektheit gegen reale Bücher (u.a. 3686/3686 aufgelöste Fußnoten bei einem Zizek-Band), Anchor-Mathematik per Unit-Test, vollständiger Pilot-Test (Navigation, Scroll, Kapitelsprung, Fußnoten-Peek, Resize), Performance-Messung über mehrere Buchgrößen inkl. Cache-Effekt (größtes Buch: ~34k Blocks, kalt ~20-30s, warm ~5s), Save/Restore-Round-Trip 5x wiederholt gegen eine Race-Condition beim ersten Post-Load-Scroll, und Progress-Push End-to-End gegen einen echten Dev-Server verifiziert.
2026-08-15 17:59:46 +02:00
EPUB-Reader im Terminal — die erste Stufe einer TUI-Version von diora.
Zeigt ein Buch als **eine durchgehende Ansicht** über alle Kapitel hinweg (wie diora's
Web-Reader), nicht Kapitel für Kapitel. Lesefortschritt wird als dieselbe
`"blockIndex:innerFraction"`-Positionsangabe geführt wie der Web-Reader (`books/models.py`,
`EBookProgress`) — `blockIndex` zählt dabei exakt wie `static/js/app.js`s
`EPUB_BLOCK_SELECTOR` (`p, h1-h6, li, blockquote, dt, dd, figcaption` + textbasierte
`div`s ohne Element-Kinder), fortlaufend über das ganze Buch. Dadurch ist die Position
zwischen TUI und Web-Reader direkt vergleichbar, und `diora-tui sync` kann Fortschritt in
beide Richtungen synchronisieren (siehe unten).
Fortschritt wird — genau wie im Web-Reader (`save_progress`) — nur vorwärts überschrieben
("furthest wins"), lokal in `progress.json` (`~/.local/share/diora-tui/`, via
`platformdirs`), damit ein älterer/gestaffelter Lauf nie eine bereits weiter gelesene
Position zurücksetzt.
## Setup
```bash
cd tui
pip install -e .
```
## Nutzung
```bash
diora-tui --library ~/Books # Standard: ~/Books
```
Tastenkürzel:
- `↑`/`k`, `↓`/`j` — zeilenweise scrollen
TUI: durchgehende Leseansicht mit web-kompatiblen Anchors, Fußnoten, bidirektionalem Progress-Sync Reader zeigt Bücher jetzt als eine fortlaufende Ansicht über alle Kapitel statt Kapitel für Kapitel (diora_tui/blocks.py, layout.py, book_view.py, reader_screen.py). blockIndex wird exakt wie app.js' EPUB_BLOCK_SELECTOR gezählt (p/h1-6/li/blockquote/dt/dd/figcaption + kindlose divs), inklusive linear="no"-Spine-Einträgen — app.js filtert die nicht, und Skippen hätte sowohl Fußnoten-Ziele verfehlt als auch alle folgenden Blockindizes gegen den Web-Reader verschoben. innerFraction ist eine zeilenbasierte Näherung (Terminal hat keine Pixel-Geometrie), was funktioniert, weil der Furthest-Wins-Vergleich primär nach blockIndex sortiert. Rendering nutzt Textuals Line-API (ContinuousBookView.render_line) statt eines einzelnen riesigen Static — bei großen Büchern (mehrere reale heruntergeladene Bücher haben zehntausende Blocks) hätte ein Static den Layout/Paint-Pass auf über eine Minute gebracht. Zusätzlich cached diora_tui/cache.py das (Buch, Layout)-Paar pro (Datei, Breite) auf Platte für schnelles Wiederöffnen. Text ist auf 120 Zeichen begrenzt und zentriert. Fußnoten (f-Taste, FootnoteScreen): Erkennung wie app.js' _looksLikeFootnoteLink; Ziel-Auflösung sammelt IDs aus dem ganzen Block-Teilbaum (nicht nur vom Block-Tag selbst), weil Fußnoten-Ziele häufig auf einem inneren <a> statt dem umschließenden <p> sitzen. Progress-Sync ist jetzt bidirektional, ohne Übersetzungsschicht nötig, da beide Seiten dasselbe Anchor-Format nutzen: sync zieht book_progress aus dem Snapshot in den lokalen Store (furthest-wins); der Reader schickt bei offenen server-verknüpften Büchern Updates zurück (force: false, im Hintergrund-Worker). Verschlüsselungs-Key-Beschaffung ergänzt um den Fallback localStorage.getItem(...) falls die Clipboard-API in der Konsole verweigert wird. Getestet: Blockindex-/Fußnoten-Korrektheit gegen reale Bücher (u.a. 3686/3686 aufgelöste Fußnoten bei einem Zizek-Band), Anchor-Mathematik per Unit-Test, vollständiger Pilot-Test (Navigation, Scroll, Kapitelsprung, Fußnoten-Peek, Resize), Performance-Messung über mehrere Buchgrößen inkl. Cache-Effekt (größtes Buch: ~34k Blocks, kalt ~20-30s, warm ~5s), Save/Restore-Round-Trip 5x wiederholt gegen eine Race-Condition beim ersten Post-Load-Scroll, und Progress-Push End-to-End gegen einen echten Dev-Server verifiziert.
2026-08-15 17:59:46 +02:00
- `n` — nächstes Kapitel, `p` — vorheriges Kapitel
- `f` — Fußnote in der Nähe der aktuellen Position anzeigen (Peek-Overlay, `Escape`/`f`/`q`
zum Schließen); Erkennung folgt derselben Heuristik wie `app.js`s
`_looksLikeFootnoteLink` (Link in/um `<sup>`, Klassenname mit note/footnote/fn, oder
`epub:type="noteref"`)
- `Enter` — markiertes Buch aus der Bibliothek öffnen
- `r` (in der Bibliothek) — gelesene Bücher ein-/ausblenden (siehe unten)
- `Escape` / `q` — zurück zur Bibliothek (im Reader) bzw. beenden (in der Bibliothek)
TUI: durchgehende Leseansicht mit web-kompatiblen Anchors, Fußnoten, bidirektionalem Progress-Sync Reader zeigt Bücher jetzt als eine fortlaufende Ansicht über alle Kapitel statt Kapitel für Kapitel (diora_tui/blocks.py, layout.py, book_view.py, reader_screen.py). blockIndex wird exakt wie app.js' EPUB_BLOCK_SELECTOR gezählt (p/h1-6/li/blockquote/dt/dd/figcaption + kindlose divs), inklusive linear="no"-Spine-Einträgen — app.js filtert die nicht, und Skippen hätte sowohl Fußnoten-Ziele verfehlt als auch alle folgenden Blockindizes gegen den Web-Reader verschoben. innerFraction ist eine zeilenbasierte Näherung (Terminal hat keine Pixel-Geometrie), was funktioniert, weil der Furthest-Wins-Vergleich primär nach blockIndex sortiert. Rendering nutzt Textuals Line-API (ContinuousBookView.render_line) statt eines einzelnen riesigen Static — bei großen Büchern (mehrere reale heruntergeladene Bücher haben zehntausende Blocks) hätte ein Static den Layout/Paint-Pass auf über eine Minute gebracht. Zusätzlich cached diora_tui/cache.py das (Buch, Layout)-Paar pro (Datei, Breite) auf Platte für schnelles Wiederöffnen. Text ist auf 120 Zeichen begrenzt und zentriert. Fußnoten (f-Taste, FootnoteScreen): Erkennung wie app.js' _looksLikeFootnoteLink; Ziel-Auflösung sammelt IDs aus dem ganzen Block-Teilbaum (nicht nur vom Block-Tag selbst), weil Fußnoten-Ziele häufig auf einem inneren <a> statt dem umschließenden <p> sitzen. Progress-Sync ist jetzt bidirektional, ohne Übersetzungsschicht nötig, da beide Seiten dasselbe Anchor-Format nutzen: sync zieht book_progress aus dem Snapshot in den lokalen Store (furthest-wins); der Reader schickt bei offenen server-verknüpften Büchern Updates zurück (force: false, im Hintergrund-Worker). Verschlüsselungs-Key-Beschaffung ergänzt um den Fallback localStorage.getItem(...) falls die Clipboard-API in der Konsole verweigert wird. Getestet: Blockindex-/Fußnoten-Korrektheit gegen reale Bücher (u.a. 3686/3686 aufgelöste Fußnoten bei einem Zizek-Band), Anchor-Mathematik per Unit-Test, vollständiger Pilot-Test (Navigation, Scroll, Kapitelsprung, Fußnoten-Peek, Resize), Performance-Messung über mehrere Buchgrößen inkl. Cache-Effekt (größtes Buch: ~34k Blocks, kalt ~20-30s, warm ~5s), Save/Restore-Round-Trip 5x wiederholt gegen eine Race-Condition beim ersten Post-Load-Scroll, und Progress-Push End-to-End gegen einen echten Dev-Server verifiziert.
2026-08-15 17:59:46 +02:00
Der Fließtext ist auf 120 Zeichen Breite begrenzt und horizontal zentriert (lesbarer als
volle Terminalbreite bei breiten Fenstern).
Unten rechts zeigt eine Statusleiste Akkustand (falls vorhanden, via `psutil`) und Uhrzeit,
sekündlich aktualisiert, auf Bibliotheks- und Reader-Ansicht.
Die Bibliotheksansicht ist nach zuletzt geöffnetem Buch sortiert (neueste zuerst; anhand
des Zeitstempels der zuletzt gespeicherten Position), und blendet gelesene Bücher
standardmäßig aus (`EBook.is_read` aus dem Sync-Snapshot, lokal in `library.json`
gespiegelt) — `r` zeigt sie wieder an, für diese Sitzung.
TUI: durchgehende Leseansicht mit web-kompatiblen Anchors, Fußnoten, bidirektionalem Progress-Sync Reader zeigt Bücher jetzt als eine fortlaufende Ansicht über alle Kapitel statt Kapitel für Kapitel (diora_tui/blocks.py, layout.py, book_view.py, reader_screen.py). blockIndex wird exakt wie app.js' EPUB_BLOCK_SELECTOR gezählt (p/h1-6/li/blockquote/dt/dd/figcaption + kindlose divs), inklusive linear="no"-Spine-Einträgen — app.js filtert die nicht, und Skippen hätte sowohl Fußnoten-Ziele verfehlt als auch alle folgenden Blockindizes gegen den Web-Reader verschoben. innerFraction ist eine zeilenbasierte Näherung (Terminal hat keine Pixel-Geometrie), was funktioniert, weil der Furthest-Wins-Vergleich primär nach blockIndex sortiert. Rendering nutzt Textuals Line-API (ContinuousBookView.render_line) statt eines einzelnen riesigen Static — bei großen Büchern (mehrere reale heruntergeladene Bücher haben zehntausende Blocks) hätte ein Static den Layout/Paint-Pass auf über eine Minute gebracht. Zusätzlich cached diora_tui/cache.py das (Buch, Layout)-Paar pro (Datei, Breite) auf Platte für schnelles Wiederöffnen. Text ist auf 120 Zeichen begrenzt und zentriert. Fußnoten (f-Taste, FootnoteScreen): Erkennung wie app.js' _looksLikeFootnoteLink; Ziel-Auflösung sammelt IDs aus dem ganzen Block-Teilbaum (nicht nur vom Block-Tag selbst), weil Fußnoten-Ziele häufig auf einem inneren <a> statt dem umschließenden <p> sitzen. Progress-Sync ist jetzt bidirektional, ohne Übersetzungsschicht nötig, da beide Seiten dasselbe Anchor-Format nutzen: sync zieht book_progress aus dem Snapshot in den lokalen Store (furthest-wins); der Reader schickt bei offenen server-verknüpften Büchern Updates zurück (force: false, im Hintergrund-Worker). Verschlüsselungs-Key-Beschaffung ergänzt um den Fallback localStorage.getItem(...) falls die Clipboard-API in der Konsole verweigert wird. Getestet: Blockindex-/Fußnoten-Korrektheit gegen reale Bücher (u.a. 3686/3686 aufgelöste Fußnoten bei einem Zizek-Band), Anchor-Mathematik per Unit-Test, vollständiger Pilot-Test (Navigation, Scroll, Kapitelsprung, Fußnoten-Peek, Resize), Performance-Messung über mehrere Buchgrößen inkl. Cache-Effekt (größtes Buch: ~34k Blocks, kalt ~20-30s, warm ~5s), Save/Restore-Round-Trip 5x wiederholt gegen eine Race-Condition beim ersten Post-Load-Scroll, und Progress-Push End-to-End gegen einen echten Dev-Server verifiziert.
2026-08-15 17:59:46 +02:00
## Bücher + Fortschritt vom Server holen (`diora-tui sync`)
Sobald einmal Zugangsdaten gespeichert sind (`~/.config/diora-tui/config.json`, siehe
unten), synct `diora-tui` **automatisch** — einmal leise im Hintergrund beim Start (neue
Bücher + Fortschritt werden nachgeladen, die Bibliotheksliste aktualisiert sich von
selbst) und einmal beim Beenden über `q` (kurzer Moment Verzögerung, bevor die App
tatsächlich schließt). Der explizite Befehl ist für's Ersteinrichten und für
Nicht-interaktive Nutzung (Cron o.ä.):
```bash
diora-tui sync # nutzt gespeicherte Zugangsdaten, sonst interaktive Abfrage
diora-tui sync --server https://diora.creamfresh.xyz --save # einmalig einrichten + speichern
```
Lädt alle EPUBs des Accounts über `GET /api/sync/` + `GET /books/<id>/data/` herunter,
entschlüsselt sie lokal (AES-256-GCM, kompatibel zu `static/js/app.js`) und legt sie als
normale `.epub`-Dateien in `--library` ab (Dateiname `<id> - <Titel>.epub`) — von da an
funktionieren sie wie jedes andere lokale Buch. Bereits heruntergeladene Bücher werden
TUI: durchgehende Leseansicht mit web-kompatiblen Anchors, Fußnoten, bidirektionalem Progress-Sync Reader zeigt Bücher jetzt als eine fortlaufende Ansicht über alle Kapitel statt Kapitel für Kapitel (diora_tui/blocks.py, layout.py, book_view.py, reader_screen.py). blockIndex wird exakt wie app.js' EPUB_BLOCK_SELECTOR gezählt (p/h1-6/li/blockquote/dt/dd/figcaption + kindlose divs), inklusive linear="no"-Spine-Einträgen — app.js filtert die nicht, und Skippen hätte sowohl Fußnoten-Ziele verfehlt als auch alle folgenden Blockindizes gegen den Web-Reader verschoben. innerFraction ist eine zeilenbasierte Näherung (Terminal hat keine Pixel-Geometrie), was funktioniert, weil der Furthest-Wins-Vergleich primär nach blockIndex sortiert. Rendering nutzt Textuals Line-API (ContinuousBookView.render_line) statt eines einzelnen riesigen Static — bei großen Büchern (mehrere reale heruntergeladene Bücher haben zehntausende Blocks) hätte ein Static den Layout/Paint-Pass auf über eine Minute gebracht. Zusätzlich cached diora_tui/cache.py das (Buch, Layout)-Paar pro (Datei, Breite) auf Platte für schnelles Wiederöffnen. Text ist auf 120 Zeichen begrenzt und zentriert. Fußnoten (f-Taste, FootnoteScreen): Erkennung wie app.js' _looksLikeFootnoteLink; Ziel-Auflösung sammelt IDs aus dem ganzen Block-Teilbaum (nicht nur vom Block-Tag selbst), weil Fußnoten-Ziele häufig auf einem inneren <a> statt dem umschließenden <p> sitzen. Progress-Sync ist jetzt bidirektional, ohne Übersetzungsschicht nötig, da beide Seiten dasselbe Anchor-Format nutzen: sync zieht book_progress aus dem Snapshot in den lokalen Store (furthest-wins); der Reader schickt bei offenen server-verknüpften Büchern Updates zurück (force: false, im Hintergrund-Worker). Verschlüsselungs-Key-Beschaffung ergänzt um den Fallback localStorage.getItem(...) falls die Clipboard-API in der Konsole verweigert wird. Getestet: Blockindex-/Fußnoten-Korrektheit gegen reale Bücher (u.a. 3686/3686 aufgelöste Fußnoten bei einem Zizek-Band), Anchor-Mathematik per Unit-Test, vollständiger Pilot-Test (Navigation, Scroll, Kapitelsprung, Fußnoten-Peek, Resize), Performance-Messung über mehrere Buchgrößen inkl. Cache-Effekt (größtes Buch: ~34k Blocks, kalt ~20-30s, warm ~5s), Save/Restore-Round-Trip 5x wiederholt gegen eine Race-Condition beim ersten Post-Load-Scroll, und Progress-Push End-to-End gegen einen echten Dev-Server verifiziert.
2026-08-15 17:59:46 +02:00
beim nächsten Lauf übersprungen (kein erneuter Download). Der Fortschritt aus dem Snapshot
wird dabei ebenfalls übernommen (nur vorwärts, wie lokal auch).
Während ein so heruntergeladenes Buch geöffnet ist (erkennbar am `<id> - `-Dateinamens-
Präfix), schickt der Reader Fortschritts-Updates zusätzlich zurück an den Server
(`POST /books/<id>/progress/`, `force: false` — überschreibt also nie eine weiter
gelesene Position, egal ob die vom Web-Reader oder einem anderen Gerät stammt). Rein
lokale Bücher (ohne dieses Präfix) bleiben unangetastet, kein Netzwerkzugriff.
Dafür nötig, beim ersten Lauf abgefragt (danach optional lokal gespeichert unter
`~/.config/diora-tui/config.json`, `chmod 600`):
- **Server-URL** — z.B. `https://diora.creamfresh.xyz`.
- **API-Token** — diora → Einstellungen (`/accounts/settings/`) → "Personal Access Token".
- **Verschlüsselungs-Key** — der AES-256-Schlüssel, mit dem deine Bücher clientseitig
verschlüsselt wurden. Der `sync`-Prompt bietet zwei Wege:
1. **Aus Benutzername + Passwort ableiten** (Standard) — reproduziert exakt, was diora's
"Unlock with password"-Formular im Browser tut (PBKDF2-HMAC-SHA256, 200.000
Iterationen, Salt `"diora:" + username`; siehe `static/js/app.js:deriveAndStoreKey`).
Funktioniert nur, wenn der Account diesen Weg im Browser mindestens einmal benutzt
hat — sonst wurde der Key ursprünglich zufällig im Browser erzeugt, und diese
Ableitung trifft ihn nicht. `sync` meldet einen falschen Key als
Entschlüsselungsfehler (harmlos, kein Datenverlust), nie als falsches Ergebnis.
2. **Base64-Key direkt einfügen** — für den Fall, dass Weg 1 nicht passt. Der
Export-Button dafür ist im Browser-UI aktuell nicht verdrahtet (`exportEncKey()` in
`app.js` existiert, hat aber keinen sichtbaren Button); bis das nachgezogen ist, in
der Browser-Devtools-Konsole auf der diora-Seite (nicht `/accounts/settings/` — die
lädt `app.js` nicht) ausführen: `await exportEncKey()` — kopiert den Key ins
TUI: durchgehende Leseansicht mit web-kompatiblen Anchors, Fußnoten, bidirektionalem Progress-Sync Reader zeigt Bücher jetzt als eine fortlaufende Ansicht über alle Kapitel statt Kapitel für Kapitel (diora_tui/blocks.py, layout.py, book_view.py, reader_screen.py). blockIndex wird exakt wie app.js' EPUB_BLOCK_SELECTOR gezählt (p/h1-6/li/blockquote/dt/dd/figcaption + kindlose divs), inklusive linear="no"-Spine-Einträgen — app.js filtert die nicht, und Skippen hätte sowohl Fußnoten-Ziele verfehlt als auch alle folgenden Blockindizes gegen den Web-Reader verschoben. innerFraction ist eine zeilenbasierte Näherung (Terminal hat keine Pixel-Geometrie), was funktioniert, weil der Furthest-Wins-Vergleich primär nach blockIndex sortiert. Rendering nutzt Textuals Line-API (ContinuousBookView.render_line) statt eines einzelnen riesigen Static — bei großen Büchern (mehrere reale heruntergeladene Bücher haben zehntausende Blocks) hätte ein Static den Layout/Paint-Pass auf über eine Minute gebracht. Zusätzlich cached diora_tui/cache.py das (Buch, Layout)-Paar pro (Datei, Breite) auf Platte für schnelles Wiederöffnen. Text ist auf 120 Zeichen begrenzt und zentriert. Fußnoten (f-Taste, FootnoteScreen): Erkennung wie app.js' _looksLikeFootnoteLink; Ziel-Auflösung sammelt IDs aus dem ganzen Block-Teilbaum (nicht nur vom Block-Tag selbst), weil Fußnoten-Ziele häufig auf einem inneren <a> statt dem umschließenden <p> sitzen. Progress-Sync ist jetzt bidirektional, ohne Übersetzungsschicht nötig, da beide Seiten dasselbe Anchor-Format nutzen: sync zieht book_progress aus dem Snapshot in den lokalen Store (furthest-wins); der Reader schickt bei offenen server-verknüpften Büchern Updates zurück (force: false, im Hintergrund-Worker). Verschlüsselungs-Key-Beschaffung ergänzt um den Fallback localStorage.getItem(...) falls die Clipboard-API in der Konsole verweigert wird. Getestet: Blockindex-/Fußnoten-Korrektheit gegen reale Bücher (u.a. 3686/3686 aufgelöste Fußnoten bei einem Zizek-Band), Anchor-Mathematik per Unit-Test, vollständiger Pilot-Test (Navigation, Scroll, Kapitelsprung, Fußnoten-Peek, Resize), Performance-Messung über mehrere Buchgrößen inkl. Cache-Effekt (größtes Buch: ~34k Blocks, kalt ~20-30s, warm ~5s), Save/Restore-Round-Trip 5x wiederholt gegen eine Race-Condition beim ersten Post-Load-Scroll, und Progress-Push End-to-End gegen einen echten Dev-Server verifiziert.
2026-08-15 17:59:46 +02:00
Clipboard, von dort ins `sync`-Prompt einfügen. Schlägt das mit einem
`NotAllowedError`/`InvalidAccessError` fehl, stattdessen direkt aus `localStorage`
lesen: `localStorage.getItem('diora_enc_key_' + window.USER_ID)`.
Der Key/Token wird genauso vertrauensvoll behandelt wie im Web-Client (dort liegt der
Schlüssel unverschlüsselt in `localStorage`): lokal als Klartext in einer 0600-Datei.
TUI: durchgehende Leseansicht mit web-kompatiblen Anchors, Fußnoten, bidirektionalem Progress-Sync Reader zeigt Bücher jetzt als eine fortlaufende Ansicht über alle Kapitel statt Kapitel für Kapitel (diora_tui/blocks.py, layout.py, book_view.py, reader_screen.py). blockIndex wird exakt wie app.js' EPUB_BLOCK_SELECTOR gezählt (p/h1-6/li/blockquote/dt/dd/figcaption + kindlose divs), inklusive linear="no"-Spine-Einträgen — app.js filtert die nicht, und Skippen hätte sowohl Fußnoten-Ziele verfehlt als auch alle folgenden Blockindizes gegen den Web-Reader verschoben. innerFraction ist eine zeilenbasierte Näherung (Terminal hat keine Pixel-Geometrie), was funktioniert, weil der Furthest-Wins-Vergleich primär nach blockIndex sortiert. Rendering nutzt Textuals Line-API (ContinuousBookView.render_line) statt eines einzelnen riesigen Static — bei großen Büchern (mehrere reale heruntergeladene Bücher haben zehntausende Blocks) hätte ein Static den Layout/Paint-Pass auf über eine Minute gebracht. Zusätzlich cached diora_tui/cache.py das (Buch, Layout)-Paar pro (Datei, Breite) auf Platte für schnelles Wiederöffnen. Text ist auf 120 Zeichen begrenzt und zentriert. Fußnoten (f-Taste, FootnoteScreen): Erkennung wie app.js' _looksLikeFootnoteLink; Ziel-Auflösung sammelt IDs aus dem ganzen Block-Teilbaum (nicht nur vom Block-Tag selbst), weil Fußnoten-Ziele häufig auf einem inneren <a> statt dem umschließenden <p> sitzen. Progress-Sync ist jetzt bidirektional, ohne Übersetzungsschicht nötig, da beide Seiten dasselbe Anchor-Format nutzen: sync zieht book_progress aus dem Snapshot in den lokalen Store (furthest-wins); der Reader schickt bei offenen server-verknüpften Büchern Updates zurück (force: false, im Hintergrund-Worker). Verschlüsselungs-Key-Beschaffung ergänzt um den Fallback localStorage.getItem(...) falls die Clipboard-API in der Konsole verweigert wird. Getestet: Blockindex-/Fußnoten-Korrektheit gegen reale Bücher (u.a. 3686/3686 aufgelöste Fußnoten bei einem Zizek-Band), Anchor-Mathematik per Unit-Test, vollständiger Pilot-Test (Navigation, Scroll, Kapitelsprung, Fußnoten-Peek, Resize), Performance-Messung über mehrere Buchgrößen inkl. Cache-Effekt (größtes Buch: ~34k Blocks, kalt ~20-30s, warm ~5s), Save/Restore-Round-Trip 5x wiederholt gegen eine Race-Condition beim ersten Post-Load-Scroll, und Progress-Push End-to-End gegen einen echten Dev-Server verifiziert.
2026-08-15 17:59:46 +02:00
## Performance bei großen Büchern
Sehr große Bücher (mehrstellige Tausend Absätze — z.B. Sammelbände) können beim
*ersten* Öffnen mehrere Sekunden bis niedrige zweistellige Sekunden brauchen (Parsing +
Zeilenumbruch-Berechnung für die durchgehende Ansicht). Ein zweites Öffnen desselben
Buchs bei gleicher Terminalbreite ist dank Cache (`~/.cache/diora-tui/layout_cache/`)
deutlich schneller. Das Rendering selbst skaliert nicht mit der Buchgröße — nur die
tatsächlich sichtbaren Zeilen werden gezeichnet (Textual Line API), nicht das ganze Buch
auf einmal.
## Grenzen der aktuellen Version
- Nur EPUB, kein PDF — `sync` lädt PDFs im Account gar nicht erst herunter (übersprungen,
wird gemeldet), da der Reader sie ohnehin nicht darstellen kann.
TUI: durchgehende Leseansicht mit web-kompatiblen Anchors, Fußnoten, bidirektionalem Progress-Sync Reader zeigt Bücher jetzt als eine fortlaufende Ansicht über alle Kapitel statt Kapitel für Kapitel (diora_tui/blocks.py, layout.py, book_view.py, reader_screen.py). blockIndex wird exakt wie app.js' EPUB_BLOCK_SELECTOR gezählt (p/h1-6/li/blockquote/dt/dd/figcaption + kindlose divs), inklusive linear="no"-Spine-Einträgen — app.js filtert die nicht, und Skippen hätte sowohl Fußnoten-Ziele verfehlt als auch alle folgenden Blockindizes gegen den Web-Reader verschoben. innerFraction ist eine zeilenbasierte Näherung (Terminal hat keine Pixel-Geometrie), was funktioniert, weil der Furthest-Wins-Vergleich primär nach blockIndex sortiert. Rendering nutzt Textuals Line-API (ContinuousBookView.render_line) statt eines einzelnen riesigen Static — bei großen Büchern (mehrere reale heruntergeladene Bücher haben zehntausende Blocks) hätte ein Static den Layout/Paint-Pass auf über eine Minute gebracht. Zusätzlich cached diora_tui/cache.py das (Buch, Layout)-Paar pro (Datei, Breite) auf Platte für schnelles Wiederöffnen. Text ist auf 120 Zeichen begrenzt und zentriert. Fußnoten (f-Taste, FootnoteScreen): Erkennung wie app.js' _looksLikeFootnoteLink; Ziel-Auflösung sammelt IDs aus dem ganzen Block-Teilbaum (nicht nur vom Block-Tag selbst), weil Fußnoten-Ziele häufig auf einem inneren <a> statt dem umschließenden <p> sitzen. Progress-Sync ist jetzt bidirektional, ohne Übersetzungsschicht nötig, da beide Seiten dasselbe Anchor-Format nutzen: sync zieht book_progress aus dem Snapshot in den lokalen Store (furthest-wins); der Reader schickt bei offenen server-verknüpften Büchern Updates zurück (force: false, im Hintergrund-Worker). Verschlüsselungs-Key-Beschaffung ergänzt um den Fallback localStorage.getItem(...) falls die Clipboard-API in der Konsole verweigert wird. Getestet: Blockindex-/Fußnoten-Korrektheit gegen reale Bücher (u.a. 3686/3686 aufgelöste Fußnoten bei einem Zizek-Band), Anchor-Mathematik per Unit-Test, vollständiger Pilot-Test (Navigation, Scroll, Kapitelsprung, Fußnoten-Peek, Resize), Performance-Messung über mehrere Buchgrößen inkl. Cache-Effekt (größtes Buch: ~34k Blocks, kalt ~20-30s, warm ~5s), Save/Restore-Round-Trip 5x wiederholt gegen eine Race-Condition beim ersten Post-Load-Scroll, und Progress-Push End-to-End gegen einen echten Dev-Server verifiziert.
2026-08-15 17:59:46 +02:00
- Text wird als Fließtext ohne Bild-/Layout-Rendering dargestellt.
- `innerFraction` (die Position *innerhalb* eines Blocks) ist eine Terminal-Näherung
(zeilenbasiert statt pixelbasiert wie im Browser) — für die Fortschritts-Sortierung
zählt primär `blockIndex`, der exakt mit dem Web-Reader übereinstimmt; `innerFraction`
ist nur ein Tiebreaker innerhalb desselben Blocks.