feat: add breadcrumb navigation to both views

Replacing the page hides the forum's own trail along with everything else,
so a thread became a dead end: the only ways back to the parent forum were
the browser's back button or editing the URL by hand. Deep in a long thread
neither is workable.

The bar has to be fixed rather than sticky. The view root carries
overflow:hidden to contain phantom horizontal overflow, which would make it
the scrollport a sticky bar sticks to — one that never scrolls. Its top
clearance hangs off a body class so pages without a trail keep their full
height instead of an empty strip.

Board index still leads to a page the script does not rebuild; that would
need a forum overview of its own.

Debug flag off for daily use.
This commit is contained in:
Andre
2026-08-02 14:38:25 +02:00
parent eff957ed07
commit bc9c4937ff
3 changed files with 91 additions and 4 deletions
+9 -1
View File
@@ -26,7 +26,8 @@ Ein IIFE, streng der Reihe nach:
4. `replaceBody(root)` blendet alle bisherigen `body`-Kinder aus und haengt die 4. `replaceBody(root)` blendet alle bisherigen `body`-Kinder aus und haengt die
neue Ansicht an neue Ansicht an
5. `buildList()` beziehungsweise `buildThread()` lesen das Original-DOM und 5. `buildList()` beziehungsweise `buildThread()` lesen das Original-DOM und
bauen ein `div.rm-root` bauen ein `div.rm-root`; beide haengen zum Schluss ueber `addCrumbs()` die
fixierte Brotkrumenleiste an
6. `run()` waehlt die Ansicht und haengt bei `DEBUG` das gruene Overlay an 6. `run()` waehlt die Ansicht und haengt bei `DEBUG` das gruene Overlay an
7. Start ueber `DOMContentLoaded`, falls `readyState === 'loading'` 7. Start ueber `DOMContentLoaded`, falls `readyState === 'loading'`
@@ -55,6 +56,13 @@ Die Funktion markiert *alle* vorhandenen `body`-Kinder mit `.rm-hidden`. Das
Sheet wird deshalb erst beim ersten Antippen gebaut — das loest das Problem Sheet wird deshalb erst beim ersten Antippen gebaut — das loest das Problem
nebenbei mit. nebenbei mit.
**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>` **`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 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. `txt(document.body)` greift trotzdem. Nicht vorschnell fuer kaputt halten.
+16
View File
@@ -27,6 +27,14 @@ 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 page; without it, only first page and the original previous/next links remain
available. available.
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 script runs at `document-start`, injects a mobile viewport meta tag, and 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 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 prepended to the page showing which view was chosen and how many rows were
@@ -183,6 +191,14 @@ Seitenlinks übernommen, weil phpBB davon immer nur eine Handvoll anzeigt.
einbettet; fehlt sie, bleiben nur die erste Seite und die ursprünglichen einbettet; fehlt sie, bleiben nur die erste Seite und die ursprünglichen
Zurück-/Weiter-Links nutzbar. Zurück-/Weiter-Links nutzbar.
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.
Das Script läuft bei `document-start`, setzt ein mobiles Viewport-Meta-Tag und 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 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 Diagnose-Overlay über die Seite gelegt, das zeigt, welche Ansicht gewählt wurde
+66 -3
View File
@@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name RIN Mobile // @name RIN Mobile
// @description Mobile Ansicht fuer cs.rin.ru: Themenlisten als Karten, Threads als Beitragsstrom // @description Mobile Ansicht fuer cs.rin.ru: Themenlisten als Karten, Threads als Beitragsstrom
// @version 3.0 // @version 3.1
// @match *://cs.rin.ru/forum/* // @match *://cs.rin.ru/forum/*
// @run-at document-start // @run-at document-start
// ==/UserScript== // ==/UserScript==
@@ -9,7 +9,7 @@
(function () { (function () {
'use strict'; 'use strict';
var DEBUG = true; var DEBUG = false;
var log = []; var log = [];
function h() { return document.head || document.documentElement; } function h() { return document.head || document.documentElement; }
@@ -60,6 +60,32 @@
/* Freihaltung hinter der fixierten Leiste der Threadansicht. */ /* Freihaltung hinter der fixierten Leiste der Threadansicht. */
.rm-root.rm-thread { padding-bottom:64px !important; } .rm-root.rm-thread { padding-bottom:64px !important; }
/* Brotkrumen, beide Ansichten. Fixiert statt sticky: .rm-root traegt
overflow:hidden und wuerde als Scrollport einer sticky-Leiste dienen,
die darin nie klebt. Fixierte Nachfahren klammert es nicht, weil der
Root keinen eigenen Containing Block aufspannt. */
.rm-crumbs { box-sizing:border-box;
position:fixed; top:0; left:0; right:0; z-index:9998;
display:flex; align-items:center; gap:6px;
height:calc(44px + env(safe-area-inset-top));
padding:env(safe-area-inset-top) 12px 0;
overflow-x:auto; overflow-y:hidden;
-webkit-overflow-scrolling:touch; white-space:nowrap;
background:rgba(28,28,30,.94);
-webkit-backdrop-filter:blur(12px);
backdrop-filter:blur(12px);
border-bottom:1px solid rgba(128,128,128,.3); }
.rm-crumbs a { flex:none; padding:8px 2px; font-size:14px;
color:#eee !important; text-decoration:none !important; }
.rm-crumbs a:last-of-type { font-weight:650; }
.rm-crumbs .rm-sep { flex:none; font-size:13px; color:#eee; opacity:.45; }
/* Nur gesetzt, wenn die Leiste wirklich gebaut wurde — ohne Brotkrumen
im Original-DOM bleibt der Platz sonst leer reserviert. */
body.rm-crumbed .rm-root {
padding-top:calc(45px + env(safe-area-inset-top)) !important; }
body.rm-crumbed .rm-dbg {
margin-top:calc(45px + env(safe-area-inset-top)); }
/* === Themenliste === */ /* === Themenliste === */
.rm-card { box-sizing:border-box !important; .rm-card { box-sizing:border-box !important;
@@ -222,6 +248,41 @@
document.body.appendChild(root); document.body.appendChild(root);
} }
// Ohne die Brotkrumen fuehrt aus der neuen Ansicht kein Weg zurueck: das
// Original-DOM ist ausgeblendet, und phpBB verlinkt das Elternforum nirgends
// sonst. Muss nach replaceBody() laufen, weil die Leiste an body haengt.
function addCrumbs() {
// Die Leiste steht zweimal im Markup, ueber und unter dem Inhalt. Die
// erste genuegt; a.breadcrumbs faengt Seiten ohne den Absatz drumherum.
var box = document.querySelector('p.breadcrumbs');
var items = box ? box.querySelectorAll('a')
: document.querySelectorAll('a.breadcrumbs');
var bar = document.createElement('div');
bar.className = 'rm-crumbs';
var n = 0;
Array.prototype.forEach.call(items, function (a) {
var t = txt(a);
if (!t) return;
if (n) {
var sep = document.createElement('span');
sep.className = 'rm-sep';
sep.textContent = '›';
bar.appendChild(sep);
}
var l = document.createElement('a');
l.href = a.href;
l.textContent = t;
bar.appendChild(l);
n++;
});
log.push('krumen=' + n);
if (!n) return;
document.body.appendChild(bar);
document.body.classList.add('rm-crumbed');
}
// ========================================================================== // ==========================================================================
// Themenliste // Themenliste
// ========================================================================== // ==========================================================================
@@ -344,6 +405,7 @@
if (pages.children.length) root.appendChild(pages); if (pages.children.length) root.appendChild(pages);
replaceBody(root); replaceBody(root);
addCrumbs();
if (DEBUG) { if (DEBUG) {
var c0 = root.querySelector('.rm-card'); var c0 = root.querySelector('.rm-card');
@@ -714,6 +776,7 @@
root.appendChild(nav); root.appendChild(nav);
replaceBody(root); replaceBody(root);
addCrumbs();
} }
// ========================================================================== // ==========================================================================
@@ -727,7 +790,7 @@
if (DEBUG) { if (DEBUG) {
var d = document.createElement('div'); var d = document.createElement('div');
d.className = 'rm-dbg'; d.className = 'rm-dbg';
d.textContent = 'RIN Mobile v3.0\n' + log.join('\n'); d.textContent = 'RIN Mobile v3.1\n' + log.join('\n');
document.body.insertBefore(d, document.body.firstChild); document.body.insertBefore(d, document.body.firstChild);
} }
} }