prev-cli 0.1.8 → 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.
- package/package.json +1 -1
- package/src/theme/Layout.tsx +97 -24
package/package.json
CHANGED
package/src/theme/Layout.tsx
CHANGED
|
@@ -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
|
-
|
|
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
|
|
82
|
+
const { svg } = await mermaidModule.default.render(`mermaid-${Math.random().toString(36).slice(2)}`, code)
|
|
25
83
|
container.innerHTML = svg
|
|
26
|
-
|
|
84
|
+
cacheSvg('mermaid', code, svg)
|
|
27
85
|
pre.style.display = 'none'
|
|
28
86
|
pre.insertAdjacentElement('afterend', container)
|
|
29
87
|
} catch (e) {
|
|
@@ -37,33 +95,48 @@ 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
|
-
|
|
41
|
-
const d2Module = await import('@terrastruct/d2')
|
|
42
|
-
const { D2 } = d2Module
|
|
43
|
-
const d2 = new D2()
|
|
98
|
+
let d2Instance: any = null
|
|
44
99
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
105
|
+
const code = block.textContent || ''
|
|
106
|
+
const container = document.createElement('div')
|
|
107
|
+
container.className = 'd2-diagram'
|
|
53
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
|
+
}
|
|
117
|
+
|
|
118
|
+
// Load D2 only if needed
|
|
119
|
+
if (!d2Instance) {
|
|
54
120
|
try {
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
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
|
|
125
|
+
console.error('D2 library load error:', e)
|
|
126
|
+
return
|
|
63
127
|
}
|
|
64
128
|
}
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|