@classic-homes/theme-docs 0.0.48 → 0.0.49

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
- onMount(async () => {
39
- try {
40
- const { highlightCode } = await import('../highlighter/index.js');
41
- highlighted = await highlightCode(code, language, { theme });
42
- loading = false;
43
- } catch (err) {
44
- // Log warning for debugging - falling back to plain code
45
- console.warn(
46
- '[docs] Failed to highlight code block, showing plain code:',
47
- err instanceof Error ? err.message : err
48
- );
49
- highlighted = `<pre class="shiki bg-muted rounded-lg p-4 overflow-x-auto"><code>${escapeHtml(code)}</code></pre>`;
50
- loading = false;
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
- onMount(async () => {
39
- if (!containerRef) return;
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
- try {
42
- // Dynamically import mermaid to avoid SSR issues
43
- const mermaid = await import('mermaid');
45
+ let cancelled = false;
44
46
 
45
- // Initialize mermaid with theme
46
- mermaid.default.initialize({
47
- startOnLoad: false,
48
- theme: theme,
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
- // Render the diagram
53
- const { svg } = await mermaid.default.render(id, code);
54
- containerRef.innerHTML = svg;
55
- rendered = true;
56
- } catch (err) {
57
- console.warn(
58
- '[docs] Failed to render Mermaid diagram:',
59
- err instanceof Error ? err.message : err
60
- );
61
- error = err instanceof Error ? err.message : 'Failed to render diagram';
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
 
@@ -1,4 +1,4 @@
1
- import { marked, Renderer } from 'marked';
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
- return `<h${depth} id="${id}">${text}</h${depth}>`;
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
- marked.use(...getAllExtensions());
105
- marked.use({ renderer, gfm: true });
106
- let html = await marked(markdownContent);
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
- const headingRegex = /<h([1-6])[^>]*id="([^"]*)"[^>]*>([^<]*)<\/h[1-6]>/gi;
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.48",
3
+ "version": "0.0.49",
4
4
  "description": "Markdown documentation components for the Classic theme system",
5
5
  "type": "module",
6
6
  "main": "./dist/lib/index.js",