@classic-homes/theme-docs 0.0.48 → 0.0.50
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.
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* For use outside of markdown content when you need
|
|
6
6
|
* to display code with highlighting directly.
|
|
7
7
|
*/
|
|
8
|
-
import { onMount } from 'svelte';
|
|
9
8
|
import { cn } from '../utils.js';
|
|
10
9
|
import { escapeHtml } from '../highlighter/index.js';
|
|
11
10
|
|
|
@@ -35,20 +34,36 @@
|
|
|
35
34
|
let highlighted = $state('');
|
|
36
35
|
let loading = $state(true);
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
37
|
+
// Re-highlight whenever code/language/theme change (not just on mount)
|
|
38
|
+
$effect(() => {
|
|
39
|
+
const currentCode = code;
|
|
40
|
+
const currentLanguage = language;
|
|
41
|
+
const currentTheme = theme;
|
|
42
|
+
let cancelled = false;
|
|
43
|
+
|
|
44
|
+
loading = true;
|
|
45
|
+
(async () => {
|
|
46
|
+
try {
|
|
47
|
+
const { highlightCode } = await import('../highlighter/index.js');
|
|
48
|
+
const html = await highlightCode(currentCode, currentLanguage, { theme: currentTheme });
|
|
49
|
+
if (cancelled) return;
|
|
50
|
+
highlighted = html;
|
|
51
|
+
loading = false;
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (cancelled) return;
|
|
54
|
+
// Log warning for debugging - falling back to plain code
|
|
55
|
+
console.warn(
|
|
56
|
+
'[docs] Failed to highlight code block, showing plain code:',
|
|
57
|
+
err instanceof Error ? err.message : err
|
|
58
|
+
);
|
|
59
|
+
highlighted = `<pre class="shiki bg-muted rounded-lg p-4 overflow-x-auto"><code>${escapeHtml(currentCode)}</code></pre>`;
|
|
60
|
+
loading = false;
|
|
61
|
+
}
|
|
62
|
+
})();
|
|
63
|
+
|
|
64
|
+
return () => {
|
|
65
|
+
cancelled = true;
|
|
66
|
+
};
|
|
52
67
|
});
|
|
53
68
|
</script>
|
|
54
69
|
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
* Requires mermaid to be installed in the consuming application:
|
|
9
9
|
* npm install mermaid
|
|
10
10
|
*/
|
|
11
|
-
import { onMount } from 'svelte';
|
|
12
11
|
import { cn } from '../utils.js';
|
|
13
12
|
|
|
14
13
|
interface Props {
|
|
@@ -34,32 +33,48 @@
|
|
|
34
33
|
let containerRef = $state<HTMLDivElement | null>(null);
|
|
35
34
|
let rendered = $state(false);
|
|
36
35
|
let error = $state<string | null>(null);
|
|
36
|
+
let renderCount = 0;
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
// Re-render whenever code/theme change (not just on mount)
|
|
39
|
+
$effect(() => {
|
|
40
|
+
const container = containerRef;
|
|
41
|
+
const currentCode = code;
|
|
42
|
+
const currentTheme = theme;
|
|
43
|
+
if (!container) return;
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
// Dynamically import mermaid to avoid SSR issues
|
|
43
|
-
const mermaid = await import('mermaid');
|
|
45
|
+
let cancelled = false;
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
securityLevel: 'strict',
|
|
50
|
-
});
|
|
47
|
+
(async () => {
|
|
48
|
+
try {
|
|
49
|
+
// Dynamically import mermaid to avoid SSR issues
|
|
50
|
+
const mermaid = await import('mermaid');
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
// Initialize mermaid with theme
|
|
53
|
+
mermaid.default.initialize({
|
|
54
|
+
startOnLoad: false,
|
|
55
|
+
theme: currentTheme,
|
|
56
|
+
securityLevel: 'strict',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Render the diagram; mermaid.render needs a fresh element id per run
|
|
60
|
+
const { svg } = await mermaid.default.render(`${id}-${++renderCount}`, currentCode);
|
|
61
|
+
if (cancelled) return;
|
|
62
|
+
container.innerHTML = svg;
|
|
63
|
+
rendered = true;
|
|
64
|
+
error = null;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (cancelled) return;
|
|
67
|
+
console.warn(
|
|
68
|
+
'[docs] Failed to render Mermaid diagram:',
|
|
69
|
+
err instanceof Error ? err.message : err
|
|
70
|
+
);
|
|
71
|
+
error = err instanceof Error ? err.message : 'Failed to render diagram';
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
|
|
75
|
+
return () => {
|
|
76
|
+
cancelled = true;
|
|
77
|
+
};
|
|
63
78
|
});
|
|
64
79
|
</script>
|
|
65
80
|
|
package/dist/lib/parser/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Marked, Renderer } from 'marked';
|
|
2
2
|
import yaml from 'js-yaml';
|
|
3
3
|
import { getHighlighter, escapeHtml } from '../highlighter/index.js';
|
|
4
4
|
import { getAllExtensions, resetFootnoteStore, renderFootnotes } from './extensions.js';
|
|
@@ -89,7 +89,7 @@ export async function parseMarkdown(content, options = {}) {
|
|
|
89
89
|
}
|
|
90
90
|
};
|
|
91
91
|
if (generateHeadingIds) {
|
|
92
|
-
renderer.heading = ({ text, depth })
|
|
92
|
+
renderer.heading = function ({ tokens, text, depth }) {
|
|
93
93
|
const id = text
|
|
94
94
|
.toLowerCase()
|
|
95
95
|
.replace(/\./g, '-') // Convert periods to hyphens (preserves version numbers like v2.0 → v2-0)
|
|
@@ -97,13 +97,18 @@ export async function parseMarkdown(content, options = {}) {
|
|
|
97
97
|
.replace(/\s+/g, '-') // Convert spaces to hyphens
|
|
98
98
|
.replace(/-+/g, '-') // Collapse multiple hyphens
|
|
99
99
|
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
|
100
|
-
|
|
100
|
+
// Render inline tokens so markdown inside headings (bold, code, links) works
|
|
101
|
+
return `<h${depth} id="${id}">${this.parser.parseInline(tokens)}</h${depth}>`;
|
|
101
102
|
};
|
|
102
103
|
}
|
|
104
|
+
// Use a per-call Marked instance: the renderer captures per-call state
|
|
105
|
+
// (theme, highlighter), and `marked.use` on the shared global instance
|
|
106
|
+
// accumulates extensions across calls.
|
|
107
|
+
const md = new Marked();
|
|
103
108
|
// Apply all markdown extensions (admonitions, footnotes, definition lists, mermaid)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
let html = await
|
|
109
|
+
md.use(...getAllExtensions());
|
|
110
|
+
md.use({ renderer, gfm: true });
|
|
111
|
+
let html = await md.parse(markdownContent);
|
|
107
112
|
// Append footnotes section if any were referenced
|
|
108
113
|
const footnotesHtml = renderFootnotes();
|
|
109
114
|
if (footnotesHtml) {
|
|
@@ -133,7 +138,9 @@ export async function parseMarkdown(content, options = {}) {
|
|
|
133
138
|
* ```
|
|
134
139
|
*/
|
|
135
140
|
export function extractToc(html, maxDepth = 3) {
|
|
136
|
-
|
|
141
|
+
// Heading content may contain inline HTML (bold, code, links) — capture
|
|
142
|
+
// everything up to the matching close tag, then strip tags for the text.
|
|
143
|
+
const headingRegex = /<h([1-6])[^>]*id="([^"]*)"[^>]*>([\s\S]*?)<\/h\1>/gi;
|
|
137
144
|
const toc = [];
|
|
138
145
|
let match;
|
|
139
146
|
while ((match = headingRegex.exec(html)) !== null) {
|
|
@@ -142,7 +149,7 @@ export function extractToc(html, maxDepth = 3) {
|
|
|
142
149
|
toc.push({
|
|
143
150
|
level,
|
|
144
151
|
id: match[2],
|
|
145
|
-
text: match[3],
|
|
152
|
+
text: match[3].replace(/<[^>]*>/g, ''),
|
|
146
153
|
});
|
|
147
154
|
}
|
|
148
155
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classic-homes/theme-docs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.50",
|
|
4
4
|
"description": "Markdown documentation components for the Classic theme system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/lib/index.js",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@classic-homes/theme-tokens": "*",
|
|
57
57
|
"clsx": "^2.1.0",
|
|
58
|
-
"js-yaml": "^4.
|
|
58
|
+
"js-yaml": "^4.3.2",
|
|
59
59
|
"marked": "^17.0.0",
|
|
60
60
|
"shiki": "^1.0.0",
|
|
61
61
|
"tailwind-merge": "^2.2.0",
|