The script previously had no way to log in or tell whether a session was already active, since ucp.php fell outside the view switch and rendered in the desktop layout. Session state is only visible in the forum's own menu bar link (mode=login vs mode=logout), so every view now surfaces it as a pill in the breadcrumb bar instead of leaving the user to dig for it. The login form itself is moved into the new layout rather than rebuilt from scratch, keeping the hidden sid/redirect fields and the autologin/viewonline checkboxes intact without the script having to reimplement the session handshake. As the first short, unscrollable view in the project, it also exposed a WebKit quirk that longer views never triggered: without enough scroll headroom, Safari's own partially-retracted toolbar has nothing to scroll away and permanently covers the submit button. The generous bottom padding on the form works around that, separately from the safe-area inset that only accounts for the home indicator.
iOS Safari Userscripts — cs.rin.ru
English
A single userscript that rebuilds the phpBB2 pages of cs.rin.ru into a mobile
layout for Safari on iOS. It replaces the original table layout entirely: the
existing page content is hidden and a freshly built DOM is appended to <body>.
rin-mobile.user.js matches *://cs.rin.ru/forum/* and picks its view from the
page type:
| Page | View |
|---|---|
viewforum.php, search.php, forum root |
Topic and forum list as cards |
viewtopic.php |
Thread as a post stream with a fixed bottom navigation bar |
ucp.php?mode=login |
Login form, restyled in place |
| anything else | Untouched — no styles are injected |
A viewforum.php page is not necessarily a list of topics: a category page
lists forums instead, and a forum page can carry both. Each table row is
therefore classified by its first usable link. Forum rows become cards with
name, description, subforum links and counts; topic rows keep their existing
card; category headers become section headings. Rows are recognised by phpBB's
own forumlink / forumdesc classes, and failing that by their shape — two
purely numeric columns are only ever a forum's topic and post count.
Forums, global announcements, announcements, stickies and topics all end up in
the same stream of cards, which loses the separation the original page draws
between them. A header strip naming the section and its card count is therefore
inserted wherever the kind changes, and forum cards additionally carry a
coloured left edge. The label is taken from the board's own separator row
(Global Announcements, Stickies, Topics), so sections this script does not
know are named correctly too. Which kind a topic row is comes from the icon in
its first column, not from its title. The strip is left out directly below a
category heading, which already introduces the forums under it.
Tapping a header collapses its section. Global announcements, announcements and
stickies start collapsed: they precede the topics on every page of a forum and
otherwise fill the first screen. The state is stored per kind in localStorage,
so collapsing stickies once applies to every forum. localStorage is used
rather than GM_setValue because it needs no @grant and behaves the same in
Tampermonkey and in the iOS app; the trade-off is that Safari may clear
script-writable storage after long inactivity, which resets the sections to
their default.
Both views share the same fixed navigation bar at the bottom: first / previous /
next / last and a page indicator. Tapping the indicator opens a sheet with a
number field and a grid of all pages. Targets are computed as
start = (page - 1) × per_page rather than taken from the rendered page links,
because phpBB only ever shows a handful of them. per_page is read from the
jumpto() function the forum embeds in the page; without it, only first page
and the original previous/next links remain available. In the list the bar is
built only where there is more than one page — forum pages run to hundreds of
them, category pages to one.
Both views carry a fixed breadcrumb bar at the top, taken from the forum's own
Board index » … » Main Forum trail. Without it there is no way back out of a
thread, because the original page is hidden and phpBB links the parent forum
nowhere else. The bar scrolls horizontally if the trail is too long for the
screen, and is left out entirely on pages that do not carry a trail. Note that
Board index leads to index.php, which the script does not rebuild — that
page arrives in its desktop layout.
The breadcrumb bar also carries a login pill, built on every page the script
touches, not only the login form itself. Session state is read from the
forum's own menu bar: a mode=login link means logged out, a mode=logout
link means logged in, and the pill switches between a "Login" link and the
account name accordingly. The name is parsed from the link text with a
Logout [ Name ] pattern; that pattern is unverified against a real logged-in
session, since the repository's fixtures are all captured while logged out —
if the pattern does not match, the pill still shows "Logout", just without a
name, rather than breaking.
The login form itself is not rebuilt from scratch: the original <form>
element is moved into the new layout and restyled, table rows collapsed to a
vertical stack. Reusing the element rather than reconstructing it keeps the
hidden sid and redirect fields, the autologin / viewonline checkboxes
and the plain server-side POST intact — the script never talks to the forum's
session handling directly, it only changes how the existing form looks.
The script runs at document-start, injects a mobile viewport meta tag, and
carries a DEBUG flag. While DEBUG is true, a green diagnostic overlay is
prepended to the page showing which view was chosen and how many rows were
converted. Set it to false before daily use.
Installation on iOS
- Install a userscript manager for Safari on iOS (for example Userscripts) and enable it under Settings → Safari → Extensions.
- Add
rin-mobile.user.jsto the manager's script directory. - Grant the extension permission for
cs.rin.ru.
Development
The script is developed on Windows in Chrome or Edge with Tampermonkey.
dev/rin-mobile.dev.user.js is a loader: it contains nothing but a metadata
block that pulls in the real script from disk via @require. Editing
rin-mobile.user.js and reloading the page is enough — no reinstall in
Tampermonkey.
⚠️ Path adjustment required
The loader in dev/ references the script by absolute path:
// @require file:///C:/Projekte/ios_safari_userscripts/rin-mobile.user.js
This path is hardcoded and only valid on the machine it was written for. After
cloning the repository or moving the project folder, adjust the @require line
in dev/rin-mobile.dev.user.js to your own location.
Use forward slashes and the file:/// prefix, including the drive letter —
file:///D:/projects/ios_safari_userscripts/rin-mobile.user.js. A @require
that cannot be resolved causes Tampermonkey to skip the script entirely and
without an error message on the page, which looks exactly like the script not
being installed at all.
Tampermonkey setup
Three settings are required; missing the first one is the most common cause of "nothing happens":
chrome://extensions→ Tampermonkey → enable Allow user scripts. On older Chrome versions, enable Developer mode at the top right instead. Without this, Manifest V3 blocks all injection.- Same page → enable Allow access to file URLs, so
@require file://can be resolved. - Tampermonkey dashboard → Settings → Externals → set the update interval to always, otherwise the loader keeps serving a cached copy of the script.
Then open the loader once via file:///.../dev/rin-mobile.dev.user.js —
Tampermonkey intercepts .user.js URLs and offers to install it.
Do not run the loader and the plain script side by side. If both are enabled in Tampermonkey, the code executes twice on the same page, producing duplicate cards and two debug overlays.
Test fixtures
cs.rin.ru requires a login, so save the pages you need once and iterate
against local copies:
- Open the page while logged in, then in DevTools right-click
<html>→ Copy outerHTML. - Save it as
dev/viewtopic.php.htmlordev/viewforum.php.html. - Serve the folder:
python -m http.server 8777 --directory .
The file name matters twice over. The .html extension makes the server send
the file as HTML instead of offering it as a download, and the retained
viewtopic.php / viewforum.php part is what the loader's @include rules
and the page-type check in rin-mobile.user.js match against.
Forum images and stylesheets are missing from such a snapshot. That is fine for
layout work, because the script rebuilds the DOM anyway — only avatars stay
empty, since their src is carried over unchanged.
Note that a snapshot saved while logged out contains span.link_removed
placeholders instead of links, and may use different markup for author names
than the logged-in view.
Device emulation
DevTools → Device Toolbar (Ctrl+Shift+M) → Edit… → add a custom device
with 430 × 932 and DPR 3 for an iPhone 15 Pro Max, device type Mobile,
and an iOS Safari user agent string.
What desktop emulation cannot show
Chrome uses Blink, iOS Safari uses WebKit. These points have to be verified on the device:
env(safe-area-inset-bottom)in.rm-nav— always0in the emulator, so the clearance for the home indicator is untested.- A bottom
position: fixedbar together with Safari's collapsing toolbar. width: 100vw— on the desktop the scrollbar counts towards it and creates phantom overflow, so the debug overlay reports different numbers than iOS.-webkit-text-size-adjustand Safari's automatic font scaling.-webkit-backdrop-filterrenders in Chrome, but looks different.
For higher fidelity without an iPhone, Playwright ships a real WebKit build for
Windows (npx playwright install webkit).
Troubleshooting
Install dev/tm-test.user.js — it injects a red bar and nothing else. If the
bar does not appear, the problem is Tampermonkey (see setup above), not the
script code. If it does appear, check the loader's Externals tab in the
Tampermonkey dashboard: an empty or failed entry means the @require path is
wrong.
If Tampermonkey keeps serving a stale copy despite the update interval, replace
the file:// require with a local HTTP server, which does not cache:
// @require http://localhost:8777/rin-mobile.user.js
Repository layout
rin-mobile.user.js topic list → cards, thread → post stream
dev/
rin-mobile.dev.user.js loader, @require with absolute path
Deutsch
Ein Userscript, das die phpBB2-Seiten von cs.rin.ru für Safari auf iOS in eine
mobile Ansicht umbaut. Es ersetzt das ursprüngliche Tabellenlayout vollständig:
der vorhandene Seiteninhalt wird ausgeblendet und ein neu gebautes DOM an
<body> gehängt.
rin-mobile.user.js greift auf *://cs.rin.ru/forum/* und wählt die Ansicht
anhand des Seitentyps:
| Seite | Ansicht |
|---|---|
viewforum.php, search.php, Forumwurzel |
Themen- und Forenliste als Karten |
viewtopic.php |
Thread als Beitragsstrom mit fixierter Navigationsleiste |
ucp.php?mode=login |
Login-Formular, an Ort und Stelle umgestylt |
| alles andere | Unverändert — es werden keine Stile injiziert |
Eine viewforum.php-Seite ist nicht zwangsläufig eine Themenliste: eine
Kategorieseite listet Foren auf, eine Forenseite kann beides enthalten. Jede
Tabellenzeile wird deshalb anhand ihres ersten verwertbaren Links eingeordnet.
Forenzeilen werden zu Karten mit Name, Beschreibung, Unterforen und Zahlen,
Themenzeilen behalten ihre bisherige Karte, Kategoriezeilen werden zu
Zwischenüberschriften. Erkannt werden die Zeilen an phpBBs eigenen Klassen
forumlink / forumdesc und ersatzweise an ihrer Form — zwei rein numerische
Spalten hat nur eine Forenzeile mit Themen- und Beitragszahl.
Foren, globale Ankündigungen, Ankündigungen, Stickies und Themen landen alle im
selben Kartenstrom, womit die Trennung verlorengeht, die die Originalseite
zwischen ihnen zieht. Bei jedem Wechsel wird deshalb ein Gruppenkopf mit dem
Namen des Abschnitts und der Zahl seiner Karten eingezogen; Forenkarten tragen
zusätzlich eine farbige linke Kante. Die Beschriftung stammt aus der Trennzeile
der Seite selbst (Global Announcements, Stickies, Topics), damit auch
Abschnitte richtig heißen, die dieses Script nicht kennt. Welcher Art eine
Themenzeile ist, verrät das Symbol ihrer ersten Spalte, nicht der Titel. Direkt
unter einer Kategorieüberschrift entfällt der Gruppenkopf, weil die Überschrift
die Foren darunter schon einleitet.
Ein Tipp auf den Kopf klappt seinen Abschnitt zu. Globale Ankündigungen,
Ankündigungen und Stickies starten zugeklappt: sie stehen auf jeder Seite
eines Forums vor den Themen und füllen sonst den ersten Bildschirm. Der Zustand
liegt je Art in localStorage, einmal zugeklappte Stickies gelten also in jedem
Forum. localStorage statt GM_setValue, weil es ohne @grant auskommt und
sich in Tampermonkey wie in der iOS-App gleich verhält; dafür kann Safari
schreibbaren Speicher nach längerer Nichtnutzung räumen, dann stehen die
Abschnitte wieder auf ihrer Voreinstellung.
Beide Ansichten teilen sich dieselbe fixierte Navigationsleiste am unteren
Rand: erste / vorherige / nächste / letzte Seite und eine Positionsanzeige. Ein
Tipp darauf öffnet ein Sheet mit Zahlenfeld und einem Raster aller Seiten. Die
Ziele werden als start = (Seite - 1) × per_page berechnet und nicht aus den
gerenderten Seitenlinks übernommen, weil phpBB davon immer nur eine Handvoll
anzeigt. per_page stammt aus der jumpto()-Funktion, die das Forum in die
Seite einbettet; fehlt sie, bleiben nur die erste Seite und die ursprünglichen
Zurück-/Weiter-Links nutzbar. In der Liste entsteht die Leiste nur, wo es mehr
als eine Seite gibt — Forenseiten haben hunderte, Kategorieseiten eine.
Beide Ansichten haben oben eine fixierte Brotkrumenleiste, übernommen aus dem
Board index » … » Main Forum-Pfad des Forums. Ohne sie führt aus einem Thread
kein Weg zurück, weil die Originalseite ausgeblendet ist und phpBB das
Elternforum nirgends sonst verlinkt. Die Leiste lässt sich waagerecht schieben,
wenn der Pfad breiter ist als der Bildschirm, und entfällt auf Seiten ohne
Pfadangabe. Board index führt auf index.php — diese Seite baut das Script
nicht um, sie erscheint im Desktop-Layout.
Die Brotkrumenleiste trägt außerdem eine Login-Pille, gebaut auf jeder Seite,
die das Script anfasst — nicht nur auf dem Login-Formular selbst. Der
Sitzungszustand wird aus dem Menübalken der Originalseite gelesen: ein
mode=login-Link bedeutet ausgeloggt, ein mode=logout-Link eingeloggt, die
Pille wechselt entsprechend zwischen einem „Login"-Link und dem Kontonamen.
Der Name wird über das Muster Logout [ Name ] aus dem Linktext gelesen; das
Muster ist gegen keine echte eingeloggte Sitzung geprüft, da alle Fixtures des
Repositories ausgeloggt aufgenommen wurden — greift es nicht, bleibt die
Pille bei „Logout" stehen, nur ohne Namen, statt zu reißen.
Das Login-Formular selbst wird nicht neu gebaut: das Original-<form>-Element
wandert unverändert in das neue Layout und wird umgestylt, Tabellenzeilen
werden zu einer senkrechten Abfolge zusammengeklappt. Die Wiederverwendung
statt Neubau erhält die versteckten Felder sid und redirect, die
Checkboxen autologin / viewonline und den schlichten serverseitigen POST —
das Script spricht nie selbst mit der Sitzungsverwaltung des Forums, es ändert
nur, wie das vorhandene Formular aussieht.
Das Script läuft bei document-start, setzt ein mobiles Viewport-Meta-Tag und
hat ein DEBUG-Flag. Solange DEBUG auf true steht, wird ein grünes
Diagnose-Overlay über die Seite gelegt, das zeigt, welche Ansicht gewählt wurde
und wie viele Zeilen umgewandelt wurden. Für den täglichen Gebrauch auf false
setzen.
Installation unter iOS
- Einen Userscript-Manager für Safari unter iOS installieren (zum Beispiel Userscripts) und unter Einstellungen → Safari → Erweiterungen aktivieren.
rin-mobile.user.jsin das Script-Verzeichnis des Managers legen.- Der Erweiterung die Berechtigung für
cs.rin.ruerteilen.
Entwicklung
Entwickelt wird unter Windows in Chrome oder Edge mit Tampermonkey.
dev/rin-mobile.dev.user.js ist ein Loader: er enthält nur einen
Metadatenblock, der das eigentliche Script per @require von der Platte
einbindet. Es genügt, rin-mobile.user.js zu bearbeiten und die Seite neu zu
laden — keine Neuinstallation in Tampermonkey nötig.
⚠️ Pfadanpassung erforderlich
Der Loader in dev/ verweist mit einem absoluten Pfad auf das Script:
// @require file:///C:/Projekte/ios_safari_userscripts/rin-mobile.user.js
Dieser Pfad ist fest eingetragen und gilt nur auf dem Rechner, für den er
geschrieben wurde. Nach dem Klonen des Repositories oder dem Verschieben des
Projektordners muss die @require-Zeile in dev/rin-mobile.dev.user.js auf den
eigenen Ablageort angepasst werden.
Dabei Schrägstriche und das Präfix file:/// inklusive Laufwerksbuchstaben
verwenden — file:///D:/projekte/ios_safari_userscripts/rin-mobile.user.js.
Ein @require, das nicht aufgelöst werden kann, führt dazu, dass Tampermonkey
das Script komplett und ohne Fehlermeldung auf der Seite überspringt. Das
sieht exakt so aus, als wäre gar kein Script installiert.
Tampermonkey einrichten
Drei Einstellungen sind nötig; die erste ist die mit Abstand häufigste Ursache für „es passiert überhaupt nichts":
chrome://extensions→ Tampermonkey → Nutzerskripte zulassen aktivieren. Bei älteren Chrome-Versionen stattdessen den Entwicklermodus oben rechts einschalten. Ohne das blockiert Manifest V3 jede Injektion.- Auf derselben Seite → Zugriff auf Datei-URLs zulassen aktivieren, damit
@require file://aufgelöst werden kann. - Tampermonkey-Dashboard → Einstellungen → Externals → Update-Intervall auf immer stellen, sonst liefert der Loader weiterhin eine zwischen gespeicherte Fassung des Scripts aus.
Danach den Loader einmal über file:///.../dev/rin-mobile.dev.user.js öffnen —
Tampermonkey fängt .user.js-URLs ab und bietet die Installation an.
Den Loader nicht parallel zum unveränderten Script betreiben. Sind beide in Tampermonkey aktiv, läuft der Code doppelt auf derselben Seite, mit doppelten Karten und zwei Debug-Overlays als Folge.
Geräte-Emulation
DevTools → Device Toolbar (Strg+Umschalt+M) → Edit… → eigenes Gerät mit
430 × 932 und DPR 3 für ein iPhone 15 Pro Max anlegen, Gerätetyp
Mobile, dazu einen iOS-Safari-User-Agent eintragen.
Was die Desktop-Emulation nicht zeigt
Chrome verwendet Blink, iOS Safari verwendet WebKit. Diese Punkte müssen am Gerät geprüft werden:
env(safe-area-inset-bottom)in.rm-nav— im Emulator immer0, die Freihaltung für den Home-Indicator bleibt also ungetestet.- Eine unten fixierte Leiste zusammen mit Safaris ein- und ausfahrender Toolbar.
width: 100vw— auf dem Desktop zählt die Scrollbar mit und erzeugt Phantom-Overflow, das Debug-Overlay meldet dort andere Zahlen als unter iOS.-webkit-text-size-adjustund Safaris automatische Schriftvergrößerung.-webkit-backdrop-filterrendert in Chrome, sieht aber anders aus.
Für höhere Treue ohne iPhone bringt Playwright einen echten WebKit-Build für
Windows mit (npx playwright install webkit).
Fehlersuche
dev/tm-test.user.js installieren — das Script blendet nur einen roten Balken
ein, sonst nichts. Erscheint der Balken nicht, liegt das Problem bei
Tampermonkey (siehe Einrichtung oben) und nicht am Script-Code. Erscheint er,
im Tampermonkey-Dashboard den Tab Externals des Loaders prüfen: ein leerer
oder fehlgeschlagener Eintrag bedeutet, dass der @require-Pfad falsch ist.
Liefert Tampermonkey trotz Update-Intervall weiterhin einen veralteten Stand
aus, das file://-Require durch einen lokalen HTTP-Server ersetzen, der nicht
zwischenspeichert:
// @require http://localhost:8777/rin-mobile.user.js
Aufbau des Repositories
rin-mobile.user.js Themenliste → Karten, Thread → Beitragsstrom
dev/
rin-mobile.dev.user.js Loader, @require mit absolutem Pfad