@mgks/docmd 0.2.0 → 0.2.2
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.
- package/assets/css/welcome.css +6 -66
- package/config.js +14 -5
- package/docs/configuration.md +15 -6
- package/docs/content/containers/steps.md +2 -2
- package/docs/content/custom-containers.md +24 -0
- package/docs/content/no-style-example.md +2 -0
- package/docs/content/no-style-pages.md +52 -28
- package/docs/index.md +49 -18
- package/docs/plugins/seo.md +80 -33
- package/package.json +13 -7
- package/src/assets/css/docmd-main.css +6 -1168
- package/src/assets/css/docmd-theme-retro.css +3 -806
- package/src/assets/css/docmd-theme-ruby.css +7 -617
- package/src/assets/css/docmd-theme-sky.css +7 -650
- package/src/assets/js/docmd-image-lightbox.js +5 -1
- package/src/assets/js/docmd-main.js +164 -29
- package/src/commands/build.js +75 -134
- package/src/commands/dev.js +2 -1
- package/src/commands/init.js +6 -1
- package/src/core/config-loader.js +2 -0
- package/src/core/file-processor.js +147 -104
- package/src/core/html-generator.js +31 -12
- package/src/core/icon-renderer.js +3 -2
- package/src/plugins/analytics.js +5 -1
- package/src/plugins/seo.js +114 -66
- package/src/plugins/sitemap.js +6 -0
- package/src/templates/layout.ejs +8 -2
- package/src/templates/navigation.ejs +69 -98
- package/src/templates/no-style.ejs +23 -6
- package/src/templates/partials/theme-init.js +26 -0
package/src/plugins/seo.js
CHANGED
|
@@ -1,69 +1,117 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Source file from the docmd project — https://github.com/mgks/docmd
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Generate SEO meta tags for a page
|
|
5
|
+
*/
|
|
6
|
+
|
|
2
7
|
function generateSeoMetaTags(config, pageData, relativePathToRoot) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
metaTagsHtml += ` <meta name="twitter:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
8
|
+
let metaTagsHtml = '';
|
|
9
|
+
const { frontmatter, outputPath } = pageData;
|
|
10
|
+
const seoFrontmatter = frontmatter.seo || {}; // Use nested seo object, fallback to empty
|
|
11
|
+
|
|
12
|
+
if (frontmatter.noindex || seoFrontmatter.noindex) {
|
|
13
|
+
metaTagsHtml += ' <meta name="robots" content="noindex">\n';
|
|
14
|
+
return metaTagsHtml; // No other SEO tags if noindex
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const siteTitle = config.siteTitle;
|
|
18
|
+
const pageTitle = frontmatter.title || 'Untitled';
|
|
19
|
+
const description = seoFrontmatter.description || frontmatter.description || config.plugins?.seo?.defaultDescription || '';
|
|
20
|
+
|
|
21
|
+
const siteUrl = config.siteUrl ? config.siteUrl.replace(/\/$/, '') : '';
|
|
22
|
+
const pageSegment = outputPath.replace(/index\.html$/, '').replace(/\.html$/, '');
|
|
23
|
+
const pageUrl = `${siteUrl}${pageSegment.startsWith('/') ? pageSegment : '/' + pageSegment}`;
|
|
24
|
+
|
|
25
|
+
metaTagsHtml += ` <meta name="description" content="${description}">\n`;
|
|
26
|
+
|
|
27
|
+
const canonicalUrl = seoFrontmatter.permalink || frontmatter.permalink || seoFrontmatter.canonicalUrl || frontmatter.canonicalUrl || pageUrl;
|
|
28
|
+
metaTagsHtml += ` <link rel="canonical" href="${canonicalUrl}">\n`;
|
|
29
|
+
|
|
30
|
+
// Open Graph
|
|
31
|
+
metaTagsHtml += ` <meta property="og:title" content="${pageTitle} | ${siteTitle}">\n`;
|
|
32
|
+
metaTagsHtml += ` <meta property="og:description" content="${description}">\n`;
|
|
33
|
+
metaTagsHtml += ` <meta property="og:url" content="${pageUrl}">\n`;
|
|
34
|
+
metaTagsHtml += ` <meta property="og:site_name" content="${config.plugins?.seo?.openGraph?.siteName || siteTitle}">\n`;
|
|
35
|
+
|
|
36
|
+
const ogImage = seoFrontmatter.image || frontmatter.image || seoFrontmatter.ogImage || frontmatter.ogImage || config.plugins?.seo?.openGraph?.defaultImage;
|
|
37
|
+
if (ogImage) {
|
|
38
|
+
const ogImageUrl = ogImage.startsWith('http') ? ogImage : `${siteUrl}${ogImage.startsWith('/') ? ogImage : '/' + ogImage}`;
|
|
39
|
+
metaTagsHtml += ` <meta property="og:image" content="${ogImageUrl}">\n`;
|
|
40
|
+
}
|
|
41
|
+
metaTagsHtml += ` <meta property="og:type" content="${seoFrontmatter.ogType || frontmatter.ogType || 'website'}">\n`;
|
|
42
|
+
|
|
43
|
+
// Twitter Card
|
|
44
|
+
const twitterCardType = seoFrontmatter.twitterCard || frontmatter.twitterCard || config.plugins?.seo?.twitter?.cardType || 'summary';
|
|
45
|
+
metaTagsHtml += ` <meta name="twitter:card" content="${twitterCardType}">\n`;
|
|
46
|
+
if (config.plugins?.seo?.twitter?.siteUsername) {
|
|
47
|
+
metaTagsHtml += ` <meta name="twitter:site" content="${config.plugins.seo.twitter.siteUsername}">\n`;
|
|
48
|
+
}
|
|
49
|
+
const twitterCreator = seoFrontmatter.twitterCreator || frontmatter.twitterCreator || config.plugins?.seo?.twitter?.creatorUsername;
|
|
50
|
+
if (twitterCreator) {
|
|
51
|
+
metaTagsHtml += ` <meta name="twitter:creator" content="${twitterCreator}">\n`;
|
|
52
|
+
}
|
|
53
|
+
metaTagsHtml += ` <meta name="twitter:title" content="${pageTitle} | ${siteTitle}">\n`;
|
|
54
|
+
metaTagsHtml += ` <meta name="twitter:description" content="${description}">\n`;
|
|
55
|
+
if (ogImage) {
|
|
56
|
+
const twitterImageUrl = ogImage.startsWith('http') ? ogImage : `${siteUrl}${ogImage.startsWith('/') ? ogImage : '/' + ogImage}`;
|
|
57
|
+
metaTagsHtml += ` <meta name="twitter:image" content="${twitterImageUrl}">\n`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Keywords
|
|
61
|
+
const keywords = seoFrontmatter.keywords || frontmatter.keywords;
|
|
62
|
+
if (keywords) {
|
|
63
|
+
const keywordsString = Array.isArray(keywords) ? keywords.join(', ') : keywords;
|
|
64
|
+
metaTagsHtml += ` <meta name="keywords" content="${keywordsString}">\n`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// LD+JSON Structured Data
|
|
68
|
+
const ldJsonConfig = seoFrontmatter.ldJson || frontmatter.ldJson;
|
|
69
|
+
if (ldJsonConfig) {
|
|
70
|
+
try {
|
|
71
|
+
const baseLdJson = {
|
|
72
|
+
'@context': 'https://schema.org',
|
|
73
|
+
'@type': 'Article',
|
|
74
|
+
mainEntityOfPage: {
|
|
75
|
+
'@type': 'WebPage',
|
|
76
|
+
'@id': canonicalUrl,
|
|
77
|
+
},
|
|
78
|
+
headline: pageTitle,
|
|
79
|
+
description: description,
|
|
80
|
+
url: canonicalUrl,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
if (config.siteTitle) {
|
|
84
|
+
baseLdJson.publisher = {
|
|
85
|
+
'@type': 'Organization',
|
|
86
|
+
name: config.siteTitle,
|
|
87
|
+
};
|
|
88
|
+
if (config.logo?.light) {
|
|
89
|
+
baseLdJson.publisher.logo = {
|
|
90
|
+
'@type': 'ImageObject',
|
|
91
|
+
url: `${siteUrl}${config.logo.light.startsWith('/') ? config.logo.light : '/' + config.logo.light}`
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (ogImage) {
|
|
97
|
+
baseLdJson.image = `${siteUrl}${ogImage.startsWith('/') ? ogImage : '/' + ogImage}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const finalLdJson = typeof ldJsonConfig === 'object'
|
|
101
|
+
? { ...baseLdJson, ...ldJsonConfig }
|
|
102
|
+
: baseLdJson;
|
|
103
|
+
|
|
104
|
+
metaTagsHtml += ` <script type="application/ld+json">\n`;
|
|
105
|
+
metaTagsHtml += ` ${JSON.stringify(finalLdJson, null, 2)}\n`;
|
|
106
|
+
metaTagsHtml += ` </script>\n`;
|
|
107
|
+
} catch (e) {
|
|
108
|
+
console.error(`❌ Error generating LD+JSON for page: ${outputPath}`);
|
|
109
|
+
console.error(` Could not stringify the ldJson object. Please check its structure in the frontmatter.`);
|
|
110
|
+
console.error(` ${e.message}`);
|
|
63
111
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return metaTagsHtml;
|
|
67
112
|
}
|
|
68
|
-
|
|
69
|
-
|
|
113
|
+
|
|
114
|
+
return metaTagsHtml;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = { generateSeoMetaTags };
|
package/src/plugins/sitemap.js
CHANGED
package/src/templates/layout.ejs
CHANGED
|
@@ -13,9 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
<%- faviconLinkHtml || '' %> <%# Favicon %>
|
|
15
15
|
|
|
16
|
+
<%- themeInitScript %>
|
|
17
|
+
|
|
16
18
|
<link rel="stylesheet" href="<%= relativePathToRoot %>assets/css/docmd-main.css">
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
<% if (config.theme?.codeHighlight !== false) { %>
|
|
21
|
+
<link rel="stylesheet" id="highlight-theme" href="<%= relativePathToRoot %>assets/css/docmd-highlight-<%= defaultMode === 'dark' ? 'dark' : 'light' %>.css" data-base-href="<%= relativePathToRoot %>assets/css/">
|
|
22
|
+
<% } %>
|
|
19
23
|
|
|
20
24
|
<%- themeCssLinkHtml || '' %> <%# For theme.name specific CSS %>
|
|
21
25
|
|
|
@@ -27,7 +31,9 @@
|
|
|
27
31
|
|
|
28
32
|
<%- pluginHeadScriptsHtml || '' %> <%# Plugin specific head scripts (e.g., Analytics) %>
|
|
29
33
|
</head>
|
|
30
|
-
<body class="<%= sidebarConfig.collapsible ? 'sidebar-collapsible' : 'sidebar-not-collapsible' %>"
|
|
34
|
+
<body class="<%= sidebarConfig.collapsible ? 'sidebar-collapsible' : 'sidebar-not-collapsible' %>"
|
|
35
|
+
data-theme="<%= defaultMode %>" data-default-collapsed="<%= sidebarConfig.defaultCollapsed %>"
|
|
36
|
+
data-copy-code-enabled="<%= config.copyCode === true %>">
|
|
31
37
|
<aside class="sidebar">
|
|
32
38
|
<div class="sidebar-header">
|
|
33
39
|
<% if (logo && logo.light && logo.dark) { %>
|
|
@@ -1,107 +1,78 @@
|
|
|
1
|
-
<%#
|
|
2
|
-
|
|
3
|
-
<%# renderIcon is passed from html-generator.js %>
|
|
4
|
-
|
|
5
|
-
<%
|
|
6
|
-
// Debug function - uncomment to troubleshoot paths
|
|
7
|
-
function debugNavPaths(item, itemPath, currentPath, isActive, isParentActive) {
|
|
8
|
-
console.log('\nDEBUG NAV ITEM:');
|
|
9
|
-
console.log(`Title: ${item.title}`);
|
|
10
|
-
console.log(`Path: ${item.path}`);
|
|
11
|
-
console.log(`Computed item path: ${itemPath}`);
|
|
12
|
-
console.log(`Current page path: ${currentPath}`);
|
|
13
|
-
console.log(`Is directly active: ${isActive}`);
|
|
14
|
-
console.log(`Is parent active: ${isParentActive}`);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function renderNavItems(items, currentLevelPagePath, rootPath) { %>
|
|
1
|
+
<%# navigation.ejs - Renders the sidebar navigation %>
|
|
2
|
+
<nav class="sidebar-nav" aria-label="Main navigation">
|
|
18
3
|
<ul>
|
|
19
|
-
<%
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (item.icon) {
|
|
30
|
-
// Use custom class for nav icons for specific styling
|
|
31
|
-
iconHtml = renderIcon(item.icon, { class: 'nav-item-icon' });
|
|
4
|
+
<%
|
|
5
|
+
// A robust, centralized function to normalize navigation paths
|
|
6
|
+
function normalizePath(p) {
|
|
7
|
+
if (!p) return '#';
|
|
8
|
+
let path = p.replace(/\\/g, '/');
|
|
9
|
+
|
|
10
|
+
if (path.endsWith('index.md')) {
|
|
11
|
+
path = path.slice(0, -'index.md'.length);
|
|
12
|
+
} else {
|
|
13
|
+
path = path.replace(/\.md$/, '');
|
|
32
14
|
}
|
|
33
15
|
|
|
34
|
-
if (
|
|
35
|
-
itemHref = item.path; // Full URL for external links
|
|
36
|
-
// Use a Lucide icon like 'arrow-up-right' or 'external-link'
|
|
37
|
-
externalLinkIconHtml = renderIcon('arrow-up-right', { class: 'nav-external-icon', width: '0.8em', height: '0.8em' });
|
|
38
|
-
} else {
|
|
39
|
-
// Path normalization for internal links
|
|
40
|
-
let cleanPath = item.path.startsWith('/') ? item.path : '/' + item.path; // Ensure leading slash
|
|
16
|
+
if (!path.startsWith('/')) path = '/' + path;
|
|
41
17
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
18
|
+
if (path.length > 1 && !path.endsWith('/')) {
|
|
19
|
+
path += '/';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Handle root case where path might be "" after stripping index.md
|
|
23
|
+
if (path === '') path = '/';
|
|
24
|
+
|
|
25
|
+
return path;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Helper to check for active children recursively
|
|
29
|
+
function hasActiveChild(item, currentPagePath) {
|
|
30
|
+
if (!item.children || !Array.isArray(item.children)) return false;
|
|
31
|
+
return item.children.some(child => {
|
|
32
|
+
if (!child.path || child.external) return false;
|
|
33
|
+
const childPath = normalizePath(child.path);
|
|
34
|
+
if (currentPagePath === childPath) return true;
|
|
35
|
+
return hasActiveChild(child, currentPagePath);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
55
38
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
39
|
+
// Recursive function to render navigation items
|
|
40
|
+
function renderNav(items) {
|
|
41
|
+
if (!items || !Array.isArray(items)) return;
|
|
42
|
+
items.forEach(item => {
|
|
43
|
+
const isExternal = item.external || false;
|
|
44
|
+
let itemPath = item.path || '#';
|
|
60
45
|
|
|
61
|
-
|
|
62
|
-
if (cleanPath === '/') {
|
|
63
|
-
isActivePage = currentLevelPagePath === 'index.html';
|
|
64
|
-
isCurrentPageActive = isActivePage;
|
|
65
|
-
}
|
|
66
|
-
// Parent folder with index.md
|
|
67
|
-
else if (cleanPath.endsWith('/')) {
|
|
68
|
-
// Direct match for folder/index.html pages
|
|
69
|
-
const folderPath = cleanPath.substring(1, cleanPath.length - 1); // Remove leading / and trailing /
|
|
70
|
-
isActivePage = currentLevelPagePath === folderPath + '/';
|
|
71
|
-
isCurrentPageActive = isActivePage;
|
|
72
|
-
|
|
73
|
-
// Check if any children are active
|
|
74
|
-
// If current path starts with this item's path, it's a parent of the active page
|
|
75
|
-
if (!isActivePage && currentLevelPagePath.startsWith(normalizedItemPath)) {
|
|
76
|
-
isCurrentPageActive = true;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
// Regular page
|
|
80
|
-
else {
|
|
81
|
-
const pagePath = cleanPath.substring(1) + '/';
|
|
82
|
-
isActivePage = currentLevelPagePath === pagePath;
|
|
83
|
-
isCurrentPageActive = isActivePage;
|
|
84
|
-
}
|
|
85
|
-
// SIMPLIFIED ACTIVE STATE LOGIC - END
|
|
86
|
-
}
|
|
87
|
-
%>
|
|
88
|
-
<li class="<%= isCurrentPageActive ? 'active-parent' : '' %>">
|
|
89
|
-
<a href="<%= itemHref %>"
|
|
90
|
-
class="<%= isActivePage ? 'active' : '' %>"
|
|
91
|
-
<%- targetBlank %>>
|
|
92
|
-
<% if (iconHtml) { %><%- iconHtml %><% } %>
|
|
93
|
-
<span><%- item.title %></span> <%# Wrap title in span for styling if needed %>
|
|
94
|
-
<% if (externalLinkIconHtml) { %><%- externalLinkIconHtml %><% } %>
|
|
95
|
-
</a>
|
|
46
|
+
const normalizedItemPath = isExternal ? itemPath : normalizePath(itemPath);
|
|
96
47
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
</li>
|
|
101
|
-
<% }); %>
|
|
102
|
-
</ul>
|
|
103
|
-
<% } %>
|
|
48
|
+
const isActive = !isExternal && currentPagePath === normalizedItemPath;
|
|
49
|
+
const isParentOfActive = !isActive && hasActiveChild(item, currentPagePath);
|
|
50
|
+
const isCollapsible = item.children && item.collapsible;
|
|
104
51
|
|
|
105
|
-
|
|
106
|
-
|
|
52
|
+
const liClasses = [];
|
|
53
|
+
if (isActive) liClasses.push('active');
|
|
54
|
+
if (isParentOfActive) liClasses.push('active-parent');
|
|
55
|
+
if (isCollapsible) liClasses.push('collapsible');
|
|
56
|
+
|
|
57
|
+
const finalHref = isExternal ? item.path : (itemPath === '#' ? '#' : (relativePathToRoot + (item.path.startsWith('/') ? item.path.substring(1) : item.path)));
|
|
58
|
+
%>
|
|
59
|
+
<li class="<%= liClasses.join(' ') %>" <%- isCollapsible ? `data-nav-id="${item.path}"` : '' %>>
|
|
60
|
+
<a href="<%- finalHref %>" class="<%- isActive ? 'active' : '' %>" <%- isExternal ? 'target="_blank" rel="noopener"' : '' %>>
|
|
61
|
+
<% if (item.icon) { %> <%- renderIcon(item.icon) %> <% } %>
|
|
62
|
+
<span class="nav-item-title"><%= item.title %></span>
|
|
63
|
+
<% if (isCollapsible) { %> <%- renderIcon('chevron-right', { class: 'collapse-icon' }) %> <% } %>
|
|
64
|
+
<% if (isExternal) { %> <%- renderIcon('external-link', { class: 'nav-external-icon' }) %> <% } %>
|
|
65
|
+
</a>
|
|
66
|
+
<% if (item.children) { %>
|
|
67
|
+
<ul class="submenu">
|
|
68
|
+
<% renderNav(item.children); %>
|
|
69
|
+
</ul>
|
|
70
|
+
<% } %>
|
|
71
|
+
</li>
|
|
72
|
+
<%
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
renderNav(navItems);
|
|
76
|
+
%>
|
|
77
|
+
</ul>
|
|
107
78
|
</nav>
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
<%- faviconLinkHtml || '' %>
|
|
17
17
|
<% } %>
|
|
18
18
|
|
|
19
|
+
<% if (frontmatter.components?.themeMode !== false) { %>
|
|
20
|
+
<%- themeInitScript %>
|
|
21
|
+
<% } %>
|
|
22
|
+
|
|
19
23
|
<% if (frontmatter.components?.css !== false) { %>
|
|
20
24
|
<link rel="stylesheet" href="<%= relativePathToRoot %>assets/css/docmd-main.css">
|
|
21
25
|
<% if (frontmatter.components?.highlight !== false) { %>
|
|
@@ -45,7 +49,16 @@
|
|
|
45
49
|
<%- frontmatter.customHead %>
|
|
46
50
|
<% } %>
|
|
47
51
|
</head>
|
|
48
|
-
<body
|
|
52
|
+
<body
|
|
53
|
+
<%
|
|
54
|
+
if (frontmatter.components?.theme !== false) {
|
|
55
|
+
%> data-theme="<%= defaultMode %>"<%
|
|
56
|
+
}
|
|
57
|
+
%><%
|
|
58
|
+
if (frontmatter.bodyClass) {
|
|
59
|
+
%> class="<%= frontmatter.bodyClass %>"<%
|
|
60
|
+
}
|
|
61
|
+
%> data-copy-code-enabled="<%= config.copyCode === true %>">
|
|
49
62
|
<% if (frontmatter.components?.layout === true || frontmatter.components?.layout === 'full') { %>
|
|
50
63
|
<div class="main-content-wrapper">
|
|
51
64
|
<% if (frontmatter.components?.header !== false) { %>
|
|
@@ -136,18 +149,22 @@
|
|
|
136
149
|
<%- content %>
|
|
137
150
|
<% } %>
|
|
138
151
|
|
|
139
|
-
<% if (frontmatter.components?.scripts
|
|
140
|
-
<% if (frontmatter.components?.
|
|
141
|
-
<script src="<%= relativePathToRoot %>assets/js/docmd-
|
|
152
|
+
<% if (frontmatter.components?.scripts === true) { %>
|
|
153
|
+
<% if (frontmatter.components?.mainScripts === true) { %>
|
|
154
|
+
<script src="<%= relativePathToRoot %>assets/js/docmd-main.js"></script>
|
|
155
|
+
<% } %>
|
|
156
|
+
|
|
157
|
+
<% if (frontmatter.components?.lightbox === true && frontmatter.components?.mainScripts === true) { %>
|
|
158
|
+
<script src="<%= relativePathToRoot %>assets/js/docmd-image-lightbox.js"></script>
|
|
142
159
|
<% } %>
|
|
143
160
|
|
|
144
|
-
<% if (frontmatter.components?.customJs
|
|
161
|
+
<% if (frontmatter.components?.customJs === true && customJsFiles && customJsFiles.length > 0) { %>
|
|
145
162
|
<% customJsFiles.forEach(jsFile => { %>
|
|
146
163
|
<script src="<%= relativePathToRoot %><%- jsFile.startsWith('/') ? jsFile.substring(1) : jsFile %>"></script>
|
|
147
164
|
<% }); %>
|
|
148
165
|
<% } %>
|
|
149
166
|
|
|
150
|
-
<% if (frontmatter.components?.pluginBodyScripts
|
|
167
|
+
<% if (frontmatter.components?.pluginBodyScripts === true) { %>
|
|
151
168
|
<%- pluginBodyScriptsHtml || '' %>
|
|
152
169
|
<% } %>
|
|
153
170
|
<% } %>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Source file from the docmd project — https://github.com/mgks/docmd
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Initialize the theme from localStorage
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
(function() {
|
|
8
|
+
try {
|
|
9
|
+
const storedTheme = localStorage.getItem('docmd-theme');
|
|
10
|
+
if (storedTheme) {
|
|
11
|
+
document.documentElement.setAttribute('data-theme', storedTheme);
|
|
12
|
+
|
|
13
|
+
// Also update highlight CSS link to match the stored theme
|
|
14
|
+
const highlightThemeLink = document.getElementById('highlight-theme');
|
|
15
|
+
if (highlightThemeLink) {
|
|
16
|
+
const baseHref = highlightThemeLink.getAttribute('data-base-href');
|
|
17
|
+
if (baseHref) {
|
|
18
|
+
const newHref = baseHref + `docmd-highlight-${storedTheme}.css`;
|
|
19
|
+
highlightThemeLink.setAttribute('href', newHref);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.error('Error applying theme from localStorage', e);
|
|
25
|
+
}
|
|
26
|
+
})();
|