Bibliothek neu: Ordner als Filter-Chips überall, Cover-Raster, Cover verschlüsselt und generiert (noch nicht veröffentlicht)
- Ordner wie Tags (docs/library.md): folders.js zeichnet die Chip-Reihe, das
Blatt „In Ordner …“ (Häkchen, Neuer Ordner) und „Ordner verwalten“
(umbenennen = zusammenlegen, löschen, Reihenfolge). E-Books: `folders` im
verschlüsselten Meta, Reihenfolge in verschlüsselten Bibliotheks-Einstellungen
(/books/prefs/). Podcasts/Hörbücher: PodcastFeed.folders (Migration 0021,
übernimmt `folder`), /podcasts/folders/ (+rename, +delete). Blogs: Reihenfolge
(/feeds/folders/order/).
- Bibliothek (53-library.js): Kopf mit Suche, Sortierung, Raster/Liste und +;
Chips Angefangen · Ungelesen · Gelesen · Ordner; eine „Weiterlesen“-Karte;
Mehrfachauswahl (lange drücken). E-Ink und diora Reader beginnen als Liste.
- Cover Ende-zu-Ende verschlüsselt (covers.js, /books/<id>/cover/): aus dem
E-Book (EPUB-Coverbild, PDF Seite 1), 300 px, mit dem Nutzerschlüssel
verschlüsselt, einmal hochgeladen; sparsam im Leerlauf, nie beim Lesen, nie
auf E-Ink.
- Bücher ohne Cover bekommen eins (gencover.js): Stichwörter aus Titel, Autor,
Regal und Ordnern wählen ein Gestaltungskonzept (Konstruktivismus, Geometrie,
Psyche, Wellen, Karte, Kosmos, Ornament, Botanik, Berge, Zellen, Raster,
Schatten, Typo, sonst Bauhaus); ein Hash aus Titel und Autor Palette, Aufbau
und Details – einzigartig, überall gleich, nichts gespeichert.
- Hörbücher: Ordner-Chips statt Kacheln, „In Ordner …“ mit mehreren Ordnern.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 20:44:23 +02:00
|
|
|
# Folders and the library (redesign)
|
|
|
|
|
|
|
|
|
|
## Folders everywhere: tags shown as filter chips
|
|
|
|
|
|
|
|
|
|
One idea of a folder for every area — Bücher, Zeitschriften, Hörbücher,
|
|
|
|
|
Podcasts, Blogs (and the news' Ressorts, which already work this way):
|
|
|
|
|
|
|
|
|
|
- A thing can be in **several folders** (they work like tags); none is fine.
|
|
|
|
|
- Folders are **filters**, not rooms: a row of chips on top —
|
|
|
|
|
`Alle · <folder> n · … · Ohne Ordner` — tapping one filters in place; no
|
|
|
|
|
"inside a folder" view, no back button. On a wide window the row wraps.
|
|
|
|
|
- **Assigning**: mark one or more things (long press / ☐ select mode, or ⋮ on
|
|
|
|
|
one), then "In Ordner …": a sheet with every folder of the area as checkboxes
|
|
|
|
|
and "Neuer Ordner". No typing a name that must match.
|
|
|
|
|
- **Managing** (⋮ on the chip row → "Ordner verwalten"): rename, merge (rename
|
|
|
|
|
onto an existing name), delete (the things stay, only the folder goes),
|
|
|
|
|
reorder by dragging. The order is the chip order.
|
|
|
|
|
- **Automatic groups** stay automatic and look different from folders (no
|
|
|
|
|
chip; a stack/group heading): audiobook series, magazine periodicals.
|
|
|
|
|
|
|
|
|
|
### Where folders are kept
|
|
|
|
|
|
|
|
|
|
| Area | Field | Notes |
|
|
|
|
|
|------|-------|-------|
|
|
|
|
|
| E-books, magazines | encrypted meta `folders: [names]` | `folder` (the first) kept for older clients; read `folders` or else `[folder]` |
|
|
|
|
|
| Audiobooks, podcasts | `PodcastFeed.folders` (JSON list) | feed setting `folders`; `folder` (string) still accepted |
|
|
|
|
|
| Blogs | `Source.folders` (exists) | |
|
|
|
|
|
| News | `Source.category` (one Ressort) | already chips in Nachrichten |
|
|
|
|
|
|
|
|
|
|
The folder order per area: e-books in the encrypted user settings blob (names
|
|
|
|
|
are private), the others on the server (`/podcasts/folders/`, `/feeds/folders/`,
|
|
|
|
|
`{order: [names]}`).
|
|
|
|
|
|
2026-10-19 17:23:06 +02:00
|
|
|
### Podcasts: a category's episodes, by time
|
|
|
|
|
|
|
|
|
|
The folder chips stand over „Folgen“ too: with one chosen it lists every episode of
|
|
|
|
|
that category (`GET /podcasts/episodes/?folder=<name>`, empty: the podcasts in none).
|
|
|
|
|
„Folgen“, „Neu“ and one podcast's page group their episodes by time — Heute, Gestern,
|
|
|
|
|
Diese Woche (the last 7 days), Diesen Monat (30 days), then the months — after the
|
|
|
|
|
Kino feed. In „Folgen“ (sort „Nach Zeitraum, seltene zuerst“, the default) and „Neu“
|
|
|
|
|
the podcasts that seldom come go first within a group: `interval_days` of each feed
|
|
|
|
|
in `/podcasts/state/`, the mean gap between its newest 10 episodes (one episode: its
|
2026-10-19 17:30:38 +02:00
|
|
|
age, at least 30 days, the same when all came within an hour; kept an hour). Heard is a filter chip, not an order.
|
2026-10-19 17:23:06 +02:00
|
|
|
|
Bibliothek neu: Ordner als Filter-Chips überall, Cover-Raster, Cover verschlüsselt und generiert (noch nicht veröffentlicht)
- Ordner wie Tags (docs/library.md): folders.js zeichnet die Chip-Reihe, das
Blatt „In Ordner …“ (Häkchen, Neuer Ordner) und „Ordner verwalten“
(umbenennen = zusammenlegen, löschen, Reihenfolge). E-Books: `folders` im
verschlüsselten Meta, Reihenfolge in verschlüsselten Bibliotheks-Einstellungen
(/books/prefs/). Podcasts/Hörbücher: PodcastFeed.folders (Migration 0021,
übernimmt `folder`), /podcasts/folders/ (+rename, +delete). Blogs: Reihenfolge
(/feeds/folders/order/).
- Bibliothek (53-library.js): Kopf mit Suche, Sortierung, Raster/Liste und +;
Chips Angefangen · Ungelesen · Gelesen · Ordner; eine „Weiterlesen“-Karte;
Mehrfachauswahl (lange drücken). E-Ink und diora Reader beginnen als Liste.
- Cover Ende-zu-Ende verschlüsselt (covers.js, /books/<id>/cover/): aus dem
E-Book (EPUB-Coverbild, PDF Seite 1), 300 px, mit dem Nutzerschlüssel
verschlüsselt, einmal hochgeladen; sparsam im Leerlauf, nie beim Lesen, nie
auf E-Ink.
- Bücher ohne Cover bekommen eins (gencover.js): Stichwörter aus Titel, Autor,
Regal und Ordnern wählen ein Gestaltungskonzept (Konstruktivismus, Geometrie,
Psyche, Wellen, Karte, Kosmos, Ornament, Botanik, Berge, Zellen, Raster,
Schatten, Typo, sonst Bauhaus); ein Hash aus Titel und Autor Palette, Aufbau
und Details – einzigartig, überall gleich, nichts gespeichert.
- Hörbücher: Ordner-Chips statt Kacheln, „In Ordner …“ mit mehreren Ordnern.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 20:44:23 +02:00
|
|
|
### Shared code: `static/js/folders.js` (diora-web, every platform)
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
DioraFolders.chips(el, {folders: [{name, count}], active, none: count|0, onPick(name|''|null), onManage()})
|
|
|
|
|
DioraFolders.assign({title, folders: [names], selected: [names], onDone(names)}) // the sheet
|
|
|
|
|
DioraFolders.manage({folders: [names], onRename(old, new), onDelete(name), onOrder(names)})
|
|
|
|
|
```
|
|
|
|
|
Each area keeps its own storage and calls these for the look and the dialogs.
|
|
|
|
|
|
|
|
|
|
## The library (Bücher, Zeitschriften)
|
|
|
|
|
|
|
|
|
|
- **Header**: title, search (title, author), sort (zuletzt gelesen · Titel ·
|
|
|
|
|
Autor · hinzugefügt), ⊞/☰ grid or list, `+` (Buch hinzufügen, Aus Cloud).
|
|
|
|
|
- **Filter chips**: Alle · Angefangen · Ungelesen · Gelesen — then the folders.
|
|
|
|
|
- **Weiterlesen**: one card on top (cover, title, "Seite 132 von 186 · gestern",
|
|
|
|
|
▶), not repeated in the list below.
|
|
|
|
|
- **Grid** (default on phone and desktop): covers with a progress line, title.
|
|
|
|
|
**List** (default on e-ink / diora Reader): one row per book, small cover,
|
|
|
|
|
title, author, % / ✓.
|
|
|
|
|
|
|
|
|
|
### Covers, end-to-end encrypted
|
|
|
|
|
|
|
|
|
|
A client that has a book's file extracts its cover (EPUB: the OPF cover image;
|
|
|
|
|
PDF: page 1), scales it to 240 px wide JPEG, **encrypts it with the user's key**
|
|
|
|
|
(AES-GCM like the meta) and uploads it once: `POST /books/<id>/cover/
|
|
|
|
|
{cover_ct, cover_iv}`. `GET /books/` lists `has_cover`; `GET /books/<id>/cover/`
|
|
|
|
|
returns `{cover_ct, cover_iv}`. Clients cache the decrypted image locally. The
|
|
|
|
|
server never sees a cover.
|
2026-10-16 22:44:45 +02:00
|
|
|
|
2026-10-19 14:27:14 +02:00
|
|
|
### Editions
|
|
|
|
|
|
|
|
|
|
Two or more books can be **editions of one work** (the PDF scan and the EPUB, two
|
|
|
|
|
translations …): the same random key in each one's encrypted meta, `work: "w…"`
|
|
|
|
|
(`''` or missing = none). The list shows a work once — the edition read last, else an EPUB,
|
|
|
|
|
else the first added — with its formats on the cover ("EPUB · PDF", in the list view after
|
|
|
|
|
the author). Each edition keeps its own file, position and highlights.
|
|
|
|
|
|
|
|
|
|
- Joining: choose several books → "Als Ausgaben verbinden", or a book's ⋮ → "Als Ausgabe
|
|
|
|
|
verbinden …" (other books listed, similar titles first, with a search).
|
|
|
|
|
- ⋮ → "Ausgaben (n) …" lists the editions: open one, "Lösen" sets one apart (the last one
|
|
|
|
|
left is set apart with it), "Weitere Ausgabe verbinden …".
|
|
|
|
|
- For the apps' own book sheets: `window.bookEditions(id)` opens that sheet,
|
|
|
|
|
`window.bookEditionsOf(id)` returns the editions (the book alone if it has none).
|
|
|
|
|
|
2026-10-16 22:44:45 +02:00
|
|
|
### Generated covers
|
|
|
|
|
|
|
|
|
|
A book without a cover (or one set to `cover: 'generated'` in its meta) gets one
|
|
|
|
|
drawn by `static/js/gencover.js`: an SVG, the same on every device, never stored.
|
|
|
|
|
A **concept** (motif, palettes, typeface) is chosen by score:
|
|
|
|
|
|
|
|
|
|
- words in the title (3), the shelf category (2) and the author (2, only at a
|
|
|
|
|
name's start). A stem of five letters or more counts anywhere (German
|
|
|
|
|
compounds), a shorter one at a word's start, one of three letters as a whole
|
|
|
|
|
word; `^` / `$` in the list force a word's start / end.
|
|
|
|
|
- the folders, by name (`FOLDER_THEMES`): the first counts 2.5 — more than an
|
|
|
|
|
author or shelf word, less than a title word — further ones 1.
|
|
|
|
|
|
2026-10-19 14:06:19 +02:00
|
|
|
A work of Marx or Engels (author, or a volume of the MEW series) is always a **blue volume**
|
|
|
|
|
(blau: blue cloth, gold rules and capitals, the spine on the left with the volume number);
|
|
|
|
|
no other book is.
|
|
|
|
|
|
|
|
|
|
Concepts: konstruktiv (Marxist and political titles), geometrie (Philosophie: rings, turning squares,
|
2026-10-16 22:44:45 +02:00
|
|
|
triangle/circle/square), psyche (Psychoanalyse: two ellipses, the Borromean
|
|
|
|
|
knot, a spiral), karte (Geschichte), kosmos (Science-Fiction), ornament
|
|
|
|
|
(Fantasy), raster (Politik, Soziologie), schatten (Krimi: a red line, a lit
|
|
|
|
|
doorway), grusel (Horror), klassiker (patterned paper with a title plate),
|
|
|
|
|
sakral (Religion & Mythologie), spiel (Kinder & Jugend), code (Informatik),
|
|
|
|
|
typo, wellen, botanik, berge, zellen — and bauhaus when nothing matches. A hash
|
|
|
|
|
of title and author picks palette, layout and every detail.
|
Zeitschriften: das Heft und seine Artikel
Das Heft bleibt das Dokument, der Artikel die Einheit, in der man sich
bewegt (docs/library.md):
- Artikel-Index auf dem Gerät aus dem Inhaltsverzeichnis des EPUB bzw. den
Lesezeichen des PDF (Rubriken, Anreißer, Lesezeit), verschlüsselt im Meta.
- Stand pro Artikel (gelesen / übersprungen, wo man war), verschlüsselt im
Meta, offline gepuffert.
- Bibliothek: Segmentbalken pro Heft, „7 von 23 gelesen“; ein Tipp öffnet den
Heft-Inhalt mit Rubriken, Stand je Artikel, „Im Heft weiterlesen“, „Heft
abschließen“.
- Reader: Kopfzeile „Artikel 8 von 23“, am Ende jedes Artikels eine Zäsur
(gelesen, Weiter im Heft, Zum Inhalt, überspringen); bei PDF eine Leiste
beim Weiterblättern. Durchgelesene Artikel zählen als gelesen, Sprünge nicht.
sw.js: diora-v127.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 11:14:28 +02:00
|
|
|
|
|
|
|
|
## Zeitschriften: das Heft und seine Artikel
|
|
|
|
|
|
|
|
|
|
An issue is a unit (the editors' selection and order, its date, its theme) and
|
|
|
|
|
so is each article in it. Neither is reduced to the other
|
|
|
|
|
(`static/js/app/54-issues.js`):
|
|
|
|
|
|
|
|
|
|
- **The issue stays the document.** It opens and reads through as before, in
|
|
|
|
|
the editors' order. The reader marks the article one is in: a running head
|
|
|
|
|
under the bar ("Schwerpunkt · Artikel 8 von 23 · Titel"), in the apps' footer
|
|
|
|
|
"Artikel 8/23", and a **pause at each article's end** (`aside.iss-end`):
|
|
|
|
|
"✓ gelesen", the issue's count, "Weiter im Heft" (the next article not read
|
|
|
|
|
yet, skipping those read or skipped), "Zum Inhalt", "überspringen",
|
|
|
|
|
"Doch nicht gelesen". In a PDF the pause is a bar that comes up when one
|
|
|
|
|
turns from an article's last page onto the next.
|
|
|
|
|
- **The states determine each other.** An article one was in and whose end one
|
|
|
|
|
reached counts as read (a jump — from the contents, "Weiter" — reads
|
|
|
|
|
nothing). The issue counts "7 von 23 gelesen · 2 übersprungen"; "Im Heft
|
|
|
|
|
weiterlesen" goes to the article one was in, else the next one open.
|
|
|
|
|
"Heft abschließen" marks the rest as skipped, never as read.
|
|
|
|
|
- **In the library** an issue shows one bar with a segment per article (read,
|
|
|
|
|
skipped, open, the one one is in). A tap opens its contents: Rubriken, the
|
|
|
|
|
articles with their state, a teaser and the reading time; the ⋮ / mark
|
|
|
|
|
sets read / unread / skipped by hand.
|
|
|
|
|
|
2026-10-17 11:48:44 +02:00
|
|
|
**The index** comes from the file, on the device:
|
|
|
|
|
|
|
|
|
|
- **EPUB**: the table of contents, by file — an article is a file (the first
|
|
|
|
|
entry pointing into it); the entries after it into the same file are its
|
|
|
|
|
sections, whatever their depth (GegenStandpunkt puts some at the top level);
|
|
|
|
|
an entry whose children point into other files is a Rubrik. A one-file EPUB:
|
|
|
|
|
the leaves. No table of contents: each spine file with text. The teaser is
|
|
|
|
|
the headline under the title (the headings before the first paragraph that
|
|
|
|
|
the title doesn't say, not numbered sections), else the first paragraph.
|
|
|
|
|
- **PDF**: a real outline (word processors' `_Hlk…`/`_Toc…` leftovers don't
|
|
|
|
|
count); most magazines' PDFs have none, so mostly **the contents page
|
|
|
|
|
printed in the issue**: among the first 16 pages the one with most numbered
|
|
|
|
|
rows (numbers that merely count 1, 2, 3 … are a list, not a contents), plus
|
|
|
|
|
its neighbours with entries. A row: the page number at the start
|
|
|
|
|
("14 Der Krieg gegen Palästina", Jacobin), or at the end, maybe with the
|
|
|
|
|
author in a column before the title ("Nathan Sperber | Beyond Neoliberalism?
|
|
|
|
|
| 23", NLR); lines under it in its type size continue the title, an author
|
|
|
|
|
line ("Von …", a name) gives the author, lines in other type the teaser; a
|
|
|
|
|
title in capitals may begin on the lines above its number, a lone word on top
|
|
|
|
|
is its kicker (LuXemburg: GESPRÄCH, INFOGRAFIK); "Name: Titel"
|
|
|
|
|
(manuskripte). Capitals in the contents' type are Rubriken. The printed
|
|
|
|
|
numbers are mapped to the file's pages by the offset most titles agree on
|
|
|
|
|
(found in the page's text, a headline in capitals preferred); running heads
|
|
|
|
|
and the contents page's own head are dropped.
|
|
|
|
|
|
|
|
|
|
Reading times count 200 words a minute. It is kept in the encrypted meta as
|
|
|
|
|
`issue: {v: 2, a: [{h, t, r, x, m, a?, p?, e?}]}` (href, title, Rubrik, teaser,
|
|
|
|
|
minutes, author, a PDF's first and last page), or `{v: 2, a: [], none: true}`
|
|
|
|
|
for an issue without a usable table of contents (read as a whole). An index of
|
|
|
|
|
another version is built anew. Built in the
|
Zeitschriften: das Heft und seine Artikel
Das Heft bleibt das Dokument, der Artikel die Einheit, in der man sich
bewegt (docs/library.md):
- Artikel-Index auf dem Gerät aus dem Inhaltsverzeichnis des EPUB bzw. den
Lesezeichen des PDF (Rubriken, Anreißer, Lesezeit), verschlüsselt im Meta.
- Stand pro Artikel (gelesen / übersprungen, wo man war), verschlüsselt im
Meta, offline gepuffert.
- Bibliothek: Segmentbalken pro Heft, „7 von 23 gelesen“; ein Tipp öffnet den
Heft-Inhalt mit Rubriken, Stand je Artikel, „Im Heft weiterlesen“, „Heft
abschließen“.
- Reader: Kopfzeile „Artikel 8 von 23“, am Ende jedes Artikels eine Zäsur
(gelesen, Weiter im Heft, Zum Inhalt, überspringen); bei PDF eine Leiste
beim Weiterblättern. Durchgelesene Artikel zählen als gelesen, Sprünge nicht.
sw.js: diora-v127.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 11:14:28 +02:00
|
|
|
background for issues without one (not on e-ink: there when the issue is
|
|
|
|
|
opened, or it comes from another device).
|
|
|
|
|
|
|
|
|
|
**The states** are in the meta too: `issueRead: {href: 'r' | 's'}`,
|
|
|
|
|
`issueAt: href` (the article one was in). A change goes to the server after
|
|
|
|
|
2.5 s; offline it waits in `localStorage.diora_issue_pending` and goes with the
|
|
|
|
|
next save (or when the connection is back).
|