Every page of a forum repeats its global announcements, announcements and stickies before the first real topic. On a phone that is the whole first screen, scrolled past on every page turn, and the rebuild made it worse: the board separates those blocks with header rows the card stream dropped, so what remained was one undifferentiated run of cards. Each block now carries a header with its name and card count, and tapping it folds the block away. The announcement blocks start folded, which is the point of the change; folding rather than filtering keeps them reachable without editing the script. The state is kept per kind, so a decision taken in one forum holds in all of them. localStorage rather than GM_setValue: no @grant, and identical behaviour in Tampermonkey and the iOS app, against the risk that Safari clears the value after long inactivity and the sections return to their default. That is a reset of one preference, not data loss. Rows are classified by the icon in their first column instead of a title prefix, because the board marks the type nowhere else. The header labels come from the board's own separator rows, so sections nobody anticipated still read correctly. The menu bar links the forum rules and the FAQ as topics. That row has neither a topic title nor numeric columns, but it passed the layout brake and had been yielding a ghost card at the head of every list — visible now that it would claim a section header of its own.
9.5 KiB
CLAUDE.md
Ein Userscript, das die phpBB-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/* |
Beide Ansichten, Weiche nach Seitentyp |
dev/rin-mobile.dev.user.js |
zusaetzlich localhost:* |
Loader, bindet per @require file:/// das echte Script ein |
dev/viewtopic.php |
— | Fixture: gespeicherter Thread, 1114 Seiten |
dev/viewforum.php |
— | Fixture: Main Forum, 610 Seiten, mit Unterforen, Ankuendigungen und Stickies |
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.
Aufbau
Ein IIFE, streng der Reihe nach:
DEBUG-Flag undlog-Array- Weiche:
viewist'thread','liste'oder''— abgeleitet auslocation.pathname + location.search - Nur bei gesetztem
view: Viewport-Meta (idempotent ueber[data-rm]) und der komplette Style-Block als Template-Literal replaceBody(root)blendet alle bisherigenbody-Kinder aus und haengt die neue Ansicht anbuildList()beziehungsweisebuildThread()lesen das Original-DOM und bauen eindiv.rm-root; beide haengen zum Schluss ueberaddCrumbs()die fixierte Brotkrumenleiste anrun()waehlt die Ansicht und haengt beiDEBUGdas gruene Overlay an- Start ueber
DOMContentLoaded, fallsreadyState === '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 globaleimg-Regel - Unterschiede zwischen den Ansichten ueber einen Modifier am Root loesen,
nicht ueber zwei Regeln mit demselben Selektor.
.rm-root.rm-threadhaelt so die 64px hinter der fixierten Leiste frei. - Namen sind ansichtsspezifisch zu halten:
.rm-jump/.rm-pagesgehoeren der Liste,.rm-goto/.rm-griddem Thread-Sheet. .rm-avbraucht!importantauf Breite und Hoehe, sonst gewinnt die globaleimg { 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 und „Mark forums read" verlinken ebenfalls Foren,
stehen aber in Zeilen ohne Forenmerkmale und werden verworfen.
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 (<td class="row3" colspan="6"><b>Stickies</b>) tragen
keinen Link und fallen beim Einsammeln durch; ihr Text ist trotzdem die beste
Beschriftung und wird ueber die Vorgaengerzeilen nachgeschlagen. Achtung beim
Zaehlen im Fixture: dieselben Symbole stehen unten noch einmal in der Legende,
ein Grep zaehlt also mehr, als es Zeilen gibt.
Der Menuebalken der Seite verlinkt Themen. „Forum rules" und die FAQ stehen
in einer Layoutzeile ohne Tabelle oder Script darin, die Bremse in rowKind()
greift dort also nicht. Ohne das Merkmal a.topictitle beziehungsweise zwei
Zahlenspalten wurde daraus eine Geisterkarte am Kopf jeder Liste — mit
Gruppenkoepfen faellt sie als eigener Abschnitt „Themen 1" auf.
Der Gruppenkopf entfaellt hinter einer Kategoriezeile. Sonst stuende auf der Forenwurzel unter jeder Kategorie noch ein „FOREN n" — die Kategorie leitet die Foren darunter bereits ein. Die Karten werden deshalb erst vollstaendig gebaut und dann ausgegeben: die Anzahl im Kopf steht erst fest, wenn feststeht, welche Zeilen ueberhaupt eine Karte hergeben.
Das Seitengeruest ist selbst eine Tabelle. closest('tr') liefert fuer
einen Link ausserhalb einer Listenzeile die Layoutzeile, die die ganze Seite
enthaelt. Ohne Bremse wurde daraus eine Karte mit dem kompletten Seitentext —
Kopfzeile, Stylesheet und Inline-Scripts inklusive — als Forenbeschreibung.
Zeilen mit table, script, style oder form darin sind deshalb keine
Kandidaten, und die Beschreibung wird nur aus den direkten Textknoten der
Titelzelle gebildet, nie aus deren gesamtem Text.
Das Theme ist rinDark, nicht subsilver2. Fuer die Forenliste waren die
Klassennamen aus der phpBB-Dokumentation (forumlink, forumdesc) deshalb
zunaechst nur eine Vermutung; dev/viewforum.php bestaetigt sie inzwischen. Der
Rueckfall auf die Form der Zeile bleibt trotzdem stehen — er kostet nichts und
traegt die Erkennung, wenn das Theme wechselt.
Die Brotkrumenleiste ist fixiert, nicht sticky. .rm-root traegt
overflow:hidden und wuerde damit zum Scrollport einer position:sticky
-Leiste, in dem nie gescrollt wird — sie klebte nie. Fixierte Nachfahren
klammert das overflow dagegen nicht, weil der Root keinen Containing Block
aufspannt. Die Freihaltung oben haengt an body.rm-crumbed, damit ohne
Brotkrumen im Markup kein leerer Streifen bleibt.
Page X of Y steht als Page <strong>1</strong> of <strong>1114</strong>
im Markup. Ein Grep im Roh-HTML nach Page 1 of findet nichts, die Regex auf
txt(document.body) greift trotzdem. Nicht vorschnell fuer kaputt halten.
per_page kommt aus der jumpto()-Funktion der Seite
(var per_page = '15';). Nur damit lassen sich Seiten-URLs als
start = (Seite - 1) * per_page selbst bauen. Die gerenderten Seitenlinks
taugen nicht als Quelle — phpBB zeigt bei 1114 Seiten nur 1, 2, 3, 4, 5 … 1114.
Ohne per_page degradiert die Leiste auf erste Seite plus die
Original-Zurueck/Weiter-Links, das Sheet verschwindet.
postRow() steigt bewusst mehrere tr hoch. Der Beitragstext liegt in
einer verschachtelten Tabelle, closest('tr') trifft die innere Zeile ohne
Autor- und Profilspalte.
DEBUG steht auf true und muss vor dem Ausliefern auf false. Die
Versionsnummer steht doppelt: im @version-Header und im Text des
Debug-Overlays.
Testen
Es gibt keine Testsuite. Bewaehrt hat sich ein Wegwerf-Harness mit jsdom im Scratchpad (nicht im Repo ablegen — das Projekt soll abhaengigkeitsfrei bleiben):
const dom = new JSDOM(html, { url: 'https://cs.rin.ru/forum/viewtopic.php?f=10&t=68689&start=45' });
Drei Stolpersteine dabei:
- JSDOM parst asynchron fertig. Nach
eval(src)rund 60 ms warten, sonst hat das Script noch aufDOMContentLoadedgewartet und nichts gebaut. location.hrefist in JSDOM nicht ueberschreibbar. Fuer Navigationstests im eingelesenen Quelltextlocation.href = u;durch einen Hook ersetzen.requestAnimationFrameundscrollIntoViewfehlen und muessen gestubbt werden.
Sinnvolle Faelle: erste Seite, zweite Seite, Mitte, letzte Seite, dazu die
Degradation ohne per_page und ein Thread ohne Paginierung. Fixture-Eckdaten:
f=10&t=68689, per_page = 15, 1114 Seiten, letzte Seite start=16695.
Fuer die Weiche und die Listenansicht genuegt eine handgeschriebene Tabelle mit
a.topictitle-Zeilen. Zu pruefen ist dabei auch eine Seite ausserhalb der
Weiche wie posting.php: dort darf weder eine Ansicht gebaut noch ein
<style> injiziert werden.
Fuer die Forenliste braucht es eine zweite Tabelle im subsilver2-Schnitt:
td.cat > h4 > a als Kategoriezeile, darunter Zeilen mit a.forumlink, drei
p.forumdesc (Beschreibung, Moderatoren, Unterforen), zwei Zahlenspalten und
einer Spalte „letzter Beitrag" mit Link auf viewtopic.php. Vier Faelle sind
sinnvoll: reine Forenliste, reine Themenliste, beides auf einer Seite, und
dieselbe Forenliste ohne die Klassen forumlink/forumdesc — die muss ueber
die Zahlenspalten dasselbe Ergebnis liefern.
Am Geraet zu pruefen bleibt, was Blink nicht zeigt: env(safe-area-inset-*),
fixierte Leisten gegen Safaris ein- und ausfahrende Toolbar,
-webkit-backdrop-filter, Schriftskalierung. Details und die
Tampermonkey-Einrichtung stehen in der README.
Konventionen
- ES5-Stil im Script-Code:
var,function, keine Arrow Functions, keinlet/const. Template-Literals undSetsind in Gebrauch. Keine Transpilation, kein Linter. - Kommentare deutsch, Umlaute als
ae/oe/uetransliteriert. Echte Umlaute nur in Strings und Regexes, die im UI landen. - Kommentare begruenden, warum etwas so gebaut ist (welche DOM-Eigenheit von phpBB dahintersteckt), nicht was der Code tut.
- CSS-Klassen durchgaengig mit
rm-praefixiert. - Neue nutzersichtbare Funktionen in der README dokumentieren — zweisprachig, siehe globale Anweisung.