@mgks/docmd 0.1.4 → 0.2.1
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/README.md +2 -4
- package/assets/css/welcome.css +5 -377
- package/assets/images/preview-dark-1.webp +0 -0
- package/assets/images/preview-dark-2.webp +0 -0
- package/assets/images/preview-dark-3.webp +0 -0
- package/assets/images/preview-light-1.webp +0 -0
- package/assets/images/preview-light-2.webp +0 -0
- package/assets/images/preview-light-3.webp +0 -0
- package/config.js +40 -6
- package/docs/configuration.md +82 -7
- package/docs/content/containers/buttons.md +88 -0
- package/docs/content/containers/callouts.md +154 -0
- package/docs/content/containers/cards.md +93 -0
- package/docs/content/containers/index.md +35 -0
- package/docs/content/containers/nested-containers.md +329 -0
- package/docs/content/containers/steps.md +175 -0
- package/docs/content/containers/tabs.md +228 -0
- package/docs/content/custom-containers.md +19 -124
- package/docs/content/frontmatter.md +2 -2
- package/docs/content/no-style-example.md +2 -0
- package/docs/content/no-style-pages.md +52 -28
- package/docs/index.md +55 -27
- package/docs/plugins/seo.md +80 -31
- package/docs/theming/available-themes.md +17 -2
- package/docs/theming/light-dark-mode.md +12 -3
- package/package.json +21 -9
- package/src/assets/css/docmd-main.css +5 -806
- package/src/assets/css/docmd-theme-retro.css +9 -0
- package/src/assets/css/docmd-theme-ruby.css +7 -604
- package/src/assets/css/docmd-theme-sky.css +7 -649
- package/src/assets/js/docmd-image-lightbox.js +4 -2
- package/src/assets/js/docmd-main.js +157 -0
- package/src/commands/build.js +62 -120
- package/src/commands/dev.js +2 -1
- package/src/commands/init.js +23 -1
- package/src/core/config-loader.js +2 -0
- package/src/core/file-processor.js +669 -373
- package/src/core/html-generator.js +49 -40
- package/src/core/icon-renderer.js +3 -2
- package/src/plugins/analytics.js +5 -1
- package/src/plugins/seo.js +114 -62
- package/src/plugins/sitemap.js +6 -0
- package/src/templates/layout.ejs +40 -8
- package/src/templates/no-style.ejs +23 -6
- package/src/templates/partials/theme-init.js +26 -0
- package/assets/images/preview-dark-1.png +0 -0
- package/assets/images/preview-dark-2.png +0 -0
- package/assets/images/preview-dark-3.png +0 -0
- package/assets/images/preview-light-1.png +0 -0
- package/assets/images/preview-light-2.png +0 -0
- package/assets/images/preview-light-3.png +0 -0
- package/src/assets/js/docmd-theme-toggle.js +0 -59
|
@@ -1,51 +1,57 @@
|
|
|
1
|
-
//
|
|
1
|
+
//
|
|
2
|
+
|
|
2
3
|
const ejs = require('ejs');
|
|
3
4
|
const path = require('path');
|
|
4
5
|
const fs = require('fs-extra');
|
|
5
|
-
const {
|
|
6
|
+
const { createMarkdownItInstance } = require('./file-processor');
|
|
6
7
|
const { generateSeoMetaTags } = require('../plugins/seo');
|
|
7
8
|
const { generateAnalyticsScripts } = require('../plugins/analytics');
|
|
8
|
-
const { renderIcon } = require('./icon-renderer');
|
|
9
|
+
const { renderIcon } = require('./icon-renderer');
|
|
10
|
+
|
|
11
|
+
// Create a markdown instance for inline rendering
|
|
12
|
+
let mdInstance = null;
|
|
13
|
+
|
|
14
|
+
let themeInitScript = '';
|
|
15
|
+
(async () => {
|
|
16
|
+
const themeInitPath = path.join(__dirname, '..', 'templates', 'partials', 'theme-init.js');
|
|
17
|
+
if (await fs.pathExists(themeInitPath)) {
|
|
18
|
+
const scriptContent = await fs.readFile(themeInitPath, 'utf8');
|
|
19
|
+
themeInitScript = `<script>${scriptContent}</script>`;
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
9
22
|
|
|
10
23
|
async function processPluginHooks(config, pageData, relativePathToRoot) {
|
|
11
24
|
let metaTagsHtml = '';
|
|
12
25
|
let faviconLinkHtml = '';
|
|
13
|
-
let themeCssLinkHtml = '';
|
|
14
|
-
let pluginStylesHtml = '';
|
|
26
|
+
let themeCssLinkHtml = '';
|
|
27
|
+
let pluginStylesHtml = '';
|
|
15
28
|
let pluginHeadScriptsHtml = '';
|
|
16
29
|
let pluginBodyScriptsHtml = '';
|
|
17
30
|
|
|
18
|
-
//
|
|
31
|
+
// Favicon (built-in handling)
|
|
19
32
|
if (config.favicon) {
|
|
20
33
|
const faviconPath = config.favicon.startsWith('/') ? config.favicon.substring(1) : config.favicon;
|
|
21
34
|
faviconLinkHtml = `<link rel="shortcut icon" href="${relativePathToRoot}${faviconPath}" type="image/x-icon">\n`;
|
|
22
35
|
}
|
|
23
36
|
|
|
24
|
-
//
|
|
37
|
+
// Theme CSS (built-in handling for theme.name)
|
|
25
38
|
if (config.theme && config.theme.name && config.theme.name !== 'default') {
|
|
26
|
-
// Assumes theme CSS files are like 'docmd-theme-yourthemename.css' in assets/css
|
|
27
39
|
const themeCssPath = `assets/css/docmd-theme-${config.theme.name}.css`;
|
|
28
|
-
// Check if theme file exists before linking (optional, good practice)
|
|
29
|
-
// For now, assume it will exist if specified.
|
|
30
40
|
themeCssLinkHtml = ` <link rel="stylesheet" href="${relativePathToRoot}${themeCssPath}">\n`;
|
|
31
41
|
}
|
|
32
42
|
|
|
33
|
-
|
|
34
|
-
// 3. SEO Plugin (if configured)
|
|
43
|
+
// SEO Plugin (if configured)
|
|
35
44
|
if (config.plugins?.seo) {
|
|
36
45
|
metaTagsHtml += generateSeoMetaTags(config, pageData, relativePathToRoot);
|
|
37
46
|
}
|
|
38
47
|
|
|
39
|
-
//
|
|
48
|
+
// Analytics Plugin (if configured)
|
|
40
49
|
if (config.plugins?.analytics) {
|
|
41
50
|
const analyticsScripts = generateAnalyticsScripts(config, pageData);
|
|
42
51
|
pluginHeadScriptsHtml += analyticsScripts.headScriptsHtml;
|
|
43
52
|
pluginBodyScriptsHtml += analyticsScripts.bodyScriptsHtml;
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
// Future: Loop through a more generic plugin array if you evolve the system
|
|
47
|
-
// for (const plugin of config.activePlugins) { /* plugin.runHook('meta', ...) */ }
|
|
48
|
-
|
|
49
55
|
return {
|
|
50
56
|
metaTagsHtml,
|
|
51
57
|
faviconLinkHtml,
|
|
@@ -58,33 +64,32 @@ async function processPluginHooks(config, pageData, relativePathToRoot) {
|
|
|
58
64
|
|
|
59
65
|
async function generateHtmlPage(templateData) {
|
|
60
66
|
const {
|
|
61
|
-
content,
|
|
67
|
+
content, siteTitle, navigationHtml,
|
|
62
68
|
relativePathToRoot, config, frontmatter, outputPath,
|
|
63
69
|
prevPage, nextPage, currentPagePath, headings
|
|
64
70
|
} = templateData;
|
|
65
71
|
|
|
72
|
+
const pageTitle = frontmatter.title;
|
|
73
|
+
|
|
66
74
|
// Process plugins to get their HTML contributions
|
|
67
75
|
const pluginOutputs = await processPluginHooks(
|
|
68
76
|
config,
|
|
69
|
-
{ frontmatter, outputPath },
|
|
77
|
+
{ frontmatter, outputPath },
|
|
70
78
|
relativePathToRoot
|
|
71
79
|
);
|
|
72
80
|
|
|
73
81
|
let footerHtml = '';
|
|
74
82
|
if (config.footer) {
|
|
83
|
+
// Initialize mdInstance if not already done
|
|
84
|
+
if (!mdInstance) {
|
|
85
|
+
mdInstance = createMarkdownItInstance(config);
|
|
86
|
+
}
|
|
75
87
|
footerHtml = mdInstance.renderInline(config.footer);
|
|
76
88
|
}
|
|
77
89
|
|
|
78
|
-
// Determine which template to use based on frontmatter
|
|
79
90
|
let templateName = 'layout.ejs';
|
|
80
91
|
if (frontmatter.noStyle === true) {
|
|
81
92
|
templateName = 'no-style.ejs';
|
|
82
|
-
|
|
83
|
-
// For no-style pages, ensure we're passing the raw HTML content
|
|
84
|
-
// without any additional processing or escaping
|
|
85
|
-
if (content.includes('<') || content.includes('>')) {
|
|
86
|
-
console.warn(`⚠️ Warning: HTML content in no-style page appears to be escaped. This may cause rendering issues.`);
|
|
87
|
-
}
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
const layoutTemplatePath = path.join(__dirname, '..', 'templates', templateName);
|
|
@@ -93,42 +98,47 @@ async function generateHtmlPage(templateData) {
|
|
|
93
98
|
}
|
|
94
99
|
const layoutTemplate = await fs.readFile(layoutTemplatePath, 'utf8');
|
|
95
100
|
|
|
96
|
-
// Determine if this is an active page for TOC display
|
|
97
|
-
// The currentPagePath exists and has content
|
|
98
101
|
const isActivePage = currentPagePath && content && content.trim().length > 0;
|
|
99
102
|
|
|
100
103
|
const ejsData = {
|
|
101
104
|
content,
|
|
102
|
-
pageTitle
|
|
103
|
-
|
|
105
|
+
pageTitle,
|
|
106
|
+
themeInitScript,
|
|
107
|
+
description: frontmatter.description,
|
|
104
108
|
siteTitle,
|
|
105
109
|
navigationHtml,
|
|
106
110
|
defaultMode: config.theme?.defaultMode || 'light',
|
|
107
111
|
relativePathToRoot,
|
|
108
112
|
logo: config.logo,
|
|
113
|
+
sidebarConfig: {
|
|
114
|
+
collapsible: config.sidebar?.collapsible ?? false,
|
|
115
|
+
defaultCollapsed: config.sidebar?.defaultCollapsed ?? false,
|
|
116
|
+
},
|
|
109
117
|
theme: config.theme,
|
|
110
118
|
customCssFiles: config.theme?.customCss || [],
|
|
111
119
|
customJsFiles: config.customJs || [],
|
|
120
|
+
sponsor: config.sponsor,
|
|
112
121
|
footer: config.footer,
|
|
113
122
|
footerHtml,
|
|
114
123
|
renderIcon,
|
|
115
124
|
prevPage,
|
|
116
125
|
nextPage,
|
|
117
|
-
currentPagePath,
|
|
118
|
-
headings: headings || [],
|
|
119
|
-
isActivePage,
|
|
120
|
-
frontmatter,
|
|
121
|
-
|
|
126
|
+
currentPagePath,
|
|
127
|
+
headings: headings || [],
|
|
128
|
+
isActivePage,
|
|
129
|
+
frontmatter,
|
|
130
|
+
config: config,
|
|
131
|
+
...pluginOutputs,
|
|
122
132
|
};
|
|
123
133
|
|
|
124
134
|
try {
|
|
125
135
|
return ejs.render(layoutTemplate, ejsData, {
|
|
126
|
-
filename: layoutTemplatePath
|
|
136
|
+
filename: layoutTemplatePath
|
|
127
137
|
});
|
|
128
138
|
} catch (e) {
|
|
129
139
|
console.error(`❌ Error rendering EJS template for ${outputPath}: ${e.message}`);
|
|
130
|
-
console.error("EJS Data:", JSON.stringify(ejsData, null, 2).substring(0, 1000) + "...");
|
|
131
|
-
throw e;
|
|
140
|
+
console.error("EJS Data:", JSON.stringify(ejsData, null, 2).substring(0, 1000) + "...");
|
|
141
|
+
throw e;
|
|
132
142
|
}
|
|
133
143
|
}
|
|
134
144
|
|
|
@@ -139,17 +149,16 @@ async function generateNavigationHtml(navItems, currentPagePath, relativePathToR
|
|
|
139
149
|
}
|
|
140
150
|
const navTemplate = await fs.readFile(navTemplatePath, 'utf8');
|
|
141
151
|
|
|
142
|
-
// Make renderIcon available to the EJS template
|
|
143
152
|
const ejsHelpers = { renderIcon };
|
|
144
153
|
|
|
145
154
|
return ejs.render(navTemplate, {
|
|
146
155
|
navItems,
|
|
147
156
|
currentPagePath,
|
|
148
157
|
relativePathToRoot,
|
|
149
|
-
config,
|
|
158
|
+
config,
|
|
150
159
|
...ejsHelpers
|
|
151
160
|
}, {
|
|
152
|
-
filename: navTemplatePath
|
|
161
|
+
filename: navTemplatePath
|
|
153
162
|
});
|
|
154
163
|
}
|
|
155
164
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
1
|
+
// Source file from the docmd project — https://github.com/mgks/docmd
|
|
2
|
+
|
|
3
|
+
const lucideStatic = require('lucide-static');
|
|
3
4
|
|
|
4
5
|
// On first load, log debug information about a specific icon to understand its structure
|
|
5
6
|
let debugRun = false;
|
package/src/plugins/analytics.js
CHANGED
package/src/plugins/seo.js
CHANGED
|
@@ -1,65 +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
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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}`);
|
|
59
111
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return metaTagsHtml;
|
|
63
112
|
}
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
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) { %>
|
|
@@ -40,7 +46,7 @@
|
|
|
40
46
|
<% } %>
|
|
41
47
|
</div>
|
|
42
48
|
<%- navigationHtml %>
|
|
43
|
-
<% if (theme && theme.enableModeToggle) { %>
|
|
49
|
+
<% if (theme && theme.enableModeToggle && theme.positionMode !== 'top') { %>
|
|
44
50
|
<button id="theme-toggle-button" aria-label="Toggle theme" class="theme-toggle-button">
|
|
45
51
|
<%# renderIcon is available in the global EJS scope from html-generator %>
|
|
46
52
|
<%- renderIcon('sun', { class: 'icon-sun' }) %>
|
|
@@ -49,9 +55,25 @@
|
|
|
49
55
|
<% } %>
|
|
50
56
|
</aside>
|
|
51
57
|
<div class="main-content-wrapper">
|
|
52
|
-
<
|
|
53
|
-
<
|
|
54
|
-
|
|
58
|
+
<div class="page-header">
|
|
59
|
+
<div class="header-left">
|
|
60
|
+
<% if (sidebarConfig.collapsible) { %>
|
|
61
|
+
<button id="sidebar-toggle-button" class="sidebar-toggle-button" aria-label="Toggle Sidebar">
|
|
62
|
+
<%- renderIcon('panel-left-close') %>
|
|
63
|
+
</button>
|
|
64
|
+
<% } %>
|
|
65
|
+
<h1><%= pageTitle %></h1>
|
|
66
|
+
</div>
|
|
67
|
+
<% if (theme && theme.enableModeToggle && theme.positionMode === 'top') { %>
|
|
68
|
+
<div class="header-right">
|
|
69
|
+
<button id="theme-toggle-button" aria-label="Toggle theme" class="theme-toggle-button theme-toggle-header">
|
|
70
|
+
<%# renderIcon is available in the global EJS scope from html-generator %>
|
|
71
|
+
<%- renderIcon('sun', { class: 'icon-sun' }) %>
|
|
72
|
+
<%- renderIcon('moon', { class: 'icon-moon' }) %>
|
|
73
|
+
</button>
|
|
74
|
+
</div>
|
|
75
|
+
<% } %>
|
|
76
|
+
</div>
|
|
55
77
|
<main class="content-area">
|
|
56
78
|
<div class="content-layout">
|
|
57
79
|
<div class="main-content">
|
|
@@ -106,11 +128,21 @@
|
|
|
106
128
|
</footer>
|
|
107
129
|
</div>
|
|
108
130
|
|
|
109
|
-
<script src="<%= relativePathToRoot %>assets/js/docmd-
|
|
131
|
+
<script src="<%= relativePathToRoot %>assets/js/docmd-main.js"></script>
|
|
132
|
+
|
|
110
133
|
<% (customJsFiles || []).forEach(jsFile => { %>
|
|
111
134
|
<script src="<%= relativePathToRoot %><%- jsFile.startsWith('/') ? jsFile.substring(1) : jsFile %>"></script>
|
|
112
135
|
<% }); %>
|
|
113
136
|
|
|
114
|
-
<%- pluginBodyScriptsHtml || '' %>
|
|
137
|
+
<%- pluginBodyScriptsHtml || '' %>
|
|
138
|
+
|
|
139
|
+
<% if (sponsor && sponsor.enabled) { %>
|
|
140
|
+
<div class="sponsor-ribbon">
|
|
141
|
+
<a href="<%= sponsor.link %>" target="_blank" rel="noopener noreferrer" class="sponsor-link">
|
|
142
|
+
<%- renderIcon('heart', { class: 'sponsor-icon' }) %>
|
|
143
|
+
<span class="sponsor-text"><%= sponsor.title %></span>
|
|
144
|
+
</a>
|
|
145
|
+
</div>
|
|
146
|
+
<% } %>
|
|
115
147
|
</body>
|
|
116
148
|
</html>
|
|
@@ -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
|
+
})();
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|