@mgks/docmd 0.2.7 → 0.2.9

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.
@@ -0,0 +1,124 @@
1
+ // Source file from the docmd project — https://github.com/mgks/docmd
2
+
3
+ const MarkdownIt = require('markdown-it');
4
+ const hljs = require('highlight.js');
5
+ const attrs = require('markdown-it-attrs');
6
+ const markdown_it_footnote = require('markdown-it-footnote');
7
+ const markdown_it_task_lists = require('markdown-it-task-lists');
8
+ const markdown_it_abbr = require('markdown-it-abbr');
9
+ const markdown_it_deflist = require('markdown-it-deflist');
10
+
11
+ const { containers } = require('./containers');
12
+ const rules = require('./rules');
13
+ const renderers = require('./renderers');
14
+
15
+ // Custom plugin for Heading IDs
16
+ const headingIdPlugin = (md) => {
17
+ const originalHeadingOpen = md.renderer.rules.heading_open || function(tokens, idx, options, env, self) {
18
+ return self.renderToken(tokens, idx, options);
19
+ };
20
+
21
+ md.renderer.rules.heading_open = function(tokens, idx, options, env, self) {
22
+ const token = tokens[idx];
23
+
24
+ // Check if an ID was already set by markdown-it-attrs (e.g. {#my-id})
25
+ const existingId = token.attrGet('id');
26
+
27
+ // If NO ID exists, generate one automatically from the text content
28
+ if (!existingId) {
29
+ const contentToken = tokens[idx + 1];
30
+ if (contentToken && contentToken.type === 'inline' && contentToken.content) {
31
+ const headingText = contentToken.content;
32
+
33
+ // Note: markdown-it-attrs strips the curly braces content from .content
34
+ // BEFORE this rule runs, so headingText should be clean.
35
+
36
+ const id = headingText
37
+ .toLowerCase()
38
+ .replace(/\s+/g, '-') // Replace spaces with -
39
+ .replace(/[^\w\u4e00-\u9fa5-]+/g, '') // Remove all non-word chars (keeping hyphens). Added unicode support implies keeping more chars if needed.
40
+ .replace(/--+/g, '-') // Replace multiple - with single -
41
+ .replace(/^-+/, '') // Trim - from start of text
42
+ .replace(/-+$/, ''); // Trim - from end of text
43
+
44
+ if (id) {
45
+ token.attrSet('id', id);
46
+ }
47
+ }
48
+ }
49
+
50
+ return originalHeadingOpen(tokens, idx, options, env, self);
51
+ };
52
+ };
53
+
54
+ function createMarkdownItInstance(config) {
55
+ const mdOptions = {
56
+ html: true,
57
+ linkify: true,
58
+ typographer: true,
59
+ breaks: true,
60
+ };
61
+
62
+ const highlightFn = (str, lang) => {
63
+ if (lang === 'mermaid') {
64
+ return `<pre class="mermaid">${new MarkdownIt().utils.escapeHtml(str)}</pre>`;
65
+ }
66
+ if (lang && hljs.getLanguage(lang)) {
67
+ try {
68
+ return `<pre class="hljs"><code>${hljs.highlight(str, { language: lang, ignoreIllegals: true }).value}</code></pre>`;
69
+ } catch (e) { console.error(e); }
70
+ }
71
+ return `<pre class="hljs"><code>${new MarkdownIt().utils.escapeHtml(str)}</code></pre>`;
72
+ };
73
+
74
+ mdOptions.highlight = config.theme?.codeHighlight !== false ? highlightFn : (str, lang) => {
75
+ if (lang === 'mermaid') return `<pre class="mermaid">${new MarkdownIt().utils.escapeHtml(str)}</pre>`;
76
+ return `<pre><code>${new MarkdownIt().utils.escapeHtml(str)}</code></pre>`;
77
+ };
78
+
79
+ const md = new MarkdownIt(mdOptions);
80
+
81
+ // Plugins
82
+ md.use(attrs, { leftDelimiter: '{', rightDelimiter: '}' });
83
+ md.use(markdown_it_footnote);
84
+ md.use(markdown_it_task_lists);
85
+ md.use(markdown_it_abbr);
86
+ md.use(markdown_it_deflist);
87
+ md.use(headingIdPlugin);
88
+
89
+ // Register Containers
90
+ Object.keys(containers).forEach(containerName => {
91
+ const container = containers[containerName];
92
+ md.renderer.rules[`container_${containerName}_open`] = container.render;
93
+ md.renderer.rules[`container_${containerName}_close`] = container.render;
94
+ });
95
+
96
+ // Register Rules (Order Matters)
97
+ md.block.ruler.before('fence', 'steps_container', rules.stepsContainerRule, { alt: ['paragraph', 'reference', 'blockquote', 'list'] });
98
+ md.block.ruler.before('fence', 'enhanced_tabs', rules.enhancedTabsRule, { alt: ['paragraph', 'reference', 'blockquote', 'list'] });
99
+ md.block.ruler.before('fence', 'changelog_timeline', rules.changelogTimelineRule, { alt: ['paragraph', 'reference', 'blockquote', 'list'] });
100
+ md.block.ruler.before('paragraph', 'advanced_container', rules.advancedContainerRule, { alt: ['paragraph', 'reference', 'blockquote', 'list'] });
101
+ md.block.ruler.before('paragraph', 'standalone_closing', rules.standaloneClosingRule, { alt: ['paragraph', 'reference', 'blockquote', 'list'] });
102
+
103
+ // Register Renderers
104
+ md.renderer.rules.ordered_list_open = renderers.customOrderedListOpenRenderer;
105
+ md.renderer.rules.list_item_open = renderers.customListItemOpenRenderer;
106
+ md.renderer.rules.image = renderers.customImageRenderer;
107
+ md.renderer.rules.table_open = renderers.tableOpenRenderer;
108
+ md.renderer.rules.table_close = renderers.tableCloseRenderer;
109
+
110
+ // Tabs Renderers
111
+ md.renderer.rules.tabs_open = renderers.tabsOpenRenderer;
112
+ md.renderer.rules.tabs_nav_open = renderers.tabsNavOpenRenderer;
113
+ md.renderer.rules.tabs_nav_close = renderers.tabsNavCloseRenderer;
114
+ md.renderer.rules.tabs_nav_item = renderers.tabsNavItemRenderer;
115
+ md.renderer.rules.tabs_content_open = renderers.tabsContentOpenRenderer;
116
+ md.renderer.rules.tabs_content_close = renderers.tabsContentCloseRenderer;
117
+ md.renderer.rules.tab_pane_open = renderers.tabPaneOpenRenderer;
118
+ md.renderer.rules.tab_pane_close = renderers.tabPaneCloseRenderer;
119
+ md.renderer.rules.tabs_close = renderers.tabsCloseRenderer;
120
+
121
+ return md;
122
+ }
123
+
124
+ module.exports = { createMarkdownItInstance };
@@ -115,7 +115,22 @@
115
115
  <%- include('toc', { content, headings, navigationHtml, isActivePage }) %>
116
116
  </div>
117
117
  </div>
118
+
119
+ <!-- Page footer actions -->
120
+ <div class="page-footer-actions">
121
+ <% if (locals.editUrl) { %>
122
+ <a href="<%= editUrl %>" target="_blank" rel="noopener noreferrer" class="edit-link">
123
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pencil"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
124
+ <%= editLinkText %>
125
+ </a>
126
+ <% } %>
127
+
128
+ <% if (locals.lastUpdated) { %>
129
+ <!-- Placeholder for future Last Updated feature -->
130
+ <% } %>
131
+ </div>
118
132
  </main>
133
+
119
134
  <footer class="page-footer">
120
135
  <div class="footer-content">
121
136
  <div class="user-footer">