# CLAUDE.md Ein Userscript, das die phpBB3-Seiten von `cs.rin.ru` fuer Safari auf iOS in eine mobile Ansicht umbaut. Kein Build, keine Abhaengigkeiten, kein `package.json` — die `.user.js`-Datei ist das Produkt. | Datei | Greift auf | Zweck | |---|---|---| | `rin-mobile.user.js` | `*://cs.rin.ru/forum/*` | Alle Ansichten, Weiche nach Seitentyp | | `dev/rin-mobile.dev.user.js` | `*://cs.rin.ru/forum/*` | Loader, bindet per `@require file:///` das echte Script ein | | `dev/viewtopic.php` | — | Fixture: gespeicherter Thread, `per_page=15`, 1114 Seiten | | `dev/viewforum.php` | — | Fixture: Main Forum, `per_page=100`, 610 Seiten, mit Unterforen, Ankuendigungen und Stickies | | `dev/index.php` | — | Fixture: Forumwurzel, zwei Kategorien mit Forenzeilen, keine Paginierung, eingeloggt aufgenommen | | `dev/ucp.php` | — | Fixture: Login-Formular (`mode=login`), nur ausgeloggter Zustand | | `dev/search.php` | — | Fixture: Suchformular ohne Treffer, eingeloggt aufgenommen | | `dev/search2.php` | — | Fixture: Suchtreffer im Modus "Posts" (15 von 1438 Treffern, `t=63042`, `per_page=15`, 96 Seiten) | Die Fixtures sind ueber `.gitignore` (`dev/*.php`) ausgeschlossen. Nach einem frischen Klon sind sie nicht da und muessen nach der Anleitung in der README neu gespeichert werden — die Seite verlangt einen Login. Der Loader greift ausschliesslich auf die echte Seite. Die Fixtures sind Markup-Vorlagen fuer das jsdom-Harness und zum Nachlesen, keine Seiten zum Anschauen im Browser — die Layoutarbeit laeuft in Chrome auf `cs.rin.ru` selbst. Sie lokal auszuliefern verlangte `@include`-Regeln fuer `localhost`, und die passen auf Seitennamen, die dort haeufig genug vorkommen (`index.php`, alles mit `search` darin), dass der globale Teil des Stylesheets (`html, body`, `img`, jeweils `!important`) in fremde lokale Projekte geriete. Das Nachziehen waere billig, der Fehlersuchweg dorthin nicht. Fuer den Zustand nach erfolgreichem Login (`ucp.php?mode=login` ohne Formular, "Information"-Vorlage) liegt derzeit keine Fixture im Repo. `buildLoginInfo()` wurde gegen einen Mitschnitt gebaut, der nicht mehr vorhanden ist; wer den Zweig anfassen will, muss die Seite neu aufnehmen. ## Aufbau Ein IIFE, streng der Reihe nach: 1. `DEBUG`-Flag und `log`-Array 2. Weiche: `view` ist `'thread'`, `'liste'`, `'login'` oder `''` — abgeleitet aus `location.pathname + location.search` 3. Nur bei gesetztem `view`: Viewport-Meta (idempotent ueber `[data-rm]`) und der komplette Style-Block als Template-Literal 4. `replaceBody(root)` blendet alle bisherigen `body`-Kinder aus und haengt die neue Ansicht an 5. `pageState()`, `pageSheet()` und `pageNav()` — die Paginierung, von Liste und Thread benutzt, nicht vom Login 6. `buildList()`, `buildThread()` beziehungsweise `buildLogin()` lesen das Original-DOM und bauen ein `div.rm-root`; alle haengen zum Schluss ueber `addCrumbs()` die fixierte Brotkrumenleiste an, die zugleich ueber `loginState()` die Login/Logout-Pille traegt. Zwei der drei Ansichten verzweigen intern weiter, weil sich mehrere Seitenformen dieselbe URL teilen: - `buildList()` versucht zuerst `buildSearchForm()` (grosses Suchformular auf `search.php`), dann `buildSearchResults()` (Trefferliste im Modus "Posts", Karten ueber `buildHit()`); erst wenn beide `false` liefern, laeuft das gewoehnliche Zeileneinsammeln - `buildLogin()` faellt ohne Formular auf `buildLoginInfo()` zurueck ("Information"-Vorlage nach dem Absenden) - `buildThread()` legt ueber `buildThreadSearch()` die zweite Leiste an, die die Lupe in den Brotkrumen per `toggleThreadSearch()` ein- und ausblendet 7. `run()` waehlt die Ansicht und haengt bei `DEBUG` das gruene Overlay an 8. Start ueber `DOMContentLoaded`, falls `readyState === 'loading'` Auf Seiten ausserhalb der Weiche (`posting.php`, Profile, …) passiert nichts, insbesondere wird kein Stylesheet injiziert. ## Fallen, die nicht aus dem Code hervorgehen **Beide Ansichten teilen sich einen Klassennamensraum.** Das war der Grund fuer die Zusammenlegung: als getrennte Scripts matchte `rin-mobile` auch `viewtopic.php` und injizierte dort sein Stylesheet, obwohl es nichts baute. Gleich benannte Regeln kollidierten mit identischer Spezifitaet, und die Ladereihenfolge der Userscripts entschied ueber das Layout. - Geteilt: `.rm-root`, `.rm-hidden`, `.rm-dbg`, die globale `img`-Regel - Geteilt sind ausserdem die Leiste und ihr Sheet: `.rm-nav`, `.rm-sheet*`, `.rm-goto`, `.rm-grid` - Unterschiede zwischen den Ansichten ueber einen Modifier am Root loesen, nicht ueber zwei Regeln mit demselben Selektor. `.rm-root.rm-paged` haelt so die 64px hinter der fixierten Leiste frei — gesetzt von `pageNav()` selbst, weil es die Leiste nicht auf jeder Liste gibt. - Namen sind ansichtsspezifisch zu halten: `.rm-jump` und `.rm-group` gehoeren der Liste, `.rm-post` und `.rm-sig` dem Thread. - `.rm-av` braucht `!important` auf Breite und Hoehe, sonst gewinnt die globale `img { height:auto !important }`-Regel gegen die Avatarmasse. **Was nach `body` soll, muss nach `replaceBody()` angehaengt werden.** Die Funktion markiert *alle* vorhandenen `body`-Kinder mit `.rm-hidden`. Das Sheet wird deshalb erst beim ersten Antippen gebaut — das loest das Problem nebenbei mit. **In der Liste entscheidet die Dokumentreihenfolge ueber den Zeilentyp.** `buildList()` sammelt Links auf `viewforum.php` und `viewtopic.php` in einem Rutsch ein und ordnet jede `tr` nach ihrem ersten verwertbaren Link zu. Eine Forenzeile verlinkt in der Spalte „letzter Beitrag" ebenfalls `viewtopic.php` — zaehlte man Themenlinks zuerst, faellt die Zeile als Thema an, der Forenname verschwindet und nur die Beschreibung bleibt als `.rm-last` uebrig. Genau so war es vor 3.2. Brotkrumen verlinken ebenfalls Foren, stehen aber in Zeilen ohne Forenmerkmale und werden verworfen. **„Mark topics read" braucht eine eigene Bremse, „Mark forums read" nicht.** Beide sind reine Aktionslinks, keine Inhaltslinks, aber nur „Mark forums read" zeigt auf `index.php` und faellt schon beim Einsammeln durch, weil das keine der gesuchten Adressen ist. „Mark topics read" oberhalb der Themenliste zeigt dagegen mit `?mark=topics` auf dieselbe `viewforum.php`-Seite und passt damit auf den Sammel-Selektor. Sein Link liegt zwar in einer eigenen inneren `tr` ohne verschachtelte Tabelle, `a.closest('td.cat')` findet aber trotzdem die aeussere Kategoriezelle drumherum — bis 3.7 wurde daraus eine dauerhafte, nicht einklappbare `.rm-sec`-Karte oberhalb von Global Announcements statt gar keiner. `rowKind()` verwirft seit 3.8 jeden Link mit `?mark=` oder `&mark=` in der Adresse, bevor die uebrige Klassifizierung ueberhaupt beginnt. Das Repository hat keine eingeloggte `viewforum.php`-Fixture, die diesen Link mit echtem Markup zeigt — geprueft ist die Bremse nur gegen den vermuteten `mark=topics`-Parameter, per Texteinfuegung in `dev/viewforum.php`. **Der Typ einer Themenzeile steht im Symbol, nicht am Titel.** `rinDark` unterscheidet `global_read.png`, `announce_read.png`, `sticky_read.png` und `topic_read.png` in der ersten Spalte, jeweils auch als `_locked`-Variante. Die Trennzeilen der Seite (`