Skip to content

Commit 8a58ffa

Browse files
committed
docs: implement deep re-routing, pagination, and migration banner
1 parent a2eb7be commit 8a58ffa

File tree

8 files changed

+311
-4
lines changed

8 files changed

+311
-4
lines changed

.hugo/hugo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ ignoreFiles = ["quickstart/shared", "quickstart/python", "quickstart/js", "quick
3737
github_subdir = "docs"
3838
offlineSearch = true
3939
version_menu = "Releases"
40+
releases_url = "/genai-toolbox/releases.releases"
41+
global_logo_url = "/genai-toolbox/"
4042
[params.ui]
4143
ul_show = 100
4244
showLightDarkModeMenu = true

.hugo/layouts/docs/section.html

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{{ define "main" }}
2+
<div class="td-content">
3+
<h1>{{ .Title }}</h1>
4+
{{ with .Params.description }}<div class="lead">{{ . | markdownify }}</div>{{ end }}
5+
<header class="article-meta">
6+
{{ partial "taxonomy_terms_article_wrapper.html" . }}
7+
{{ if (and (not .Params.hide_readingtime) (.Site.Params.ui.readingtime.enable)) }}
8+
{{ partial "reading-time.html" . }}
9+
{{ end }}
10+
</header>
11+
{{ .Content }}
12+
{{ partial "section-index.html" . }}
13+
{{ partial "pager.html" . }}
14+
{{ if (and (not .Params.hide_feedback) (.Site.Params.ui.feedback.enable) (.Site.GoogleAnalytics)) }}
15+
{{ partial "feedback.html" .Site.Params.ui.feedback }}
16+
<br />
17+
{{ end }}
18+
{{ if (.Site.Params.DisqusShortname) }}
19+
<br />
20+
{{ partial "disqus-comment.html" . }}
21+
{{ end }}
22+
{{ partial "page-meta-lastmod.html" . }}
23+
</div>
24+
{{ end }}

.hugo/layouts/docs/single.html

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{{ define "main" }}
2+
<div class="td-content">
3+
<h1>{{ .Title }}</h1>
4+
{{ with .Params.description }}<div class="lead">{{ . | markdownify }}</div>{{ end }}
5+
<header class="article-meta">
6+
{{ partial "taxonomy_terms_article_wrapper.html" . }}
7+
{{ if (and (not .Params.hide_readingtime) (.Site.Params.ui.readingtime.enable)) }}
8+
{{ partial "reading-time.html" . }}
9+
{{ end }}
10+
</header>
11+
{{ .Content }}
12+
{{ partial "section-index.html" . }}
13+
{{ partial "pager.html" . }}
14+
{{ if (and (not .Params.hide_feedback) (.Site.Params.ui.feedback.enable) (.Site.GoogleAnalytics)) }}
15+
{{ partial "feedback.html" .Site.Params.ui.feedback }}
16+
<br />
17+
{{ end }}
18+
{{ if (.Site.Params.DisqusShortname) }}
19+
<br />
20+
{{ partial "disqus-comment.html" . }}
21+
{{ end }}
22+
{{ partial "page-meta-lastmod.html" . }}
23+
</div>
24+
{{ end }}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script>
2+
document.addEventListener("DOMContentLoaded", function() {
3+
const logoLink = document.querySelector('.navbar-brand');
4+
if (logoLink) {
5+
logoLink.href = "{{ .Site.Params.global_logo_url | default "/" }}";
6+
}
7+
});
8+
</script>
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
<script src='{{ .Site.BaseURL }}js/w3.js' type="application/x-javascript"></script>
1+
<script src='{{ "js/w3.js" | relURL }}'></script>
2+
{{ if not .Site.Params.disableMigrationBanner }}
3+
<script src="/js/migration-banner.js"></script>
4+
{{ end }}

.hugo/layouts/partials/navbar-version-selector.html

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,53 @@
22
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
33
{{ .Site.Params.version_menu }}
44
</a>
5-
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
6-
<div w3-include-html="/genai-toolbox/releases.releases" w3-include-html-default='<a class="dropdown-item" href="/genai-toolbox/dev/">Dev</a>'></div>
5+
<div class="dropdown-menu" id="version-dropdown-menu" aria-labelledby="navbarDropdown">
6+
<div w3-include-html="{{ .Site.Params.releases_url }}" w3-include-html-default='<a class="dropdown-item" href="{{ .Site.Params.global_logo_url }}dev/">Dev</a>'></div>
7+
78
<script>
8-
// This must run after the w3.js script has loaded.
99
w3.includeHTML();
10+
11+
const basePath = "{{ site.BaseURL | relURL }}";
12+
13+
function stripBase(path) {
14+
if (path.startsWith(basePath)) return path.substring(basePath.length);
15+
if (basePath === "/" && path.startsWith("/")) return path.substring(1);
16+
return path.replace(/^\//, '');
17+
}
18+
19+
document.getElementById('version-dropdown-menu').addEventListener('click', function(e) {
20+
const link = e.target.closest('a.dropdown-item');
21+
if (!link) return;
22+
e.preventDefault();
23+
24+
let targetPath = link.pathname;
25+
if (!targetPath.endsWith('/')) targetPath += '/';
26+
27+
let cleanCurrentPath = stripBase(window.location.pathname);
28+
29+
const allLinks = document.querySelectorAll('#version-dropdown-menu a.dropdown-item');
30+
let currentVersionPrefix = "";
31+
32+
allLinks.forEach(a => {
33+
let cleanVPath = stripBase(a.pathname);
34+
if (!cleanVPath.endsWith('/')) cleanVPath += '/';
35+
36+
if (cleanVPath !== "" && cleanVPath !== "/" && cleanCurrentPath.startsWith(cleanVPath)) {
37+
if (cleanVPath.length > currentVersionPrefix.length) {
38+
currentVersionPrefix = cleanVPath;
39+
}
40+
}
41+
});
42+
43+
let deepPath = cleanCurrentPath;
44+
if (currentVersionPrefix !== "") {
45+
deepPath = cleanCurrentPath.substring(currentVersionPrefix.length);
46+
}
47+
deepPath = deepPath.replace(/^\//, '');
48+
49+
// Execute the deep route
50+
window.location.href = targetPath + deepPath;
51+
});
1052
</script>
1153
</div>
1254
{{ end -}}

.hugo/layouts/partials/pager.html

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
{{ $curr := . }}
2+
{{ $next := "" }}
3+
{{ $prev := "" }}
4+
5+
{{/* 1. Calculate PREVIOUS */}}
6+
{{ if .Parent }}
7+
{{ $siblings := .Parent.Pages.ByWeight }}
8+
{{ $currIndex := -1 }}
9+
10+
{{ range $index, $page := $siblings }}
11+
{{ if eq $page.RelPermalink $curr.RelPermalink }}
12+
{{ $currIndex = $index }}
13+
{{ end }}
14+
{{ end }}
15+
16+
{{ if gt $currIndex 0 }}
17+
{{/* Preceding sibling (if it's a folder, this naturally points to its _index.md!) */}}
18+
{{ $prev = index $siblings (sub $currIndex 1) }}
19+
{{ else }}
20+
{{/* First child, so Previous points up to the Parent folder */}}
21+
{{ if ne .Parent.Type "home" }}
22+
{{ $prev = .Parent }}
23+
{{ end }}
24+
{{ end }}
25+
{{ end }}
26+
27+
28+
{{/* 2. Calculate NEXT */}}
29+
{{ if and .IsNode (gt (len .Pages) 0) }}
30+
{{/* If it's a folder with children, Next dives into the first child */}}
31+
{{ $next = index .Pages.ByWeight 0 }}
32+
{{ else }}
33+
{{/* Leaf page, or empty folder */}}
34+
{{ if .Parent }}
35+
{{ $siblings := .Parent.Pages.ByWeight }}
36+
{{ $currIndex := -1 }}
37+
38+
{{ range $index, $page := $siblings }}
39+
{{ if eq $page.RelPermalink $curr.RelPermalink }}
40+
{{ $currIndex = $index }}
41+
{{ end }}
42+
{{ end }}
43+
44+
{{ if lt $currIndex (sub (len $siblings) 1) }}
45+
{{/* Next sibling in the same folder */}}
46+
{{ $next = index $siblings (add $currIndex 1) }}
47+
{{ else }}
48+
{{/* Last item in folder, step out and find the Parent's next sibling */}}
49+
{{ $p := .Parent }}
50+
{{ $foundNext := false }}
51+
52+
{{/* Check up to 3 directory levels up to find the next section */}}
53+
{{ range seq 3 }}
54+
{{ if and (not $foundNext) $p }}
55+
{{ if $p.Parent }}
56+
{{ $pSiblings := $p.Parent.Pages.ByWeight }}
57+
{{ $pIndex := -1 }}
58+
{{ range $index, $page := $pSiblings }}
59+
{{ if eq $page.RelPermalink $p.RelPermalink }}
60+
{{ $pIndex = $index }}
61+
{{ end }}
62+
{{ end }}
63+
64+
{{ if and (ge $pIndex 0) (lt $pIndex (sub (len $pSiblings) 1)) }}
65+
{{ $next = index $pSiblings (add $pIndex 1) }}
66+
{{ $foundNext = true }}
67+
{{ else }}
68+
{{ $p = $p.Parent }}
69+
{{ end }}
70+
{{ else }}
71+
{{ $p = false }}
72+
{{ end }}
73+
{{ end }}
74+
{{ end }}
75+
76+
{{ end }}
77+
{{ end }}
78+
{{ end }}
79+
80+
<!-- 3. Render the Buttons -->
81+
{{ if or $prev $next }}
82+
<nav class="mt-5 pt-4 border-top d-flex justify-content-between" aria-label="Page navigation">
83+
84+
<div>
85+
{{ with $prev }}
86+
<a href="{{ .RelPermalink }}" class="text-decoration-none">
87+
<small class="text-muted d-block mb-1">&laquo; Previous</small>
88+
<span class="text-body">{{ .Title }}</span>
89+
</a>
90+
{{ end }}
91+
</div>
92+
93+
<div class="text-end">
94+
{{ with $next }}
95+
<a href="{{ .RelPermalink }}" class="text-decoration-none">
96+
<small class="text-muted d-block mb-1">Next &raquo;</small>
97+
<span class="text-body">{{ .Title }}</span>
98+
</a>
99+
{{ end }}
100+
</div>
101+
102+
</nav>
103+
{{ end }}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
document.addEventListener('DOMContentLoaded', function() {
2+
// Trigger your releases dropdown
3+
if (typeof w3 !== 'undefined') { w3.includeHTML(); }
4+
5+
// Setup CSS for the wrapper and the banner
6+
var styleTag = document.createElement('style');
7+
styleTag.innerHTML = `
8+
.td-navbar .dropdown-menu {
9+
z-index: 9999 !important;
10+
}
11+
12+
.theme-banner-wrapper {
13+
position: sticky;
14+
z-index: 20;
15+
padding-top: 15px; /* This is your gap! */
16+
padding-bottom: 5px; /* Breathing room below the banner */
17+
/* Uses Bootstrap's native body background variable, with white as fallback */
18+
background-color: var(--bs-body-bg, #ffffff);
19+
}
20+
21+
.theme-migration-banner {
22+
background-color: #ebf3fc;
23+
border: 1px solid #80a7e9;
24+
color: #1c3a6b;
25+
border-radius: 4px;
26+
padding: 15px;
27+
text-align: center;
28+
width: 100%;
29+
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
30+
}
31+
32+
.theme-migration-banner a {
33+
color: #4484f4;
34+
text-decoration: underline;
35+
font-weight: bold;
36+
}
37+
38+
/* DARK MODE STYLING */
39+
html[data-bs-theme="dark"] .theme-banner-wrapper,
40+
body.dark .theme-banner-wrapper,
41+
html.dark-mode .theme-banner-wrapper {
42+
/* Uses Docsy's dark mode background fallback if var fails */
43+
background-color: var(--bs-body-bg, #20252b);
44+
}
45+
46+
html[data-bs-theme="dark"] .theme-migration-banner,
47+
body.dark .theme-migration-banner,
48+
html.dark-mode .theme-migration-banner {
49+
background-color: #1a273b;
50+
color: #e6efff;
51+
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
52+
}
53+
54+
html[data-bs-theme="dark"] .theme-migration-banner a,
55+
body.dark .theme-migration-banner a,
56+
html.dark-mode .theme-migration-banner a {
57+
color: #80a7e9;
58+
}
59+
60+
/* Fallback for OS-level dark mode */
61+
@media (prefers-color-scheme: dark) {
62+
html:not([data-bs-theme="light"]):not(.light) .theme-banner-wrapper {
63+
background-color: var(--bs-body-bg, #20252b);
64+
}
65+
html:not([data-bs-theme="light"]):not(.light) .theme-migration-banner {
66+
background-color: #1a273b;
67+
color: #e6efff;
68+
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
69+
}
70+
html:not([data-bs-theme="light"]):not(.light) .theme-migration-banner a {
71+
color: #80a7e9;
72+
}
73+
}
74+
`;
75+
document.head.appendChild(styleTag);
76+
77+
// Create the Wrapper
78+
var wrapper = document.createElement('div');
79+
wrapper.id = 'migration-banner-wrapper';
80+
wrapper.className = 'theme-banner-wrapper';
81+
82+
// Create the Banner
83+
var banner = document.createElement('div');
84+
banner.className = 'theme-migration-banner';
85+
banner.innerHTML = '⚠️ <strong>Archived Docs:</strong> Visit <a href="https://mcp-toolbox.dev/">mcp-toolbox.dev</a> for the latest version.';
86+
wrapper.appendChild(banner);
87+
88+
// Inject the wrapper into the center information column
89+
var contentArea = document.querySelector('.td-content') || document.querySelector('main');
90+
if (contentArea) {
91+
contentArea.prepend(wrapper);
92+
} else {
93+
console.warn("Could not find the main content column to inject the banner.");
94+
}
95+
96+
setTimeout(function() {
97+
var navbar = document.querySelector('.td-navbar');
98+
var navbarHeight = navbar ? navbar.offsetHeight : 64;
99+
wrapper.style.top = navbarHeight + 'px';
100+
}, 50);
101+
});

0 commit comments

Comments
 (0)