feat(mdbook): version-picker always renders a label
Previously the picker silently hid when the URL didn't contain a /<version>/ segment — which is exactly the case for `just build` opened via file://. The reader saw no version indicator at all. Now the picker always mounts a "version: latest" label first (immediately, before any fetch). When a versions.json is reachable AND we have a real version segment in the URL (so navigation is computable), the label upgrades into the existing dropdown. On file:// or single-version sites the static label stays. CSS gains styling for the static "version: <name>" name span. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2 files changed
+68
-28
No files matched your search
@@ -30,6 +30,14 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.version-picker-name {
|
||||||
|
color: var(--fg);
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0.2em 0.45em;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
.version-picker-floating {
|
.version-picker-floating {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0.5em;
|
top: 0.5em;
|
||||||
|
|||||||
+60
-28
@@ -1,15 +1,11 @@
|
|||||||
// version-picker.js
|
// version-picker.js
|
||||||
//
|
//
|
||||||
// Renders a version dropdown in the mdBook menu bar for multi-version
|
// Shows the current spec version in the mdBook menu bar. On a deployed
|
||||||
// deployments. Reads /<base>/versions.json at page load and lets the
|
// multi-version site (path like <base>/<version>/<page>) it upgrades
|
||||||
// reader switch to the same page in a different version.
|
// into a dropdown that switches between versions. On a single-version
|
||||||
//
|
// or local build (file:// or no versions.json reachable) it just
|
||||||
// The deployed URL layout is <base>/<version>/<page>, where <version>
|
// renders a static "version: <name>" label so the reader can always
|
||||||
// is "latest" or a semver-like tag (e.g., "v0.1.0"). When the reader
|
// see which version they're reading.
|
||||||
// chooses a different version, the picker navigates to the matching
|
|
||||||
// page under the new version subtree; if that page doesn't exist in
|
|
||||||
// the target version, the browser will show a 404, which is fine for
|
|
||||||
// a v0.1 deployment.
|
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
@@ -17,6 +13,10 @@
|
|||||||
// Match the version segment in a path. Accepts "latest" or "vX[.Y[.Z]][-pre]".
|
// Match the version segment in a path. Accepts "latest" or "vX[.Y[.Z]][-pre]".
|
||||||
var VERSION_RE = /\/(latest|v\d+(?:\.\d+){0,2}(?:-[A-Za-z0-9.]+)?)(\/|$)/;
|
var VERSION_RE = /\/(latest|v\d+(?:\.\d+){0,2}(?:-[A-Za-z0-9.]+)?)(\/|$)/;
|
||||||
|
|
||||||
|
function labelFor(version) {
|
||||||
|
return (version === 'latest') ? 'latest (main)' : version;
|
||||||
|
}
|
||||||
|
|
||||||
function parseLocation() {
|
function parseLocation() {
|
||||||
var path = window.location.pathname;
|
var path = window.location.pathname;
|
||||||
var m = path.match(VERSION_RE);
|
var m = path.match(VERSION_RE);
|
||||||
@@ -28,16 +28,35 @@
|
|||||||
return { version: version, basePath: basePath, pagePath: pagePath };
|
return { version: version, basePath: basePath, pagePath: pagePath };
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildPicker(versions, current, basePath, pagePath) {
|
function buildLabel(version) {
|
||||||
|
var wrap = document.createElement('div');
|
||||||
|
wrap.className = 'version-picker version-picker-label-only';
|
||||||
|
wrap.setAttribute('role', 'group');
|
||||||
|
wrap.setAttribute('aria-label', 'Spec version');
|
||||||
|
|
||||||
|
var prefix = document.createElement('span');
|
||||||
|
prefix.className = 'version-picker-label';
|
||||||
|
prefix.textContent = 'version:';
|
||||||
|
wrap.appendChild(prefix);
|
||||||
|
|
||||||
|
var name = document.createElement('span');
|
||||||
|
name.className = 'version-picker-name';
|
||||||
|
name.textContent = labelFor(version);
|
||||||
|
wrap.appendChild(name);
|
||||||
|
|
||||||
|
return wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDropdown(versions, current, basePath, pagePath) {
|
||||||
var wrap = document.createElement('div');
|
var wrap = document.createElement('div');
|
||||||
wrap.className = 'version-picker';
|
wrap.className = 'version-picker';
|
||||||
wrap.setAttribute('role', 'group');
|
wrap.setAttribute('role', 'group');
|
||||||
wrap.setAttribute('aria-label', 'Spec version');
|
wrap.setAttribute('aria-label', 'Spec version');
|
||||||
|
|
||||||
var label = document.createElement('span');
|
var prefix = document.createElement('span');
|
||||||
label.className = 'version-picker-label';
|
prefix.className = 'version-picker-label';
|
||||||
label.textContent = 'version:';
|
prefix.textContent = 'version:';
|
||||||
wrap.appendChild(label);
|
wrap.appendChild(prefix);
|
||||||
|
|
||||||
var select = document.createElement('select');
|
var select = document.createElement('select');
|
||||||
select.className = 'version-picker-select';
|
select.className = 'version-picker-select';
|
||||||
@@ -47,7 +66,7 @@
|
|||||||
var v = versions[i];
|
var v = versions[i];
|
||||||
var opt = document.createElement('option');
|
var opt = document.createElement('option');
|
||||||
opt.value = v;
|
opt.value = v;
|
||||||
opt.textContent = (v === 'latest') ? 'latest (main)' : v;
|
opt.textContent = labelFor(v);
|
||||||
if (v === current) opt.selected = true;
|
if (v === current) opt.selected = true;
|
||||||
select.appendChild(opt);
|
select.appendChild(opt);
|
||||||
}
|
}
|
||||||
@@ -62,8 +81,29 @@
|
|||||||
return wrap;
|
return wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mount(picker) {
|
||||||
|
var slot = document.querySelector('.right-buttons');
|
||||||
|
if (slot) {
|
||||||
|
slot.insertBefore(picker, slot.firstChild);
|
||||||
|
} else {
|
||||||
|
picker.classList.add('version-picker-floating');
|
||||||
|
document.body.appendChild(picker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function install() {
|
function install() {
|
||||||
var loc = parseLocation();
|
var loc = parseLocation();
|
||||||
|
var current = loc ? loc.version : 'latest';
|
||||||
|
|
||||||
|
// Always mount the label first — visible immediately, even before
|
||||||
|
// (or without) a versions.json fetch.
|
||||||
|
var picker = buildLabel(current);
|
||||||
|
mount(picker);
|
||||||
|
|
||||||
|
// Only attempt the upgrade-to-dropdown path when we have a real
|
||||||
|
// version segment in the URL. Without one (e.g., file:// or local
|
||||||
|
// `just build` opened directly), we can't compute navigation
|
||||||
|
// targets, so leave the label as-is.
|
||||||
if (!loc) return;
|
if (!loc) return;
|
||||||
|
|
||||||
fetch(loc.basePath + 'versions.json', { cache: 'no-cache' })
|
fetch(loc.basePath + 'versions.json', { cache: 'no-cache' })
|
||||||
@@ -74,21 +114,13 @@
|
|||||||
// ["latest", "v0.1.0"]
|
// ["latest", "v0.1.0"]
|
||||||
// { "versions": ["latest", "v0.1.0"] }
|
// { "versions": ["latest", "v0.1.0"] }
|
||||||
var versions = Array.isArray(data) ? data : (data && data.versions);
|
var versions = Array.isArray(data) ? data : (data && data.versions);
|
||||||
if (!versions || !versions.length) return;
|
if (!versions || versions.length < 2) return;
|
||||||
|
|
||||||
var picker = buildPicker(versions, loc.version, loc.basePath, loc.pagePath);
|
var dropdown = buildDropdown(versions, current, loc.basePath, loc.pagePath);
|
||||||
|
picker.replaceWith(dropdown);
|
||||||
// Prefer slotting into mdBook's menu bar; fall back to floating.
|
|
||||||
var slot = document.querySelector('.right-buttons');
|
|
||||||
if (slot) {
|
|
||||||
slot.insertBefore(picker, slot.firstChild);
|
|
||||||
} else {
|
|
||||||
picker.classList.add('version-picker-floating');
|
|
||||||
document.body.appendChild(picker);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
// Non-fatal: dev / file:// / pre-deploy will all land here.
|
// Non-fatal: keep the label-only widget mounted.
|
||||||
if (window.console) console.warn('version-picker:', err);
|
if (window.console) console.warn('version-picker:', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user