prev-cli 0.1.7 → 0.2.0

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/theme/Layout.tsx +114 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prev-cli",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "Transform MDX directories into beautiful documentation websites",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -3,13 +3,56 @@ import { Outlet, useLocation } from 'react-router-dom'
3
3
  import { Sidebar } from './Sidebar'
4
4
  import './styles.css'
5
5
 
6
+ // Simple hash function for cache keys
7
+ function hashCode(str: string): string {
8
+ let hash = 0
9
+ for (let i = 0; i < str.length; i++) {
10
+ const char = str.charCodeAt(i)
11
+ hash = ((hash << 5) - hash) + char
12
+ hash = hash & hash // Convert to 32bit integer
13
+ }
14
+ return hash.toString(36)
15
+ }
16
+
17
+ // In-memory cache for current session
18
+ const memoryCache = new Map<string, string>()
19
+
20
+ // Get cached SVG (memory first, then localStorage)
21
+ function getCachedSvg(type: string, code: string): string | null {
22
+ const key = `prev-diagram-${type}-${hashCode(code)}`
23
+
24
+ // Check memory cache first
25
+ if (memoryCache.has(key)) {
26
+ return memoryCache.get(key)!
27
+ }
28
+
29
+ // Check localStorage
30
+ try {
31
+ const cached = localStorage.getItem(key)
32
+ if (cached) {
33
+ memoryCache.set(key, cached) // Populate memory cache
34
+ return cached
35
+ }
36
+ } catch {}
37
+
38
+ return null
39
+ }
40
+
41
+ // Cache SVG (both memory and localStorage)
42
+ function cacheSvg(type: string, code: string, svg: string): void {
43
+ const key = `prev-diagram-${type}-${hashCode(code)}`
44
+ memoryCache.set(key, svg)
45
+ try {
46
+ localStorage.setItem(key, svg)
47
+ } catch {} // Ignore quota errors
48
+ }
49
+
6
50
  // Lazy-load and render mermaid diagrams
7
51
  async function renderMermaidDiagrams() {
8
52
  const codeBlocks = document.querySelectorAll('code.language-mermaid, code.hljs.language-mermaid')
9
53
  if (codeBlocks.length === 0) return
10
54
 
11
- const mermaid = await import('mermaid')
12
- mermaid.default.initialize({ startOnLoad: false, theme: 'neutral' })
55
+ let mermaidModule: any = null
13
56
 
14
57
  for (const block of codeBlocks) {
15
58
  const pre = block.parentElement as HTMLElement
@@ -20,10 +63,25 @@ async function renderMermaidDiagrams() {
20
63
  const container = document.createElement('div')
21
64
  container.className = 'mermaid-diagram'
22
65
 
66
+ // Check cache first
67
+ const cached = getCachedSvg('mermaid', code)
68
+ if (cached) {
69
+ container.innerHTML = cached
70
+ pre.style.display = 'none'
71
+ pre.insertAdjacentElement('afterend', container)
72
+ continue
73
+ }
74
+
75
+ // Load mermaid only if needed
76
+ if (!mermaidModule) {
77
+ mermaidModule = await import('mermaid')
78
+ mermaidModule.default.initialize({ startOnLoad: false, theme: 'neutral' })
79
+ }
80
+
23
81
  try {
24
- const { svg } = await mermaid.default.render(`mermaid-${Math.random().toString(36).slice(2)}`, code)
82
+ const { svg } = await mermaidModule.default.render(`mermaid-${Math.random().toString(36).slice(2)}`, code)
25
83
  container.innerHTML = svg
26
- // Hide original instead of replacing (avoids React DOM conflicts)
84
+ cacheSvg('mermaid', code, svg)
27
85
  pre.style.display = 'none'
28
86
  pre.insertAdjacentElement('afterend', container)
29
87
  } catch (e) {
@@ -37,36 +95,62 @@ async function renderD2Diagrams() {
37
95
  const codeBlocks = document.querySelectorAll('code.language-d2, code.hljs.language-d2')
38
96
  if (codeBlocks.length === 0) return
39
97
 
40
- try {
41
- const d2Module = await import('@terrastruct/d2')
42
- const { D2 } = d2Module
43
- const d2 = new D2()
98
+ let d2Instance: any = null
44
99
 
45
- for (const block of codeBlocks) {
46
- const pre = block.parentElement as HTMLElement
47
- if (!pre || pre.dataset.rendered) continue
48
- pre.dataset.rendered = 'true'
100
+ for (const block of codeBlocks) {
101
+ const pre = block.parentElement as HTMLElement
102
+ if (!pre || pre.dataset.rendered) continue
103
+ pre.dataset.rendered = 'true'
49
104
 
50
- const code = block.textContent || ''
51
- const container = document.createElement('div')
52
- container.className = 'd2-diagram'
105
+ const code = block.textContent || ''
106
+ const container = document.createElement('div')
107
+ container.className = 'd2-diagram'
108
+
109
+ // Check cache first
110
+ const cached = getCachedSvg('d2', code)
111
+ if (cached) {
112
+ container.innerHTML = cached
113
+ pre.style.display = 'none'
114
+ pre.insertAdjacentElement('afterend', container)
115
+ continue
116
+ }
53
117
 
118
+ // Load D2 only if needed
119
+ if (!d2Instance) {
54
120
  try {
55
- const result = await d2.compile(code)
56
- const svg = await d2.render(result.diagram, result.renderOptions)
57
- container.innerHTML = svg
58
- // Hide original instead of replacing (avoids React DOM conflicts)
59
- pre.style.display = 'none'
60
- pre.insertAdjacentElement('afterend', container)
121
+ const d2Module = await import('@terrastruct/d2')
122
+ const { D2 } = d2Module
123
+ d2Instance = new D2()
61
124
  } catch (e) {
62
- console.error('D2 render error:', e)
125
+ console.error('D2 library load error:', e)
126
+ return
63
127
  }
64
128
  }
65
- } catch (e) {
66
- console.error('D2 library load error:', e)
129
+
130
+ try {
131
+ const result = await d2Instance.compile(code)
132
+ const svg = await d2Instance.render(result.diagram, result.renderOptions)
133
+ container.innerHTML = svg
134
+ cacheSvg('d2', code, svg)
135
+ pre.style.display = 'none'
136
+ pre.insertAdjacentElement('afterend', container)
137
+ } catch (e) {
138
+ console.error('D2 render error:', e)
139
+ }
67
140
  }
68
141
  }
69
142
 
143
+ // Clean up rendered diagrams (for navigation)
144
+ function cleanupDiagrams() {
145
+ // Remove all rendered diagram containers
146
+ document.querySelectorAll('.mermaid-diagram, .d2-diagram').forEach(el => el.remove())
147
+ // Reset rendered state on pre elements and restore visibility
148
+ document.querySelectorAll('pre[data-rendered="true"]').forEach(el => {
149
+ delete (el as HTMLElement).dataset.rendered;
150
+ (el as HTMLElement).style.display = ''
151
+ })
152
+ }
153
+
70
154
  // Render all diagrams
71
155
  async function renderDiagrams() {
72
156
  await Promise.all([
@@ -79,9 +163,14 @@ export function Layout() {
79
163
  const location = useLocation()
80
164
 
81
165
  useEffect(() => {
166
+ // Clean up old diagrams before rendering new ones
167
+ cleanupDiagrams()
82
168
  // Render diagrams after content loads
83
169
  const timer = setTimeout(renderDiagrams, 100)
84
- return () => clearTimeout(timer)
170
+ return () => {
171
+ clearTimeout(timer)
172
+ cleanupDiagrams()
173
+ }
85
174
  }, [location.pathname])
86
175
 
87
176
  return (