@docmd/ui 0.4.11 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -176,18 +176,19 @@ a:any-link {
176
176
  .sidebar {
177
177
  display: flex;
178
178
  flex-direction: column;
179
- width: 260px;
179
+ width: var(--sidebar-width);
180
180
  background-color: var(--sidebar-bg);
181
181
  color: var(--sidebar-text);
182
- padding: .5em .25em .5em .5em;
183
182
  border-right: 1px solid var(--border-color);
184
183
  height: 100vh;
185
184
  position: fixed;
185
+ padding: .5em .25em .5em .5em;
186
186
  top: 0;
187
187
  left: 0;
188
- overflow-y: auto;
188
+ overflow: hidden;
189
189
  box-sizing: border-box;
190
- flex-shrink: 0
190
+ flex-shrink: 0;
191
+ z-index: 100;
191
192
  }
192
193
 
193
194
  .sidebar h1 {
@@ -527,7 +528,7 @@ body.sidebar-collapsed .main-content-wrapper {
527
528
  }
528
529
 
529
530
  .sidebar-options-wrapper {
530
- padding: .5rem 0;
531
+ padding: .25rem 0;
531
532
  }
532
533
 
533
534
  .sidebar-options-wrapper.mt-auto {
@@ -536,6 +537,123 @@ body.sidebar-collapsed .main-content-wrapper {
536
537
  border-top: 1px solid var(--border-color);
537
538
  }
538
539
 
540
+ /* --- Version Dropdown (Flat UI) --- */
541
+ .sidebar-version-wrapper {
542
+ padding: 0.25rem .5em;
543
+ border-bottom: 1px solid var(--border-color);
544
+ }
545
+
546
+ .sidebar-bottom-group.mt-auto {
547
+ margin-top: auto;
548
+ }
549
+
550
+ .sidebar-bottom-group .sidebar-version-wrapper {
551
+ border-bottom: none;
552
+ }
553
+
554
+ .docmd-version-dropdown {
555
+ position: relative;
556
+ width: 100%;
557
+ }
558
+
559
+ .version-dropdown-toggle {
560
+ display: flex;
561
+ align-items: center;
562
+ justify-content: space-between;
563
+ width: 100%;
564
+ padding: 0.5rem 0.75rem;
565
+ background-color: var(--bg-color);
566
+ border: 1px solid var(--border-color);
567
+ border-radius: var(--ui-border-radius);
568
+ color: var(--text-heading);
569
+ font-size: 0.85rem;
570
+ font-weight: 500;
571
+ cursor: pointer;
572
+ transition: all 0.2s ease;
573
+ }
574
+
575
+ .version-dropdown-toggle:hover {
576
+ border-color: var(--text-muted);
577
+ }
578
+
579
+ .version-dropdown-menu {
580
+ position: absolute;
581
+ top: calc(100% + 4px);
582
+ left: 0;
583
+ right: 0;
584
+ margin: 0;
585
+ padding: 0.25rem;
586
+ background-color: var(--bg-color);
587
+ border: 1px solid var(--border-color);
588
+ border-radius: var(--ui-border-radius);
589
+ box-shadow: var(--shadow-md);
590
+ list-style: none;
591
+ z-index: 100;
592
+ opacity: 0;
593
+ visibility: hidden;
594
+ transform: translateY(-5px);
595
+ transition: all 0.2s ease;
596
+ }
597
+
598
+ .docmd-version-dropdown.open .version-dropdown-menu {
599
+ opacity: 1;
600
+ visibility: visible;
601
+ transform: translateY(0);
602
+ }
603
+
604
+ .version-dropdown-item {
605
+ display: flex;
606
+ align-items: center;
607
+ justify-content: space-between;
608
+ padding: 0.4rem 0.5rem;
609
+ color: var(--text-color);
610
+ font-size: 0.85rem;
611
+ text-decoration: none;
612
+ border-radius: 4px;
613
+ transition: background-color 0.2s;
614
+ }
615
+
616
+ .version-dropdown-item:hover {
617
+ background-color: var(--sidebar-link-active-bg);
618
+ color: var(--text-heading);
619
+ text-decoration: none;
620
+ }
621
+
622
+ .version-dropdown-item.active {
623
+ font-weight: 600;
624
+ color: var(--link-color);
625
+ background-color: var(--sidebar-link-active-bg);
626
+ }
627
+
628
+ .version-check {
629
+ width: 1rem;
630
+ height: 1rem;
631
+ }
632
+
633
+ .version-chevron {
634
+ width: 1rem;
635
+ height: 1rem;
636
+ transition: transform 0.2s ease;
637
+ }
638
+
639
+ .docmd-version-dropdown.open .version-chevron {
640
+ transform: rotate(180deg);
641
+ }
642
+
643
+ .sidebar-bottom-group .version-dropdown-menu {
644
+ top: auto;
645
+ bottom: calc(100% + 4px);
646
+ transform: translateY(5px);
647
+ }
648
+
649
+ .sidebar-bottom-group .docmd-version-dropdown.open .version-dropdown-menu {
650
+ transform: translateY(0);
651
+ }
652
+
653
+ .sidebar-bottom-group .version-dropdown-toggle .version-chevron {
654
+ transform: rotate(180deg);
655
+ }
656
+
539
657
  .card .card-title {
540
658
  border-bottom: 1px solid var(--border-color)
541
659
  }
@@ -645,6 +763,7 @@ html[data-theme=dark] .theme-toggle-button .icon-sun {
645
763
  .sidebar-nav {
646
764
  flex-grow: 1;
647
765
  overflow-y: auto;
766
+ overflow-x: hidden;
648
767
  min-height: 0;
649
768
  scrollbar-width: thin;
650
769
  }
@@ -11,6 +11,7 @@
11
11
  * [docmd-source] - Please do not remove this header.
12
12
  * --------------------------------------------------------------------
13
13
  */
14
+
14
15
  /**
15
16
  * --------------------------------------------------------------------
16
17
  * docmd : Client-Side Application Logic (SPA Router & UI)
@@ -18,9 +19,8 @@
18
19
  */
19
20
 
20
21
  (function() {
21
- // =========================================================================
22
+
22
23
  // 1. EVENT DELEGATION
23
- // =========================================================================
24
24
  document.addEventListener('click', (e) => {
25
25
  // Collapsible Navigation
26
26
  const navLabel = e.target.closest('.nav-label, .collapse-icon-wrapper');
@@ -61,6 +61,51 @@
61
61
  tabItem.classList.add('active');
62
62
  if (tabPanes[index]) tabPanes[index].classList.add('active');
63
63
  }
64
+
65
+ // Version Dropdown Toggle
66
+ const versionToggle = e.target.closest('.version-dropdown-toggle');
67
+ if (versionToggle) {
68
+ e.preventDefault();
69
+ const dropdown = versionToggle.closest('.docmd-version-dropdown');
70
+ dropdown.classList.toggle('open');
71
+ versionToggle.setAttribute('aria-expanded', dropdown.classList.contains('open'));
72
+ return;
73
+ }
74
+
75
+ // Sticky Version Switching (Path Preservation)
76
+ const versionLink = e.target.closest('.version-dropdown-item');
77
+ if (versionLink) {
78
+ const targetRoot = versionLink.dataset.versionRoot;
79
+ // Use global fallback if undefined (e.g. on 404 pages)
80
+ const currentRoot = window.DOCMD_VERSION_ROOT || '/';
81
+
82
+ if (targetRoot && window.location.pathname) {
83
+ try {
84
+ let currentPath = window.location.pathname;
85
+ const normCurrentRoot = currentRoot.endsWith('/') ? currentRoot : currentRoot + '/';
86
+
87
+ // Only try sticky if we are actually INSIDE the known version path
88
+ if (currentPath.startsWith(normCurrentRoot)) {
89
+ e.preventDefault();
90
+ const suffix = currentPath.substring(normCurrentRoot.length);
91
+ const normTargetRoot = targetRoot.endsWith('/') ? targetRoot : targetRoot + '/';
92
+ window.location.href = normTargetRoot + suffix + window.location.hash;
93
+ return;
94
+ }
95
+ } catch(e) {
96
+ // Ignore errors, let default click happen
97
+ }
98
+ }
99
+ // If sticky logic skipped (e.g. on 404 page or outside root), default <a> click handles it
100
+ }
101
+
102
+ // Close Dropdown if clicked outside
103
+ if (!e.target.closest('.docmd-version-dropdown')) {
104
+ document.querySelectorAll('.docmd-version-dropdown.open').forEach(d => {
105
+ d.classList.remove('open');
106
+ d.querySelector('.version-dropdown-toggle').setAttribute('aria-expanded', 'false');
107
+ });
108
+ }
64
109
 
65
110
  // Copy Code Button
66
111
  const copyBtn = e.target.closest('.copy-code-button');
@@ -79,9 +124,7 @@
79
124
  }
80
125
  });
81
126
 
82
- // =========================================================================
83
127
  // 2. COMPONENT INITIALIZERS
84
- // =========================================================================
85
128
  function injectCopyButtons() {
86
129
  if (document.body.dataset.copyCodeEnabled !== 'true') return;
87
130
  const svg = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>`;
@@ -143,9 +186,7 @@
143
186
  });
144
187
  }
145
188
 
146
- // =========================================================================
147
189
  // 3. TARGETED SPA ROUTER
148
- // =========================================================================
149
190
  function initializeSPA() {
150
191
  if (location.protocol === 'file:') return;
151
192
  if (document.body.dataset.spaEnabled !== 'true') return;
@@ -179,6 +220,8 @@
179
220
  document.addEventListener('click', async (e) => {
180
221
  if (e.target.closest('.collapse-icon-wrapper')) return;
181
222
 
223
+ if (e.target.closest('[data-spa-ignore]')) return;
224
+
182
225
  const link = e.target.closest('.sidebar-nav a, .page-navigation a');
183
226
  if (!link || link.target === '_blank' || link.hasAttribute('download')) return;
184
227
 
@@ -300,9 +343,7 @@
300
343
  }
301
344
  }
302
345
 
303
- // =========================================================================
304
346
  // 4. BOOTSTRAP
305
- // =========================================================================
306
347
  document.addEventListener('DOMContentLoaded', () => {
307
348
  if (localStorage.getItem('docmd-sidebar-collapsed') === 'true') {
308
349
  document.body.classList.add('sidebar-collapsed');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docmd/ui",
3
- "version": "0.4.11",
3
+ "version": "0.5.0",
4
4
  "description": "Base UI templates and assets for docmd",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -0,0 +1,89 @@
1
+ <!--
2
+ ---------------------------------------------------------------
3
+ docmd : the minimalist, zero-config documentation generator.
4
+ @website https://docmd.io
5
+ [docmd-source] - Please do not remove this header.
6
+ ---------------------------------------------------------------
7
+ -->
8
+
9
+ <!DOCTYPE html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="UTF-8">
13
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
14
+ <title><%= pageTitle %></title>
15
+
16
+ <!-- Force absolute paths for assets using relativePathToRoot (which we will set to '/') -->
17
+ <%- faviconLinkHtml || '' %>
18
+
19
+ <!-- 1. Core CSS -->
20
+ <link rel="stylesheet" href="<%= relativePathToRoot %>assets/css/docmd-main.css?v=<%= buildHash %>">
21
+
22
+ <!-- 2. Highlight.js (Theme aware) -->
23
+ <%
24
+ const isDarkDefault = defaultMode === 'dark';
25
+ %>
26
+ <link rel="stylesheet" href="<%= relativePathToRoot %>assets/css/docmd-highlight-light.css?v=<%= buildHash %>" id="hljs-light" <%= isDarkDefault ? 'disabled' : '' %>>
27
+ <link rel="stylesheet" href="<%= relativePathToRoot %>assets/css/docmd-highlight-dark.css?v=<%= buildHash %>" id="hljs-dark" <%= isDarkDefault ? '' : 'disabled' %>>
28
+
29
+ <!-- 3. Theme CSS -->
30
+ <% if (theme && theme.name !== 'default') { %>
31
+ <link rel="stylesheet" href="<%= relativePathToRoot %>assets/css/docmd-theme-<%= theme.name %>.css?v=<%= buildHash %>">
32
+ <% } %>
33
+
34
+ <!-- 4. Custom CSS -->
35
+ <% (customCssFiles || []).forEach(cssFile => {
36
+ const cleanPath = cssFile.startsWith('/') ? cssFile.substring(1) : cssFile;
37
+ %>
38
+ <link rel="stylesheet" href="<%= relativePathToRoot %><%= cleanPath %>?v=<%= buildHash %>">
39
+ <% }); %>
40
+
41
+ <!-- 5. Theme Init (Dark Mode logic) -->
42
+ <%- themeInitScript %>
43
+
44
+ <style>
45
+ body {
46
+ display: flex;
47
+ flex-direction: column;
48
+ align-items: center;
49
+ justify-content: center;
50
+ min-height: 100vh;
51
+ text-align: center;
52
+ padding: 20px;
53
+ background-color: var(--bg-color);
54
+ color: var(--text-color);
55
+ }
56
+ .error-logo { height: 60px; margin-bottom: 2rem; width: auto; display: block; }
57
+ .error-logo.light { display: var(--display-light, block); }
58
+ .error-logo.dark { display: var(--display-dark, none); }
59
+
60
+ /* Simple theme logic for logo if JS fails */
61
+ @media (prefers-color-scheme: dark) {
62
+ :root[data-theme="system"] .error-logo.light { display: none; }
63
+ :root[data-theme="system"] .error-logo.dark { display: block; }
64
+ }
65
+ :root[data-theme="dark"] .error-logo.light { display: none; }
66
+ :root[data-theme="dark"] .error-logo.dark { display: block; }
67
+
68
+ h1 { font-size: 5rem; margin: 0; line-height: 1; color: var(--link-color); font-weight: 700; }
69
+ h2 { font-size: 2rem; margin: 1rem 0; }
70
+ p { font-size: 1.2rem; color: var(--text-muted); margin-bottom: 2rem; max-width: 500px; }
71
+ .docmd-button { font-size: 1.1rem; padding: 0.8rem 1.5rem; }
72
+ </style>
73
+ </head>
74
+ <body>
75
+ <% if (logo) { %>
76
+ <% if (logo.light) { %>
77
+ <img src="<%= relativePathToRoot %><%= logo.light.replace(/^\//, '') %>" class="error-logo light" alt="Logo">
78
+ <% } %>
79
+ <% if (logo.dark) { %>
80
+ <img src="<%= relativePathToRoot %><%= logo.dark.replace(/^\//, '') %>" class="error-logo dark" alt="Logo">
81
+ <% } %>
82
+ <% } %>
83
+
84
+ <h1>404</h1>
85
+ <h2>Page Not Found</h2>
86
+ <p><%= content %></p>
87
+ <a href="<%= relativePathToRoot %>" class="docmd-button">Return Home</a>
88
+ </body>
89
+ </html>
@@ -10,14 +10,40 @@
10
10
  <html lang="en">
11
11
  <head>
12
12
  <meta charset="UTF-8">
13
- <meta name="generator" content="docmd v0.4.x">
13
+ <meta name="generator" content="docmd v0.5.x">
14
14
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
15
+ <%
16
+ let versionRoot = '/';
17
+ let siteRoot = relativePathToRoot;
18
+
19
+ if (config.versions?.current && config._activeVersion?.id) {
20
+ const isSubVersion = config.versions.current !== config._activeVersion.id;
21
+ versionRoot = isSubVersion ? '/' + config._activeVersion.id + '/' : '/';
22
+
23
+ // If we are in a sub-version (v04/), the relativePathToRoot takes us to /v04/.
24
+ // We need to go up one more level to reach the true site root.
25
+ if (isSubVersion) {
26
+ siteRoot = (relativePathToRoot === './' ? '' : relativePathToRoot) + '../';
27
+ }
28
+ }
29
+
30
+ if (locals.isOfflineMode) {
31
+ versionRoot = '';
32
+ }
33
+ %>
15
34
  <script>
16
35
  var root = "<%= relativePathToRoot %>";
17
36
  if (root && !root.endsWith('/')) root += '/';
18
37
  if (root === '') root = './';
19
- window.DOCMD_ROOT = root;
38
+ window.DOCMD_ROOT = root; // Context Root (Assets)
39
+
40
+ var siteRoot = "<%= siteRoot %>";
41
+ if (siteRoot && !siteRoot.endsWith('/')) siteRoot += '/';
42
+ if (siteRoot === '') siteRoot = './';
43
+ window.DOCMD_SITE_ROOT = siteRoot; // True Site Root (Search Index)
44
+
20
45
  window.DOCMD_DEFAULT_MODE = "<%= defaultMode %>";
46
+ window.DOCMD_VERSION_ROOT = "<%- versionRoot %>";
21
47
  </script>
22
48
  <title><%= pageTitle %></title>
23
49
  <%- faviconLinkHtml || '' %>
@@ -41,42 +67,60 @@
41
67
  <% }); %>
42
68
  <%- themeInitScript %>
43
69
  </head>
44
- <body class="<%= sidebarConfig?.collapsible ? 'sidebar-collapsible' : 'sidebar-not-collapsible' %>"
45
- data-default-collapsed="<%= sidebarConfig?.defaultCollapsed %>"
46
- data-copy-code-enabled="<%= config.copyCode === true %>"
47
- data-spa-enabled="<%= config.layout?.spa !== false %>">
70
+ <body class="<%= sidebarConfig?.enabled === false ? 'no-sidebar' : (sidebarConfig?.collapsible ? 'sidebar-collapsible' : 'sidebar-not-collapsible') %>"
71
+ data-default-collapsed="<%= sidebarConfig?.defaultCollapsed %>"
72
+ data-copy-code-enabled="<%= config.copyCode === true %>"
73
+ data-spa-enabled="<%= config.layout?.spa !== false %>">
48
74
 
49
75
  <a href="#main-content" class="skip-link">Skip to main content</a>
50
-
51
- <aside class="sidebar">
52
- <div class="sidebar-header">
53
- <% if (logo && logo.light && logo.dark) { %>
54
- <a href="<%= logo.href || relativePathToRoot %>" class="logo-link">
55
- <img src="<%= relativePathToRoot %><%- logo.light.startsWith('/') ? logo.light.substring(1) : logo.light %>" alt="<%= logo.alt || siteTitle %>" class="logo-light" <% if (logo.height) { %>style="height: <%= logo.height %>;"<% } %>>
56
- <img src="<%= relativePathToRoot %><%- logo.dark.startsWith('/') ? logo.dark.substring(1) : logo.dark %>" alt="<%= logo.alt || siteTitle %>" class="logo-dark" <% if (logo.height) { %>style="height: <%= logo.height %>;"<% } %>>
57
- </a>
58
- <% } else { %>
59
- <h1><a href="<%= relativePathToRoot %>index.html"><%= siteTitle %></a></h1>
60
- <% } %>
61
- <span class="mobile-view sidebar-menu-button float-right">
62
- <%- renderIcon("menu") %>
63
- </span>
64
- </div>
65
76
 
66
- <% if (optionsMenu?.position === 'sidebar-top') { %>
67
- <div class="sidebar-options-wrapper">
68
- <%- include('partials/options-menu', { optionsMenu }) %>
77
+ <% if (sidebarConfig?.enabled !== false) { %>
78
+ <aside class="sidebar">
79
+ <div class="sidebar-header">
80
+ <% if (logo && logo.light && logo.dark) { %>
81
+ <a href="<%= logo.href || relativePathToRoot %>" class="logo-link">
82
+ <img src="<%= relativePathToRoot %><%- logo.light.startsWith('/') ? logo.light.substring(1) : logo.light %>" alt="<%= logo.alt || siteTitle %>" class="logo-light" <% if (logo.height) { %>style="height: <%= logo.height %>;"<% } %>>
83
+ <img src="<%= relativePathToRoot %><%- logo.dark.startsWith('/') ? logo.dark.substring(1) : logo.dark %>" alt="<%= logo.alt || siteTitle %>" class="logo-dark" <% if (logo.height) { %>style="height: <%= logo.height %>;"<% } %>>
84
+ </a>
85
+ <% } else { %>
86
+ <h1><a href="<%= relativePathToRoot %>index.html"><%= siteTitle %></a></h1>
87
+ <% } %>
88
+ <span class="mobile-view sidebar-menu-button float-right">
89
+ <%- renderIcon("menu") %>
90
+ </span>
69
91
  </div>
70
- <% } %>
71
92
 
72
- <%- navigationHtml %>
93
+ <div class="sidebar-top-group">
94
+ <% if (locals.optionsMenu && optionsMenu.position === 'sidebar-top') { %>
95
+ <div class="sidebar-options-wrapper">
96
+ <%- include('partials/options-menu', { optionsMenu }) %>
97
+ </div>
98
+ <% } %>
99
+
100
+ <% if (config.versions && config.versions.position === 'sidebar-top') { %>
101
+ <div class="sidebar-version-wrapper">
102
+ <%- include('partials/version-dropdown', { versions: config.versions, activeVersion: config._activeVersion, relativePathToRoot }) %>
103
+ </div>
104
+ <% } %>
105
+ </div>
106
+
107
+ <%- navigationHtml %>
108
+
109
+ <div class="sidebar-bottom-group mt-auto">
110
+ <% if (config.versions && config.versions.position === 'sidebar-bottom') { %>
111
+ <div class="sidebar-version-wrapper">
112
+ <%- include('partials/version-dropdown', { versions: config.versions, activeVersion: config._activeVersion, relativePathToRoot }) %>
113
+ </div>
114
+ <% } %>
73
115
 
74
- <% if (optionsMenu?.position === 'sidebar-bottom') { %>
75
- <div class="sidebar-options-wrapper mt-auto">
76
- <%- include('partials/options-menu', { optionsMenu }) %>
116
+ <% if (locals.optionsMenu && optionsMenu.position === 'sidebar-bottom') { %>
117
+ <div class="sidebar-options-wrapper">
118
+ <%- include('partials/options-menu', { optionsMenu }) %>
119
+ </div>
120
+ <% } %>
77
121
  </div>
78
- <% } %>
79
- </aside>
122
+ </aside>
123
+ <% } %>
80
124
 
81
125
  <div class="main-content-wrapper">
82
126
  <% if (headerConfig?.enabled !== false) { %>
@@ -10,7 +10,7 @@
10
10
  <html lang="en">
11
11
  <head>
12
12
  <meta charset="UTF-8">
13
- <meta name="generator" content="docmd v0.4.x">
13
+ <meta name="generator" content="docmd v0.5.x">
14
14
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
15
15
 
16
16
  <!-- 1. GLOBALS -->
@@ -40,18 +40,22 @@
40
40
 
41
41
  <div class="footer-complete-bottom">
42
42
  <div class="user-footer"><%- footerHtml || (footerConfig.copyright ? footerConfig.copyright : '') %></div>
43
+ <% if (footerConfig.branding !== false) { %>
43
44
  <div class="branding-footer">
44
45
  Built with <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"></path><path d="M12 5 9.04 7.96a2.17 2.17 0 0 0 0 3.08c.82.82 2.13.85 3 .07l2.07-1.9a2.82 2.82 0 0 1 3.79 0l2.96 2.66"></path><path d="m18 15-2-2"></path><path d="m15 18-2-2"></path></svg> <a href="https://docmd.io" target="_blank" rel="noopener">docmd.</a>
45
46
  </div>
47
+ <% } %>
46
48
  </div>
47
49
  </footer>
48
50
  <% } else { %>
49
51
  <footer class="page-footer">
50
52
  <div class="footer-content">
51
53
  <div class="user-footer"><%- footerHtml || (footerConfig?.copyright ? footerConfig.copyright : '') %></div>
54
+ <% if (footerConfig?.branding !== false) { %>
52
55
  <div class="branding-footer">
53
56
  Built with <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"></path><path d="M12 5 9.04 7.96a2.17 2.17 0 0 0 0 3.08c.82.82 2.13.85 3 .07l2.07-1.9a2.82 2.82 0 0 1 3.79 0l2.96 2.66"></path><path d="m18 15-2-2"></path><path d="m15 18-2-2"></path></svg> <a href="https://docmd.io" target="_blank" rel="noopener">docmd.</a>
54
57
  </div>
58
+ <% } %>
55
59
  </div>
56
60
  </footer>
57
61
  <% } %>
@@ -0,0 +1,50 @@
1
+ <%# ---------------------------------------------------------------
2
+ # docmd : the minimalist, zero-config documentation generator.
3
+ # @website https://docmd.io
4
+ # [docmd-source] - Please do not remove this header.
5
+ # ---------------------------------------------------------------
6
+ %>
7
+
8
+ <% if (versions && versions.all.length > 0) {
9
+ const current = activeVersion || versions.all.find(v => v.id === versions.current);
10
+
11
+ // If we are in a sub-version (e.g. /v1/), we go up one level (../) to get to root.
12
+ const isCurrentSubVersion = current && current.id !== versions.current;
13
+ const upOneLevel = isCurrentSubVersion ? '../' : '';
14
+ %>
15
+ <div class="docmd-version-dropdown">
16
+ <button class="version-dropdown-toggle" aria-expanded="false" aria-label="Select Version">
17
+ <span class="version-label"><%= current ? current.label : 'Version' %></span>
18
+ <%- renderIcon('chevron-down', { class: 'version-chevron' }) %>
19
+ </button>
20
+ <ul class="version-dropdown-menu">
21
+ <% versions.all.forEach(v => {
22
+ const isCurrentActive = current && v.id === current.id;
23
+ const isTargetRootVersion = v.id === versions.current;
24
+
25
+ // We use the 'config' object which is available in all templates
26
+
27
+ const base = config.base || '/';
28
+ const normalizedBase = base.endsWith('/') ? base : base + '/';
29
+
30
+ const targetSuffix = isTargetRootVersion ? '' : v.id + '/';
31
+ const absoluteHref = normalizedBase + targetSuffix;
32
+
33
+ // Data Root for JS Sticky Logic
34
+ const dataRoot = absoluteHref;
35
+ %>
36
+ <li>
37
+ <a href="<%= absoluteHref %>"
38
+ class="version-dropdown-item <%= isCurrentActive ? 'active' : '' %>"
39
+ data-spa-ignore
40
+ data-version-root="<%= dataRoot %>">
41
+ <%= v.label %>
42
+ <% if (isCurrentActive) { %>
43
+ <%- renderIcon('check', { class: 'version-check' }) %>
44
+ <% } %>
45
+ </a>
46
+ </li>
47
+ <% }) %>
48
+ </ul>
49
+ </div>
50
+ <% } %>