14 Commits
Author SHA1 Message Date
Andre e459973149 fix: start threads at the top on iOS Safari
The mobile view is assembled after DOMContentLoaded, long after the browser
has settled on a scroll position, and the rebuild never touched it. A rebuilt
thread stays taller than the viewport, so an inherited offset was never
clamped back to zero and the thread opened somewhere inside its first post.
Safari makes this visible where desktop Chrome does not: it restores the
position of an address it has seen before and revises it again after load and
after late images settle, so resetting once is not enough.

Deciding what "top" means also required a target for addresses that carry a
fragment. Post anchors existed only in the original markup, which the rebuild
hides, so a jump from the topic list had nowhere to land and ended up wherever
the browser happened to stand.
2026-08-03 11:33:14 +02:00
Andre c477537826 docs: lead with screenshots and correct outdated documentation
Both documents had drifted away from the repository they describe: they
pointed at fixtures and a helper script that are not present, documented a
local-server workflow that is no longer used, and filed one page type under
the wrong view. Following them meant setting up things that cannot work while
trusting claims that no longer held.

The README also explained every design decision before showing a single
result, so the question a reader asks first — what does this actually look
like — took the longest to answer. Screenshots and a short feature list now
carry that job. The reasoning stays where it is needed, next to the code.

Restricting the dev loader to the live site closes a side effect that only
surfaced locally: its patterns matched page names common enough that the
stylesheet would have leaked into unrelated local projects.

The version number existed in two places and had already drifted apart.
Reading it back from the metadata block leaves one place to change it.
2026-08-03 10:21:05 +02:00
Andre a8ca211805 feat: optimize search.php for the mobile view
search.php had no mobile treatment: the query form rendered as an
unstyled desktop table, and a submitted search fell back to the regular
forum-listing view on the untested assumption that results looked like
topic rows.

The query form is now recognised by its forum-picker select, the one
element the small "search this forum/topic" widgets embedded on other
pages don't share — they reuse the same form action and keyword field
name and would otherwise be picked up by mistake. Results in the "Posts"
display mode the in-thread search bar produces turned out to carry post
excerpts, not topic rows, and needed their own card layout instead of
running through the list view, which would have misread the topic and
profile links inside each hit.

Submit/reset controls kept native iOS Safari chrome even under
-webkit-appearance:none once restyled, clipping their labels — only
visible on device, not in the emulator this project is developed
against. Replacing them with real <button> elements (same name/value,
so the submitted form data is unchanged) fixed it where the CSS-only
attempt didn't; touch targets were sized up to match.
2026-08-03 09:03:27 +02:00
Andre 0d2731eaa9 fix: stop "Mark topics read" from becoming a stuck-open list section
Its link sits inside a category cell but in an inner row with no
nested table, so it slipped past the layout-row guard and got read as
a genuine category heading. Unlike a real heading it never got the
group machinery that makes Announcements and Stickies collapsible, so
it just sat there permanently open with no way to close it.
2026-08-02 16:28:57 +02:00
Andre aec883e7c9 feat: rebuild the forum index and post-login pages into the mobile view
The list view already handled every table row shape a forum page uses,
but the switch only ever routed viewforum.php and search.php into it;
index.php arrived untouched, so the very first page a visitor lands on
stayed in the desktop layout. It matches the same category/forum row
structure a viewforum.php category page already produces, so no new
building logic was needed, only the routing.

Submitting the login form reveals a second gap: it re-renders the same
URL with a generic confirmation page instead of the form, which the
existing form-lookup silently ignored, leaving that page unstyled too
right before phpBB's own redirect fires. It shares its shape with any
other message phpBB might render there (a wrong password, for example),
so the fix recognises it structurally rather than by its wording, and
carries the original message cell over unchanged the same way the login
form itself is reused rather than rebuilt.
2026-08-02 16:28:13 +02:00
Andre 27551e6937 feat: add a mobile login view and a login/logout status pill
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.
2026-08-02 16:06:14 +02:00
Andre 07b4a7437a feat: give the list view the same page navigation as the thread
The list built its own page bar from every link on the page carrying a
start parameter. Topic rows bring their own "Go to page: 1 … 42, 43" along,
so on the main forum that bar came to 103 entries, most of them jumping into
the middle of some unrelated thread rather than to a page of the list. Even
the correct entries were only the handful phpBB renders, which for 610 pages
means there is no way to reach page 300.

The thread already solved this: read per_page out of the page's own jumpto
function, compute every target, and offer them through a fixed bar and a
sheet. Both views paginate over the same parameter, so the bar, the sheet
and the state behind them now sit in one place and serve both. The list
leaves the bar out where a single page holds everything, which a thread of
posts never does but a category page always does.

Bottom clearance moved onto the bar for that reason — tied to the view, it
would reserve an empty strip under lists that have no bar.
2026-08-02 15:35:04 +02:00
Andre 2474cd3b9f feat: group the list view by section and let the groups collapse
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.
2026-08-02 15:26:04 +02:00
Andre 4ce48b3875 feat: mark the boundary between forums and topics in the list view
A forum page carries its subforums and its topics in tables of their own,
each with a header. Rebuilding the page into one stream of cards dissolves
that separation: both kinds render identically, so where the subforums end
and the topics begin was no longer visible, and a single card gave no hint
whether tapping it opens a forum or a thread.

A header naming the kind and its count is therefore inserted wherever the
kind changes, and forum cards are tinted and edged. Directly below a
category heading the header is suppressed — on the forum root that heading
already introduces the forums under it, and repeating the kind there adds
a strip per category for nothing.

Counting requires knowing how many cards a block actually yields, which is
not the number of rows: rows that turn out to be layout or fail conversion
drop out. The cards are therefore built completely before any of them is
emitted.
2026-08-02 15:11:47 +02:00
Andre 3609d9e151 fix: recognise forum rows in the list view
A viewforum page is not always a list of topics — a category page lists
forums, and a forum page can carry both. Every row was treated as a topic,
so on those pages the only link the row offered was the icon in its last-post
column: the forum name was dropped, its description ended up in the muted
footer line, and nothing was left to tap. Moving between forums on a phone
was impossible.

Rows are now classified by their first usable link, which relies on document
order: a forum row names the forum before it links the last post. Detection
prefers phpBB's own classes and falls back to the shape of the row, because
the board runs a custom theme whose markup could not be verified — the page
requires a login.

That fallback needs a brake. The page scaffolding is itself a table, so a
link outside any list row resolves to a row containing the whole document,
which passed for a forum row and turned the page header, a stylesheet and
inline scripts into a forum description. Rows carrying nested tables, forms
or scripts are therefore rejected, and descriptions are read only from the
title cell's own text.
2026-08-02 14:55:39 +02:00
Andre bc9c4937ff 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.
2026-08-02 14:38:25 +02:00
Andre eff957ed07 refactor: merge the two userscripts into one
The list script matched the whole forum, thread pages included, and injected its
stylesheet there before bailing out of its own build. Both stylesheets were
therefore live on every thread, and rules sharing a class name resolved by
userscript load order rather than by intent — a layout that could differ between
two machines with the same files installed. One of those clashes was already
costing the clearance behind the fixed bar.

A single script picks its view from the page type instead, which turns the
shared class namespace from a hazard into a plain fact of the file, resolved
with a modifier on the root element. Pages outside either view now stay
untouched, where before they still received a full stylesheet.

Two things the merge brought to light: the global image rule was overriding the
avatar dimensions, which only went unnoticed because square avatars happen to
scale to the same result, and a zero-width space had been sitting in the source
as a literal character rather than an escape.

Installations carrying the old thread script have to remove it; the remaining
one keeps its name and updates in place.

Claude-Session: https://claude.ai/code/session_0158owAAsXAfFiT3hGkQt1Xk
2026-08-02 14:24:29 +02:00
Andre bd4fc85d38 docs: note that the test fixture is not versioned
The project instructions describe testing against a saved forum page without
mentioning that the ignore rules exclude it. A fresh clone therefore has no
fixture, and obtaining one requires a logged-in session — not something that
becomes apparent from a missing file alone.

Claude-Session: https://claude.ai/code/session_0158owAAsXAfFiT3hGkQt1Xk
2026-08-02 14:11:41 +02:00
Andre c3f56b673d docs: add project instructions for Claude Code
Several traits of this codebase cost time to rediscover and are invisible in
the source: both scripts share a stylesheet namespace on thread pages, the page
indicator in the forum markup is split across inline tags so searching the raw
HTML for it comes up empty, and anything appended to the body has to come after
the original content is hidden.

The file also records the throwaway jsdom harness used to check the thread
script against a saved page, since the project deliberately carries no test
dependencies and the setup is easy to get wrong.

Claude-Session: https://claude.ai/code/session_0158owAAsXAfFiT3hGkQt1Xk
2026-08-02 14:08:59 +02:00