From 850fa52fa2726cef9adfa3d2fb84fc92b998286c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 15:01:24 +0000 Subject: [PATCH 1/3] docs: take the page order from MediaWiki:Sidebar The order of the documentation was written down twice: as MediaWiki:Sidebar, and again as the arguments of the {{prevnext}} at the foot of each of twenty-one pages. Nothing checked that the two agreed, and twice they did not -- a page reached the sidebar with no call naming it, and the last page of the chain had no row at all, because a lone argument read as the next step and there was no way to spell "nothing after this". Module:Sequence reads the sidebar, so {{prevnext}} takes no arguments. The direction is forced: MediaWiki:Sidebar is read line by line by the skin rather than parsed as wikitext, so it can call nothing; but it is an ordinary page, so anything can read it. What the sidebar contributes is order alone. Labels stay in Template:prevnext/label, which reads each target's own translated title, so none of this reaches the translations -- a prevnext sits outside the translate tags and no ko file changed. The two decisions #455 left open: the sequence keeps crossing from the docs group into the references group, because a reader at the end of the first group is better sent on than stopped; and index heads it without being a sidebar entry, so the module prepends it rather than the sidebar gaining a duplicate of what every skin's logo already points at. #455 also made this conditional on Scribunto running in the image, which was not known when it was filed. It does: the base image installs LuaSandbox 4.1.2 and this site has rendered a Lua module since #473. No Dockerfile change. smoke asserts what went wrong before: every page the sidebar names has a navigation row, index has no previous link, and Licenses has no next. --- .github/workflows/smoke.yml | 32 ++++++++++++ docs/Commands.wikitext | 2 +- docs/Configuration.wikitext | 2 +- docs/Deploying.wikitext | 2 +- docs/Development.wikitext | 2 +- docs/Editing sidebar.wikitext | 2 +- docs/Extensions.wikitext | 2 +- docs/Getting Started.wikitext | 2 +- docs/Images.wikitext | 2 +- docs/Installation.wikitext | 2 +- docs/JavaScript.wikitext | 2 +- docs/Licenses.wikitext | 2 +- docs/Lua modules.wikitext | 2 +- docs/Module:Sequence | 77 +++++++++++++++++++++++++++++ docs/Pages.wikitext | 2 +- docs/Searching.wikitext | 2 +- docs/Skins.wikitext | 2 +- docs/Standalone binary.wikitext | 2 +- docs/Template:prevnext.wikitext | 10 ++-- docs/Template:prevnext/doc.wikitext | 30 ++++++----- docs/Translating.wikitext | 2 +- docs/Troubleshooting.wikitext | 2 +- docs/Why wikitext.wikitext | 2 +- docs/Writing an extension.wikitext | 2 +- docs/index.wikitext | 2 +- 25 files changed, 151 insertions(+), 40 deletions(-) create mode 100644 docs/Module:Sequence diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index d45bc53c2..533e262a5 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -253,6 +253,38 @@ jobs: exit 1 fi + # Every page the sidebar names gets a navigation row, and the two ends of the sequence get + # one link rather than two. The order lives in MediaWiki:Sidebar and Module:Sequence reads + # it (#455); before that it was restated in each call, and what went wrong twice was + # exactly this -- a page in the sidebar with no row, and the last page with none either. + missing= + while IFS= read -r page; do + file="dist/$(printf '%s' "$page" | tr ' ' '_').html" + if [ ! -s "$file" ] || ! grep -q 'wikven-prevnext' "$file"; then + missing="$missing $page" + fi + done < <(sed -n 's@^\*\* *Special:MyLanguage/\([^|]*\).*@\1@p' 'docs/MediaWiki:Sidebar.wikitext') + if [ -n "$missing" ]; then + echo "::error::sidebar pages with no navigation row:$missing" + exit 1 + fi + + # index heads the sequence without being a sidebar entry, so it is named here rather than + # read. The two ends are what the old shape could not spell: a lone argument was the next + # step, so the last page had no way to say it had none. + if ! grep -q 'wikven-prevnext' dist/index.html; then + echo "::error::dist/index.html has no navigation row" + exit 1 + fi + if grep -q 'wikven-prevnext-prev' dist/index.html; then + echo "::error::index heads the sequence and should have no previous link" + exit 1 + fi + if grep -q 'wikven-prevnext-next' dist/Licenses.html; then + echo "::error::Licenses ends the sequence and should have no next link" + exit 1 + fi + # A prevnext link is labelled with the target page's own title, so on a translated page it # must carry the translated one. The label is not in the calling page's source: the call # sits outside the translate tags and passes a page name, and the template reads the diff --git a/docs/Commands.wikitext b/docs/Commands.wikitext index 33f239cbb..69709312b 100644 --- a/docs/Commands.wikitext +++ b/docs/Commands.wikitext @@ -193,4 +193,4 @@ Runs translate check over a source tree and reports each finding as |} -{{prevnext|Standalone binary|Development}} +{{prevnext}} diff --git a/docs/Configuration.wikitext b/docs/Configuration.wikitext index 5f85d733d..125b2b2e8 100644 --- a/docs/Configuration.wikitext +++ b/docs/Configuration.wikitext @@ -222,4 +222,4 @@ docker run --rm --entrypoint cat ghcr.io/chaotic-ground/wikven extensions/Wikven It is also in the [https://github.com/chaotic-ground/wikven/blob/main/default.yml repository]. -{{prevnext|Troubleshooting|Standalone binary}} +{{prevnext}} diff --git a/docs/Deploying.wikitext b/docs/Deploying.wikitext index 9b3cc62fa..f9d7ea236 100644 --- a/docs/Deploying.wikitext +++ b/docs/Deploying.wikitext @@ -182,4 +182,4 @@ A commit that follows a branch is the same manifest with the source This site is the working example. Its manifests are in [https://github.com/chaotic-ground/wikven/tree/main/updatecli/updatecli.d updatecli/updatecli.d/] (one release tag per reference pin, one branch head per commit pin), and [https://github.com/chaotic-ground/wikven/blob/main/.github/workflows/updatecli.yml a weekly workflow] applies them, opening one pull request per pipeline. [https://www.updatecli.io/docs/ Upstream's own documentation] covers everything a manifest can do beyond the two shapes above. -{{prevnext|Translating|Troubleshooting}} +{{prevnext}} diff --git a/docs/Development.wikitext b/docs/Development.wikitext index 892983e38..377976416 100644 --- a/docs/Development.wikitext +++ b/docs/Development.wikitext @@ -196,4 +196,4 @@ The e2e run serves dist/ under a /wikven/ path prefix, Versions follow [https://www.conventionalcommits.org/ Conventional Commits], which release-please uses to generate the changelog and bump the version. -{{prevnext|Commands|Writing an extension}} +{{prevnext}} diff --git a/docs/Editing sidebar.wikitext b/docs/Editing sidebar.wikitext index 71de7bc39..5b7d6f679 100644 --- a/docs/Editing sidebar.wikitext +++ b/docs/Editing sidebar.wikitext @@ -55,4 +55,4 @@ Some default sidebar blocks need a live wiki, so {{SITENAME}} drops them from th * [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Interface/Sidebar Manual:Interface/Sidebar] -{{prevnext|Pages|Images}} +{{prevnext}} diff --git a/docs/Extensions.wikitext b/docs/Extensions.wikitext index 478ac886a..50b9ce4eb 100644 --- a/docs/Extensions.wikitext +++ b/docs/Extensions.wikitext @@ -88,4 +88,4 @@ Scribunto is one of those, and the one most wikis reach for: it has a page of it An extension of your own can also be made to survive a build deliberately; [[Special:MyLanguage/Writing an extension|Writing an extension]] is the contract for that. -{{prevnext|Skins|Lua modules}} +{{prevnext}} diff --git a/docs/Getting Started.wikitext b/docs/Getting Started.wikitext index ce10984fc..c9782e601 100644 --- a/docs/Getting Started.wikitext +++ b/docs/Getting Started.wikitext @@ -128,4 +128,4 @@ echo '{{Note|Back up your wiki first.}}' >> src/index.wikitext Build again: the home page now shows the note. See [[Special:MyLanguage/Pages#Templates|Pages#Templates]] for parameters and more. -{{prevnext|Installation|Pages}} +{{prevnext}} diff --git a/docs/Images.wikitext b/docs/Images.wikitext index baffbb3a0..dbe4e0933 100644 --- a/docs/Images.wikitext +++ b/docs/Images.wikitext @@ -31,4 +31,4 @@ The thumbnailing backend is detected from the tools present at build time: [File: namespace, the same way other namespaced pages are named. For My photo.png, that is File:My photo.png.wikitext. -{{prevnext|Editing sidebar|Skins}} +{{prevnext}} diff --git a/docs/Installation.wikitext b/docs/Installation.wikitext index 534574f29..4485a46b8 100644 --- a/docs/Installation.wikitext +++ b/docs/Installation.wikitext @@ -61,4 +61,4 @@ This produces a local wikven image you run exactly like the publish Once you have {{SITENAME}}, head to [[Special:MyLanguage/Getting Started|Getting Started]] to build your first site. -{{prevnext|Why wikitext|Getting Started}} +{{prevnext}} diff --git a/docs/JavaScript.wikitext b/docs/JavaScript.wikitext index ff854f631..418f1dac6 100644 --- a/docs/JavaScript.wikitext +++ b/docs/JavaScript.wikitext @@ -38,4 +38,4 @@ Only gadgets marked default are loaded: a static site has no logged These docs ship one such gadget, PersistTabber, which remembers whether you pick the Docker or the binary tab in the install steps. Every other tabber on the page switches to the tab you picked, and the choice is still there when you move to another page. -{{prevnext|Lua modules|Searching}} +{{prevnext}} diff --git a/docs/Licenses.wikitext b/docs/Licenses.wikitext index 9538c9bf1..b6f1d8946 100644 --- a/docs/Licenses.wikitext +++ b/docs/Licenses.wikitext @@ -24,4 +24,4 @@ __TOC__ The standalone binary is compiled with [https://frankenphp.dev/ FrankenPHP], which also links several Caddy modules; see the FrankenPHP project for their licenses. The Docker image keeps MediaWiki's own COPYING, and each component's full license text is available from its project. -{{prevnext|prev=Writing an extension}} +{{prevnext}} diff --git a/docs/Lua modules.wikitext b/docs/Lua modules.wikitext index 0cc623886..ec209e4cd 100644 --- a/docs/Lua modules.wikitext +++ b/docs/Lua modules.wikitext @@ -195,4 +195,4 @@ A refused build says so in its exit status as well as in words, so a script that This documentation site uses a module itself, and the page you are reading is baked with the image on every change, with the module's answer asserted to be in it. The binary is held to the same rule on a source tree of its own, x86-64 on every change and arm64 on every nightly, so each row of the table above is something a machine checks rather than something this page claims. -{{prevnext|Extensions|JavaScript}} +{{prevnext}} diff --git a/docs/Module:Sequence b/docs/Module:Sequence new file mode 100644 index 000000000..b5907021d --- /dev/null +++ b/docs/Module:Sequence @@ -0,0 +1,77 @@ +-- The order of the documentation, read from the one place it is written down. +-- +-- MediaWiki:Sidebar lists the pages in order, and Template:prevnext used to list them again as its +-- arguments. Nothing checked that the two agreed, and twice they did not: a page was added to the +-- sidebar without a chain entry, and the chain's last page was left with no row at all (#455). Both +-- are the same failure -- adding a page meant editing three files and forgetting one was invisible. +-- +-- The direction is forced. MediaWiki:Sidebar is read line by line by the skin rather than parsed as +-- wikitext, so it cannot call anything; but it is an ordinary page, so anything can read it. +-- +-- What the sidebar contributes is order alone. The link labels stay where they were, in +-- Template:prevnext/label, which reads each target's own translated title. +local p = {} + +-- Where the order lives, and the entry line to read out of it: "** Special:MyLanguage/Page|key". +local SIDEBAR = 'MediaWiki:Sidebar' +local ENTRY = '^%*%*%s*Special:MyLanguage/([^|\n]+)' + +-- The sidebar has no entry for the main page: every skin points its logo there already. It heads +-- the sequence all the same, so it is prepended rather than added to the sidebar, where it would +-- show up as a duplicate of the logo. +local FIRST = 'index' + +-- Both sidebar groups, in one sequence. The chain has always crossed from the docs group into the +-- references group, and a reader at the end of the first group is better sent on than stopped. +local function sequence() + local sidebar = mw.title.new(SIDEBAR) + local content = sidebar and sidebar:getContent() + local pages = { FIRST } + if content then + for line in mw.text.gsplit(content, '\n') do + local target = line:match(ENTRY) + if target then + pages[#pages + 1] = mw.text.trim(target) + end + end + end + return pages +end + +-- The page on either side of this one, or nil at an end of the sequence. +-- +-- Matched on the root title, because on a translation this is "Searching/ko" and the sidebar names +-- the page it was translated from. +local function neighbour(step) + local here = mw.title.getCurrentTitle().rootText + local pages = sequence() + for i, page in ipairs(pages) do + if page == here then + return pages[i + step] + end + end + return nil +end + +local function link(frame, page, before, after) + local label = frame:expandTemplate{ title = 'prevnext/label', args = { page } } + return before .. '[[Special:MyLanguage/' .. page .. '|' .. label .. ']]' .. after +end + +-- The row: a previous link, a next link, or both. A page the sidebar does not name gets neither, +-- which is what every page outside the sequence -- a template, a category, a File: page -- should +-- get if it ever calls this. +function p.row(frame) + local out = {} + local previous = neighbour(-1) + if previous then + out[#out + 1] = '
' .. link(frame, previous, '← ', '') .. '
' + end + local following = neighbour(1) + if following then + out[#out + 1] = '
' .. link(frame, following, '', ' →') .. '
' + end + return table.concat(out) +end + +return p diff --git a/docs/Pages.wikitext b/docs/Pages.wikitext index 2a7b7e2f2..82c5112de 100644 --- a/docs/Pages.wikitext +++ b/docs/Pages.wikitext @@ -87,4 +87,4 @@ saved as Template:Note.wikitext and used on a page as: renders as "'''Note:''' Back up your wiki first." Templates take positional ({{{1}}}) and named parameters and run [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Parser_functions parser functions] exactly as in MediaWiki. The syntax itself is standard MediaWiki; see [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Templates Help:Templates]. -{{prevnext|Getting Started|Editing sidebar}} +{{prevnext}} diff --git a/docs/Searching.wikitext b/docs/Searching.wikitext index 67791c5f5..824d7ac91 100644 --- a/docs/Searching.wikitext +++ b/docs/Searching.wikitext @@ -81,4 +81,4 @@ containing…" action and its Enter key go to that page. This site uses a page titled Search. -{{prevnext|JavaScript|Translating}} +{{prevnext}} diff --git a/docs/Skins.wikitext b/docs/Skins.wikitext index 4f3243d66..b246babb4 100644 --- a/docs/Skins.wikitext +++ b/docs/Skins.wikitext @@ -153,4 +153,4 @@ The other six the skin reads off the configuration itself, so they behave as doc What the inert ones would have configured, {{SITENAME}} supplies its own way where it can: the colour theme is on the Settings page the build writes in place of Special:MobileOptions, and the site's own navigation is written into the main menu of every rendered page. -{{prevnext|Images|Extensions}} +{{prevnext}} diff --git a/docs/Standalone binary.wikitext b/docs/Standalone binary.wikitext index a65fae12e..ca51b8f22 100644 --- a/docs/Standalone binary.wikitext +++ b/docs/Standalone binary.wikitext @@ -133,4 +133,4 @@ The binary renders on its own, but uses these host programs when they are presen {{note|Fetching over HTTPS, Wikimedia Commons images via InstantCommons and any [[Special:MyLanguage/Configuration#WikvenRepositories|third-party extensions or skins]], relies on your system's CA certificates. On a minimal system without them, install your distribution's CA bundle (for example the ca-certificates package). A site with only local content and images needs no network access.}} -{{prevnext|Configuration|Commands}} +{{prevnext}} diff --git a/docs/Template:prevnext.wikitext b/docs/Template:prevnext.wikitext index e1b050d03..c520b0d54 100644 --- a/docs/Template:prevnext.wikitext +++ b/docs/Template:prevnext.wikitext @@ -1,11 +1,7 @@
{{#if:{{{2|}}}{{{prev|}}}| -
← [[Special:MyLanguage/{{{prev|{{{1|}}}}}}|{{prevnext/label|{{{prev|{{{1|}}}}}}}}]]
- }}{{#if:{{{2|{{{1|}}}}}}|
[[Special:MyLanguage/{{{2|{{{1|}}}}}}|{{prevnext/label|{{{2|{{{1|}}}}}}}}]] →
}}{{#invoke:Sequence|row}}
{{prevnext/doc}} diff --git a/docs/Template:prevnext/doc.wikitext b/docs/Template:prevnext/doc.wikitext index aa06ca596..a3f11aa1a 100644 --- a/docs/Template:prevnext/doc.wikitext +++ b/docs/Template:prevnext/doc.wikitext @@ -2,24 +2,30 @@ Bottom-of-page navigation between the steps of the documentation, as a row of on == Usage == - {{prevnext|next step}} -{{prevnext|previous step|next step}} -{{prevnext|prev=previous step}} + {{prevnext}} -Each argument is a page name, not a link label: it is the link target, and the link text is the -target's own title. +No arguments. The order of the documentation is [[MediaWiki:Sidebar]], and +Module:Sequence reads it: the page before this one in the sidebar becomes the previous +link, the page after it the next. The first and last pages of the sequence get one link rather than +two, without having to say so, and a page the sidebar does not name gets no row at all. -The next page is the last positional argument, so one of them is a next and two are a previous and -a next. The last page of the chain is the one form that leaves out: it has a previous and no next, -and an empty second argument reads the same as an absent one, so there is no positional way to say -it. That page names prev= instead, which is otherwise the same previous link. +It used to take the page names, which meant the order was written down twice -- once as the +sidebar's and once across twenty-one calls -- with nothing checking that the two agreed. Twice they +did not: a page reached the sidebar without a call naming it, and the last page of the chain had no +row, because a lone argument read as the *next* step and there was no way to spell "nothing after +this". Adding a page meant three edits, and forgetting the third was invisible. Now it is two: the +sidebar, and a {{prevnext}} at the foot of the new page. + +index is the exception the module carries: it heads the sequence but is not a sidebar +entry, because every skin already points its logo at the main page. == Translated pages == A prevnext sits outside the <translate> tags, so it is copied whole into every -translation of the page it is on. That is why it takes page names alone: a label passed in would be -a second copy of the target's title in every page that links to it, and one nothing checks, so -renaming a page would leave the links to it quietly wrong in every language. +translation of the page it is on. That is why no label is written into the call, and was not when +the call still took page names: a label passed in would be a second copy of the target's title in +every page that links to it, and one nothing checks, so renaming a page would leave the links to it +quietly wrong in every language. Template:prevnext/label reads that title instead, from the one place it is translated: the target's title unit, which the build writes to Translate's "Page display title" diff --git a/docs/Translating.wikitext b/docs/Translating.wikitext index cd30f70b1..3232f8aa9 100644 --- a/docs/Translating.wikitext +++ b/docs/Translating.wikitext @@ -147,4 +147,4 @@ $ docker run --rm -v "$PWD/src:/workspace/src" ghcr.io/chaotic-ground/wikven tra The [https://github.com/chaotic-ground/wikven/tree/main/actions/check-translations check-translations action] runs the same check on pull requests, reporting each one as an annotation on the file it belongs to. It fails the build on a broken source page and never on a translation that is behind; pass gate: false to report even a broken page without failing. This documentation site is checked this way. To bring a translation back up to date, edit it to match the new source, then translate stamp it again. -{{prevnext|Searching|Deploying}} +{{prevnext}} diff --git a/docs/Troubleshooting.wikitext b/docs/Troubleshooting.wikitext index 7a3f53570..948341536 100644 --- a/docs/Troubleshooting.wikitext +++ b/docs/Troubleshooting.wikitext @@ -75,4 +75,4 @@ Every parse of a page embedding a Commons image asks Commons for its thumbnail. On a site with several skins, each is rendered by its own process and the build reports each one that failed by name (build failed for skin ...). The output of each pass is printed under its own heading, so read the section for the named skin rather than the end of the log. If the machine is short of memory, WIKVEN_BUILD_JOBS=1 runs the passes one at a time; see [[Special:MyLanguage/Commands|Commands]]. -{{prevnext|Deploying|Configuration}} +{{prevnext}} diff --git a/docs/Why wikitext.wikitext b/docs/Why wikitext.wikitext index fa18af7ac..a73f5f113 100644 --- a/docs/Why wikitext.wikitext +++ b/docs/Why wikitext.wikitext @@ -30,4 +30,4 @@ __TOC__ That last cost, running MediaWiki, is the one {{SITENAME}} removes: it renders your wikitext through MediaWiki once, at build time, and ships plain static HTML. You get templates, parser functions and the rest with nothing to run in production. Reach for {{SITENAME}} when that trade is worth it, typically when your content is already wikitext, or when templates and transclusion would save real work. -{{prevnext|Installation}} +{{prevnext}} diff --git a/docs/Writing an extension.wikitext b/docs/Writing an extension.wikitext index 650a50411..4bfdf34af 100644 --- a/docs/Writing an extension.wikitext +++ b/docs/Writing an extension.wikitext @@ -150,4 +150,4 @@ Worth checking specifically: * [[Special:MyLanguage/JavaScript|JavaScript]] — how scripts and gadgets are bundled for a static host. -{{prevnext|Development|Licenses}} +{{prevnext}} diff --git a/docs/index.wikitext b/docs/index.wikitext index 138083de1..b1c5df9b6 100644 --- a/docs/index.wikitext +++ b/docs/index.wikitext @@ -54,5 +54,5 @@ These need a live wiki, so they are not part of the static export: What replaces some of them is written down: the reader's own display choices are on a [[Special:MyLanguage/Skins#Settings|Settings page]] the build writes, and searching is [[Special:MyLanguage/Searching|done in the browser]]. -{{prevnext|Why wikitext}} +{{prevnext}} {{DISPLAYTITLE:Wikven}} From e6c00929048c257c9fc720caefa58b07d29a30b8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 15:07:24 +0000 Subject: [PATCH 2/3] fix: keep the main page out of the sequence, and match the div not the class The main page is where a reader arrives rather than a step they walk, and it already names its own way on in its opening lines -- Why wikitext, Installation and Getting Started are all linked there. So it leaves the sequence entirely: the module no longer prepends it and index.wikitext no longer calls prevnext. Why wikitext heads the sequence, which is what the sidebar says. That also drops a name the module had hardcoded, which was a small second copy of something written down elsewhere -- the thing this branch exists to remove. The smoke check that caught this was itself wrong, and its own failure is what showed it: ##[error]index heads the sequence and should have no previous link index had no previous link. TemplateStyles inlines prevnext/styles.css into every page carrying a row, so `.wikven-prevnext-prev` appears in the page's own stylesheet whether or not the div was rendered. Matched on class="wikven-prevnext-prev" now, which is the div. --- .github/workflows/smoke.yml | 20 ++++++++++---------- docs/Module:Sequence | 12 +++++------- docs/Template:prevnext/doc.wikitext | 5 +++-- docs/index.wikitext | 1 - 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index 533e262a5..b83e9cafe 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -269,18 +269,18 @@ jobs: exit 1 fi - # index heads the sequence without being a sidebar entry, so it is named here rather than - # read. The two ends are what the old shape could not spell: a lone argument was the next - # step, so the last page had no way to say it had none. - if ! grep -q 'wikven-prevnext' dist/index.html; then - echo "::error::dist/index.html has no navigation row" - exit 1 - fi - if grep -q 'wikven-prevnext-prev' dist/index.html; then - echo "::error::index heads the sequence and should have no previous link" + # The two ends of the sequence get one link rather than two, which the old shape could not + # spell: a lone argument was the next step, so the last page had no way to say it had none. + # + # Matched on the div rather than the class name. TemplateStyles inlines prevnext/styles.css + # into every page that has a row, so both class names appear in the page's own stylesheet + # whether or not either link was rendered -- which is what the first draft of this check + # tripped over. + if grep -q 'class="wikven-prevnext-prev"' dist/Why_wikitext.html; then + echo "::error::Why wikitext heads the sequence and should have no previous link" exit 1 fi - if grep -q 'wikven-prevnext-next' dist/Licenses.html; then + if grep -q 'class="wikven-prevnext-next"' dist/Licenses.html; then echo "::error::Licenses ends the sequence and should have no next link" exit 1 fi diff --git a/docs/Module:Sequence b/docs/Module:Sequence index b5907021d..b1ac882a4 100644 --- a/docs/Module:Sequence +++ b/docs/Module:Sequence @@ -16,17 +16,12 @@ local p = {} local SIDEBAR = 'MediaWiki:Sidebar' local ENTRY = '^%*%*%s*Special:MyLanguage/([^|\n]+)' --- The sidebar has no entry for the main page: every skin points its logo there already. It heads --- the sequence all the same, so it is prepended rather than added to the sidebar, where it would --- show up as a duplicate of the logo. -local FIRST = 'index' - -- Both sidebar groups, in one sequence. The chain has always crossed from the docs group into the -- references group, and a reader at the end of the first group is better sent on than stopped. local function sequence() local sidebar = mw.title.new(SIDEBAR) local content = sidebar and sidebar:getContent() - local pages = { FIRST } + local pages = {} if content then for line in mw.text.gsplit(content, '\n') do local target = line:match(ENTRY) @@ -38,7 +33,10 @@ local function sequence() return pages end --- The page on either side of this one, or nil at an end of the sequence. +-- The page on either side of this one, or nil at an end of the sequence -- and nil either way for a +-- page the sidebar does not name, which is every page that is not a step of the documentation. The +-- main page is one of those: it is where a reader arrives rather than a step they walk, and it +-- names its own way on in its opening lines. -- -- Matched on the root title, because on a translation this is "Searching/ko" and the sidebar names -- the page it was translated from. diff --git a/docs/Template:prevnext/doc.wikitext b/docs/Template:prevnext/doc.wikitext index a3f11aa1a..b0e1bba37 100644 --- a/docs/Template:prevnext/doc.wikitext +++ b/docs/Template:prevnext/doc.wikitext @@ -16,8 +16,9 @@ row, because a lone argument read as the *next* step and there was no way to spe this". Adding a page meant three edits, and forgetting the third was invisible. Now it is two: the sidebar, and a {{prevnext}} at the foot of the new page. -index is the exception the module carries: it heads the sequence but is not a sidebar -entry, because every skin already points its logo at the main page. +The main page has no row. It is not a sidebar entry -- every skin already points its logo at it -- +and it is where a reader arrives rather than a step they walk, so it names its own way on in its +opening lines instead. == Translated pages == diff --git a/docs/index.wikitext b/docs/index.wikitext index b1c5df9b6..eb1651038 100644 --- a/docs/index.wikitext +++ b/docs/index.wikitext @@ -54,5 +54,4 @@ These need a live wiki, so they are not part of the static export: What replaces some of them is written down: the reader's own display choices are on a [[Special:MyLanguage/Skins#Settings|Settings page]] the build writes, and searching is [[Special:MyLanguage/Searching|done in the browser]]. -{{prevnext}} {{DISPLAYTITLE:Wikven}} From bfb6a6a184ce371b175f6802f6935a38176eaf09 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 15:15:16 +0000 Subject: [PATCH 3/3] fix: match a translation to its source page without asking for a root title MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The row rendered empty on every page: ##[error]Searching/ko's prevnext does not show Translating's Korean title (번역)
rootText only strips a subpage where the namespace has subpages turned on, and this one does not -- nothing sets $wgNamespacesWithSubpages, so "Searching/ko" is one whole title and matched nothing in a list of source page names. Matched as a prefix of the sidebar's own names instead, so it works either way and does not depend on a setting this repository never made. No sidebar entry is another entry plus a slash, which is what makes the prefix safe. The module now fails loudly when the sidebar yields no entries, rather than returning nothing. An empty row is the invisible failure -- the wrapper div is still there and the page still looks finished -- and it is the failure this module exists to end. Two of the three checks added with it were the same kind of quiet: they matched the wrapper class, which prevnext always emits, so an empty row passed. They match the link div now. Only the Korean-label check, which was already there, caught this. Also renames the module to Module:Sequence.lua, so its file says what it is to an editor. Special:MyLanguage/Lua modules documents that naming without recommending it, and this is the site that gets to try it. --- .github/workflows/smoke.yml | 5 ++- docs/{Module:Sequence => Module:Sequence.lua} | 38 +++++++++++++------ docs/Template:prevnext.wikitext | 4 +- docs/Template:prevnext/doc.wikitext | 2 +- 4 files changed, 34 insertions(+), 15 deletions(-) rename docs/{Module:Sequence => Module:Sequence.lua} (61%) diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index b83e9cafe..42eaf2a26 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -260,7 +260,10 @@ jobs: missing= while IFS= read -r page; do file="dist/$(printf '%s' "$page" | tr ' ' '_').html" - if [ ! -s "$file" ] || ! grep -q 'wikven-prevnext' "$file"; then + # The link div, not the wrapper: prevnext always emits the wrapper, so matching that + # passes an empty row -- which is exactly what a broken module produces, and what the + # first draft of this check waved through. + if [ ! -s "$file" ] || ! grep -q 'class="wikven-prevnext-' "$file"; then missing="$missing $page" fi done < <(sed -n 's@^\*\* *Special:MyLanguage/\([^|]*\).*@\1@p' 'docs/MediaWiki:Sidebar.wikitext') diff --git a/docs/Module:Sequence b/docs/Module:Sequence.lua similarity index 61% rename from docs/Module:Sequence rename to docs/Module:Sequence.lua index b1ac882a4..0f7cdee04 100644 --- a/docs/Module:Sequence +++ b/docs/Module:Sequence.lua @@ -10,6 +10,9 @@ -- -- What the sidebar contributes is order alone. The link labels stay where they were, in -- Template:prevnext/label, which reads each target's own translated title. +-- +-- Named with the .lua marker, which Special:MyLanguage/Lua modules describes without recommending: +-- the page is Module:Sequence.lua, and Template:prevnext spells the invoke that way. local p = {} -- Where the order lives, and the entry line to read out of it: "** Special:MyLanguage/Page|key". @@ -33,24 +36,37 @@ local function sequence() return pages end --- The page on either side of this one, or nil at an end of the sequence -- and nil either way for a --- page the sidebar does not name, which is every page that is not a step of the documentation. The --- main page is one of those: it is where a reader arrives rather than a step they walk, and it --- names its own way on in its opening lines. +-- Where this page sits in the sequence, or nil if the sidebar does not name it -- which is every +-- page that is not a step of the documentation. The main page is one of those: it is where a reader +-- arrives rather than a step they walk, and it names its own way on in its opening lines. -- --- Matched on the root title, because on a translation this is "Searching/ko" and the sidebar names --- the page it was translated from. -local function neighbour(step) - local here = mw.title.getCurrentTitle().rootText - local pages = sequence() +-- A translation counts as its source page: "Searching/ko" is the sidebar's "Searching" in another +-- language, and the sidebar names the page it was translated from. Matched as a prefix rather than +-- by asking the title for its root, because rootText only strips a subpage where the namespace has +-- them turned on, and this one does not -- there, "Searching/ko" is one whole title. Anchoring on +-- the names the sidebar gives keeps that out of it: no entry is another entry plus a slash. +local function positionOf(pages, here) for i, page in ipairs(pages) do - if page == here then - return pages[i + step] + if here == page or here:sub(1, #page + 1) == page .. '/' then + return i end end return nil end +-- The page on either side of this one, or nil at an end of the sequence. +local function neighbour(step) + local pages = sequence() + -- Loud rather than empty. A row that renders as nothing is the failure this module exists to + -- end, and it is the invisible kind: the wrapper div is still there and the page still looks + -- finished. If the sidebar stops being readable, that should stop a build, not reach a reader. + if #pages == 0 then + error(SIDEBAR .. ' has no "** Special:MyLanguage/Page" entries to read the order from', 0) + end + local here = positionOf(pages, mw.title.getCurrentTitle().text) + return here and pages[here + step] or nil +end + local function link(frame, page, before, after) local label = frame:expandTemplate{ title = 'prevnext/label', args = { page } } return before .. '[[Special:MyLanguage/' .. page .. '|' .. label .. ']]' .. after diff --git a/docs/Template:prevnext.wikitext b/docs/Template:prevnext.wikitext index c520b0d54..13fc49e8a 100644 --- a/docs/Template:prevnext.wikitext +++ b/docs/Template:prevnext.wikitext @@ -1,7 +1,7 @@
{{#invoke:Sequence|row}}{{#invoke:Sequence.lua|row}}
{{prevnext/doc}} diff --git a/docs/Template:prevnext/doc.wikitext b/docs/Template:prevnext/doc.wikitext index b0e1bba37..417d3f2a6 100644 --- a/docs/Template:prevnext/doc.wikitext +++ b/docs/Template:prevnext/doc.wikitext @@ -5,7 +5,7 @@ Bottom-of-page navigation between the steps of the documentation, as a row of on {{prevnext}} No arguments. The order of the documentation is [[MediaWiki:Sidebar]], and -Module:Sequence reads it: the page before this one in the sidebar becomes the previous +Module:Sequence.lua reads it: the page before this one in the sidebar becomes the previous link, the page after it the next. The first and last pages of the sequence get one link rather than two, without having to say so, and a page the sidebar does not name gets no row at all.