From 1a813ff43494d095a47bcaca7d9ab9ff7548d6c3 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 1 Dec 2025 08:27:32 +0100 Subject: [PATCH 1/6] Add version picker in the navbar Fixes: #951 Signed-off-by: Johannes Marbach --- assets/js/versions.js | 78 +++++++++++++++++++ .../internal/newsfragments/2256.clarification | 1 + config/_default/hugo.toml | 4 + layouts/_partials/hooks/body-end.html | 2 + .../_partials/navbar-version-selector.html | 18 +++++ layouts/docs/baseof.html | 19 +++++ 6 files changed, 122 insertions(+) create mode 100644 assets/js/versions.js create mode 100644 changelogs/internal/newsfragments/2256.clarification create mode 100644 layouts/_partials/navbar-version-selector.html diff --git a/assets/js/versions.js b/assets/js/versions.js new file mode 100644 index 00000000..5e39342b --- /dev/null +++ b/assets/js/versions.js @@ -0,0 +1,78 @@ +/* +Copyright 2025 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +function appendVersion(parent, name, url) { + // The list item + const li = document.createElement("li"); + parent.appendChild(li); + + // The link + const a = document.createElement("a"); + a.classList.add("dropdown-item") + a.setAttribute("href", url); + li.appendChild(a); + + // Handle clicks manually to preserve the current path / fragment + a.addEventListener("click", (ev) => { + // If the URL is a relative link (i.e. the historical versions changelog), just + // let the browser load it + if (url.startsWith("/")) { + return; + } + + // Stop further event handling + ev.preventDefault(); + ev.stopPropagation(); + + // Try to find the current version segment + const href = window.location.href; + const matches = href.match(/\/unstable\/|\/latest\/|\/v\d+.\d+\//g); + if (!matches) { + window.location.href = url; + return; + } + + // Replace the segment + window.location.href = href.replace(matches[0], `/${name}/`); + }); + + // The link text + const text = document.createTextNode(name); + a.appendChild(text); +} + +// TODO: Load /latest/versions.json +fetch("/versions.json") + .then(r => r.json()) + .then(versions => { + // Find the surrounding list element + const ul = document.querySelector("ul#version-selector"); + if (!ul) { + console.error("Cannot populate version selector: ul element not found"); + return; + } + + // Add an entry for the unstable version + appendVersion(ul, "unstable", "https://spec.matrix.org/unstable"); + + // Add an entry for each proper version + for (const version of versions) { + appendVersion(ul, version.name, `https://spec.matrix.org/${version.name}`); + } + + // For historical versions, simply link to the changelog + appendVersion(ul, "historical", "/changelog/historical/"); + }); diff --git a/changelogs/internal/newsfragments/2256.clarification b/changelogs/internal/newsfragments/2256.clarification new file mode 100644 index 00000000..468f55d5 --- /dev/null +++ b/changelogs/internal/newsfragments/2256.clarification @@ -0,0 +1 @@ +Add version picker in the navbar. diff --git a/config/_default/hugo.toml b/config/_default/hugo.toml index 4b817cd0..b09b9562 100644 --- a/config/_default/hugo.toml +++ b/config/_default/hugo.toml @@ -78,6 +78,10 @@ current_version_url = "https://spec.matrix.org/latest" # major = "1" # minor = "16" +[[params.versions]] +# We must include this parameter to enable docsy's version picker in the navbar. The picker +# is populated automatically in navbar-version-selector.html. + # User interface configuration [params.ui] # Collapse HTTP API and event
elements diff --git a/layouts/_partials/hooks/body-end.html b/layouts/_partials/hooks/body-end.html index 75997417..c1324c0a 100644 --- a/layouts/_partials/hooks/body-end.html +++ b/layouts/_partials/hooks/body-end.html @@ -8,3 +8,5 @@ */}} {{ $toc := resources.Get "js/toc.js" -}} +{{ $versions := resources.Get "js/versions.js" -}} + diff --git a/layouts/_partials/navbar-version-selector.html b/layouts/_partials/navbar-version-selector.html new file mode 100644 index 00000000..215bca62 --- /dev/null +++ b/layouts/_partials/navbar-version-selector.html @@ -0,0 +1,18 @@ +{{- /* + + A version of the navbar-version-selector.html partial in Docsy, + modified to read the versions from /versions.json. + +*/ -}} + +{{ $changelog := site.GetPage "changelog" }} +{{ $pages := $changelog.RegularPages.ByDate.Reverse }} + + diff --git a/layouts/docs/baseof.html b/layouts/docs/baseof.html index 4f353ce2..b5b7a0f0 100644 --- a/layouts/docs/baseof.html +++ b/layouts/docs/baseof.html @@ -5,6 +5,25 @@ */}} +{{/* Generate a static file versions.json that can be used to populate the version picker */}} +{{ if .IsHome }} + {{- /* Load all changelog subpages, sorted by release date */ -}} + {{ $changelog := site.GetPage "changelog" }} + {{ $pages := $changelog.RegularPages.ByDate.Reverse }} + + {{- /* Collect proper versions and build metadata dicts */ -}} + {{ $versions := slice }} + {{ range $pages }} + {{ if findRE `^v[0-9]+\.[0-9]+$` .Params.linkTitle }} + {{ $versions = $versions | append (dict "name" .Params.linkTitle "date" .Params.date ) }} + {{ end }} + {{ end }} + + {{- /* Generate the JSON */ -}} + {{ $json := jsonify $versions }} + {{ $noop := (resources.FromString "/versions.json" $json).Permalink }} +{{ end }} + Date: Wed, 3 Dec 2025 08:07:20 +0100 Subject: [PATCH 2/6] Convert versions.js to a template to resolve the correct path of /changelog/historical Signed-off-by: Johannes Marbach --- assets/js/{versions.js => versions.template.js} | 2 +- layouts/_partials/hooks/body-end.html | 9 +++++++-- layouts/_partials/navbar-version-selector.html | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) rename assets/js/{versions.js => versions.template.js} (96%) diff --git a/assets/js/versions.js b/assets/js/versions.template.js similarity index 96% rename from assets/js/versions.js rename to assets/js/versions.template.js index 5e39342b..ab66309e 100644 --- a/assets/js/versions.js +++ b/assets/js/versions.template.js @@ -74,5 +74,5 @@ fetch("/versions.json") } // For historical versions, simply link to the changelog - appendVersion(ul, "historical", "/changelog/historical/"); + appendVersion(ul, "historical", '{{ (site.GetPage "changelog/historical").RelPermalink }}'); }); diff --git a/layouts/_partials/hooks/body-end.html b/layouts/_partials/hooks/body-end.html index c1324c0a..3abfe9b4 100644 --- a/layouts/_partials/hooks/body-end.html +++ b/layouts/_partials/hooks/body-end.html @@ -8,5 +8,10 @@ */}} {{ $toc := resources.Get "js/toc.js" -}} -{{ $versions := resources.Get "js/versions.js" -}} - + +{{- /* Load the versions script template, run and publish it */ -}} +{{ with resources.Get "js/versions.template.js" }} + {{ with resources.ExecuteAsTemplate "js/versions.js" $ . }} + + {{ end }} +{{ end }} diff --git a/layouts/_partials/navbar-version-selector.html b/layouts/_partials/navbar-version-selector.html index 215bca62..0a2fb136 100644 --- a/layouts/_partials/navbar-version-selector.html +++ b/layouts/_partials/navbar-version-selector.html @@ -13,6 +13,6 @@ All Versions From eea13b0ff2aa1a8e39df6700962b38d10825b244 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 3 Dec 2025 08:37:51 +0100 Subject: [PATCH 3/6] Color the currently selected version in the picker Signed-off-by: Johannes Marbach --- assets/js/versions.template.js | 15 +++++++++++++++ assets/scss/_styles_project.scss | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/assets/js/versions.template.js b/assets/js/versions.template.js index ab66309e..0083a7fd 100644 --- a/assets/js/versions.template.js +++ b/assets/js/versions.template.js @@ -14,9 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ +// This oddity is an attempt at producing a readable Hugo template while avoiding +// JS syntax errors in your IDE +const currentVersion = `{{ if eq .Site.Params.version.status "unstable" }} + {{- /**/ -}} + unstable + {{- /**/ -}} +{{ else }} + {{- /**/ -}} + {{ printf "v%s.%s" .Site.Params.version.major .Site.Params.version.minor }} + {{- /**/ -}} +{{ end }}`; + function appendVersion(parent, name, url) { // The list item const li = document.createElement("li"); + if (name === currentVersion) { + li.classList.add("selected") + } parent.appendChild(li); // The link diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index 9739f239..62952bd3 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -50,6 +50,10 @@ Custom SCSS for the Matrix spec a { color: $black; } + + ul#version-selector li.selected a { + color: $secondary; + } } /* Styles for the sidebar nav */ From 9170eacfcd7103aa63c19c97247cde46cbe64bad Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 3 Dec 2025 08:45:25 +0100 Subject: [PATCH 4/6] Color the unstable version differently Signed-off-by: Johannes Marbach --- assets/js/versions.template.js | 3 +++ assets/scss/_styles_project.scss | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/assets/js/versions.template.js b/assets/js/versions.template.js index 0083a7fd..3ce9b1a7 100644 --- a/assets/js/versions.template.js +++ b/assets/js/versions.template.js @@ -32,6 +32,9 @@ function appendVersion(parent, name, url) { if (name === currentVersion) { li.classList.add("selected") } + if (name === "unstable") { + li.classList.add("unstable") + } parent.appendChild(li); // The link diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index 62952bd3..7f761d62 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -54,6 +54,10 @@ Custom SCSS for the Matrix spec ul#version-selector li.selected a { color: $secondary; } + + ul#version-selector li.unstable:not(.selected) a { + color: $warning; + } } /* Styles for the sidebar nav */ From 77b37cb9a9e96027200b63ab27e0921a0d125832 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 3 Dec 2025 08:57:06 +0100 Subject: [PATCH 5/6] Limit the dropdown's height and make it scrollable Signed-off-by: Johannes Marbach --- assets/scss/_styles_project.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index 7f761d62..bf9e9757 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -51,6 +51,12 @@ Custom SCSS for the Matrix spec color: $black; } + /* Make the version dropdown scroll if its too large */ + ul#version-selector { + max-height: 80vh; + overflow-y: auto; + } + ul#version-selector li.selected a { color: $secondary; } From 70904ff1a83cdff206afe640604d4ee8f64c4985 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Thu, 4 Dec 2025 08:50:24 +0100 Subject: [PATCH 6/6] Use bold for the select item, remove red for the unstable item and add a latest item in blue Signed-off-by: Johannes Marbach --- assets/js/versions.template.js | 49 +++++++++++++++++++------------- assets/scss/_styles_project.scss | 6 ++-- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/assets/js/versions.template.js b/assets/js/versions.template.js index 3ce9b1a7..a00ac263 100644 --- a/assets/js/versions.template.js +++ b/assets/js/versions.template.js @@ -14,8 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -// This oddity is an attempt at producing a readable Hugo template while avoiding -// JS syntax errors in your IDE +// Determine the current version as defined in hugo.toml. This will either be +// "unstable" or "vX.X" and doesn't depend on the current URL. +// +// The oddity below is an attempt at producing a readable Hugo template while +// avoiding JS syntax errors in your IDE. const currentVersion = `{{ if eq .Site.Params.version.status "unstable" }} {{- /**/ -}} unstable @@ -26,20 +29,31 @@ const currentVersion = `{{ if eq .Site.Params.version.status "unstable" }} {{- /**/ -}} {{ end }}`; +// Determine the current version segment by regex matching the URL. This will either +// be "unstable", "latest", "vX.X" (production) or undefined (local & netlify). +const href = window.location.href; +const segmentMatches = href.match(/(?<=\/)unstable|latest|v\d+.\d+(?=\/)/); +const currentSegment = segmentMatches ? segmentMatches[0] : undefined; + +// Determine the selected menu element. If we were able to obtain the version +// segment from the URL (production), use that. Otherwise (local & netlify), +// fall back to the version as defined in Hugo. +const selected = currentSegment ?? currentVersion; + function appendVersion(parent, name, url) { // The list item const li = document.createElement("li"); - if (name === currentVersion) { - li.classList.add("selected") + if (name === selected) { + li.classList.add("selected"); } - if (name === "unstable") { - li.classList.add("unstable") + if (name === "latest") { + li.classList.add("latest"); } parent.appendChild(li); // The link const a = document.createElement("a"); - a.classList.add("dropdown-item") + a.classList.add("dropdown-item"); a.setAttribute("href", url); li.appendChild(a); @@ -51,20 +65,16 @@ function appendVersion(parent, name, url) { return; } - // Stop further event handling - ev.preventDefault(); - ev.stopPropagation(); - - // Try to find the current version segment - const href = window.location.href; - const matches = href.match(/\/unstable\/|\/latest\/|\/v\d+.\d+\//g); - if (!matches) { - window.location.href = url; + // If we couldn't determine the current segment, we cannot safely replace + // it and have to let the browser load the (root) URL instead + if (!currentSegment) { return; } - // Replace the segment - window.location.href = href.replace(matches[0], `/${name}/`); + // Otherwise, stop further event handling and replace the segment + ev.preventDefault(); + ev.stopPropagation(); + window.location.href = href.replace(`/${currentSegment}/`, `/${name}/`); }); // The link text @@ -83,8 +93,9 @@ fetch("/versions.json") return; } - // Add an entry for the unstable version + // Add a entries for the unstable version and the "latest" shortcut appendVersion(ul, "unstable", "https://spec.matrix.org/unstable"); + appendVersion(ul, "latest", "https://spec.matrix.org/latest"); // Add an entry for each proper version for (const version of versions) { diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index bf9e9757..ce0ce0ee 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -58,11 +58,11 @@ Custom SCSS for the Matrix spec } ul#version-selector li.selected a { - color: $secondary; + font-weight: bold; } - ul#version-selector li.unstable:not(.selected) a { - color: $warning; + ul#version-selector li.latest a { + color: $secondary; } }