From f6e62706c6c6fac30202e73710ae2910b1d4676e Mon Sep 17 00:00:00 2001 From: Bryant Date: Tue, 23 Jun 2026 08:14:21 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=90=9B=20(docs):=20Always=20reveal=20?= =?UTF-8?q?feedback=20widget,=20keep=20GA=20event=20consent-gated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'Was this page helpful?' widget (AAASM-3555) was only revealed and wired by mkdocs-material's __md_analytics(), which runs only after analytics consent is granted. With opt-in consent (off by default) the widget stayed hidden for all ordinary visitors. Add a document$-driven script in the theme override that always reveals the widget and wires the 👍/👎 handlers (thank-you note + issue link), without calling gtag — Material's own handler still sends the GA feedback event only when consent is granted, preserving the GDPR posture and avoiding double-counting. Refs AAASM-3557. Fixes AAASM-3555 regression. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/_overrides/main.html | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/_overrides/main.html b/docs/_overrides/main.html index 5490c824..dd14ae5e 100644 --- a/docs/_overrides/main.html +++ b/docs/_overrides/main.html @@ -103,3 +103,44 @@ })(); {% endblock %} + +{#- + Feedback widget decoupling (AAASM-3557, fixes the AAASM-3555 regression). + + Material only reveals `form.md-feedback` (form.hidden = false) and wires the + 👍/👎 click handlers from inside its generated `__md_analytics()`, which runs + only after analytics consent is granted. With opt-in consent (off by default) + ordinary visitors never see the widget. + + This script reveals the widget for everyone and wires our own handlers so the + thank-you note / "open an issue" link works without consent. It deliberately + does NOT call gtag: when consent IS granted, Material's own handler sends the + GA `feedback` event. Keeping the event in Material's handler preserves the + GDPR posture (event stays consent-gated) and avoids double-counting. +-#} +{% block scripts %} + {{ super() }} + +{% endblock %} From deaadacb956cbe93c61ebb50f891853f2b87e515 Mon Sep 17 00:00:00 2001 From: Bryant Date: Tue, 23 Jun 2026 08:14:29 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=90=9B=20(docs):=20Fix=20new=20URL(".?= =?UTF-8?q?.")=20base=20arg=20in=20outdated=20banner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Material's generated outdated-banner reveal script calls new URL("{{ base_url }}") with no base argument; base_url is a bare relative value like "." or "..", so new URL("..") throws 'TypeError: Failed to construct URL: Invalid URL' on every page load. Override partials/javascripts/outdated.html to pass location as the base (new URL(base_url, location)) instead of forking the installed material package. Behaviour is otherwise identical. Refs AAASM-3557. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/_overrides/partials/javascripts/outdated.html | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 docs/_overrides/partials/javascripts/outdated.html diff --git a/docs/_overrides/partials/javascripts/outdated.html b/docs/_overrides/partials/javascripts/outdated.html new file mode 100644 index 00000000..17ca0c3c --- /dev/null +++ b/docs/_overrides/partials/javascripts/outdated.html @@ -0,0 +1,13 @@ +{#- + Override of Material's partials/javascripts/outdated.html (AAASM-3557). + + Material's generated script calls `new URL("{{ base_url }}")` with no base + argument. On many pages `base_url` is a bare relative value such as `.` or + `..`, which is not an absolute URL, so `new URL("..")` throws + `TypeError: Failed to construct 'URL': Invalid URL` on every page load. + + The only change here is passing `location` as the base so the relative + `base_url` resolves against the current page. Behaviour is otherwise identical + to Material's generated script. +-#} + From ef5e4268970c35597b4e4ac5102ca2a3bc16e0cf Mon Sep 17 00:00:00 2001 From: Bryant Date: Tue, 23 Jun 2026 08:14:42 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=90=9B=20(docs):=20Fix=20versions.jso?= =?UTF-8?q?n=20path=20for=20deep=20pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The version-banner script built versionsUrl by popping only the page's last path segment, so on deep pages it requested ///versions.json (404) instead of the deploy-root //versions.json (200). Resolve the URL from the rendered base_url (the deploy root, correct at any page depth) and read ../versions.json relative to it, so the fetch always hits the deploy-root versions.json. Refs AAASM-3557. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/_overrides/main.html | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/_overrides/main.html b/docs/_overrides/main.html index dd14ae5e..88b213b7 100644 --- a/docs/_overrides/main.html +++ b/docs/_overrides/main.html @@ -33,13 +33,19 @@ : document.querySelector("[data-aaasm-outdated]"); if (!host) return; - // Derive the current version directory and the versions.json URL from the - // page location: mike serves each version under ///, with - // versions.json one level up at //versions.json. - var path = window.location.pathname.replace(/\/+$/, ""); - var segments = path.split("/"); - var current = segments.pop() || ""; - var versionsUrl = segments.join("/") + "/versions.json"; + // mike serves each version under ///, with versions.json + // one level up at //versions.json. mkdocs renders `base_url` so it + // points at the deploy root (the current version directory) from *any* + // page depth, so the deploy-root versions.json is reliably one level + // above it. Resolving the bare relative `base_url` against `location` + // yields an absolute URL; popping only the page's last path segment (the + // previous approach) 404s on deep pages. + var versionRoot = new URL("{{ base_url }}/", window.location); + var versionsUrl = new URL("../versions.json", versionRoot).toString(); + + // The current version id is the last path segment of the deploy root. + var current = + versionRoot.pathname.replace(/\/+$/, "").split("/").pop() || ""; fetch(versionsUrl, { cache: "no-store" }) .then(function (r) { return r.ok ? r.json() : []; })