@barzhsieh/nuxt-content-mermaid 2.1.1 → 2.1.3
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/README.md +2 -0
- package/README.zh-TW.md +3 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +100 -6
- package/dist/runtime/components/Mermaid.vue +2 -1
- package/dist/runtime/components/Spinner.vue +8 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
[English](./README.md) | [中文](./README.zh-TW.md)
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@barzhsieh/nuxt-content-mermaid)
|
|
4
|
+
|
|
3
5
|
# nuxt-content-mermaid
|
|
4
6
|
|
|
5
7
|
[![npm version][npm-version-src]][npm-version-href]
|
package/README.zh-TW.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
[
|
|
1
|
+
[中文](./README.zh-TW.md) | [English](./README.md)
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@barzhsieh/nuxt-content-mermaid)
|
|
2
4
|
|
|
3
5
|
# nuxt-content-mermaid
|
|
4
6
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -2,8 +2,8 @@ import { defineNuxtModule, useLogger, createResolver, addVitePlugin, addPlugin,
|
|
|
2
2
|
import { defu } from 'defu';
|
|
3
3
|
import { DEFAULT_MERMAID_CONFIG, DEFAULT_DARK_THEME, DEFAULT_LIGHT_THEME, DEFAULT_FRONTMATTER_CONFIG_KEY } from '../dist/runtime/constants.js';
|
|
4
4
|
|
|
5
|
-
const MERMAID_BLOCK = /```mermaid([\s\S]*?)```/gi;
|
|
6
5
|
const SANITIZE_URL_PACKAGE = "@braintree/sanitize-url";
|
|
6
|
+
const MERMAID_FENCE_RE = /^[ \t]*(?:`{3,}|~{3,})[ \t]*mermaid(?:$|[ \t{[])/im;
|
|
7
7
|
const DEFAULTS = {
|
|
8
8
|
enabled: true,
|
|
9
9
|
loader: {
|
|
@@ -106,7 +106,7 @@ export {}
|
|
|
106
106
|
nuxt.hook("content:file:beforeParse", (ctx) => {
|
|
107
107
|
const { file } = ctx;
|
|
108
108
|
if (!file.id?.endsWith(".md")) return;
|
|
109
|
-
if (!file.body
|
|
109
|
+
if (!MERMAID_FENCE_RE.test(file.body)) return;
|
|
110
110
|
file.body = transformMermaidCodeBlocks(
|
|
111
111
|
file.body,
|
|
112
112
|
baseMermaidComponentName
|
|
@@ -115,12 +115,106 @@ export {}
|
|
|
115
115
|
}
|
|
116
116
|
});
|
|
117
117
|
function transformMermaidCodeBlocks(body, componentName, frontmatterConfigKey = DEFAULT_FRONTMATTER_CONFIG_KEY) {
|
|
118
|
-
|
|
118
|
+
const newline = body.includes("\r\n") ? "\r\n" : "\n";
|
|
119
|
+
const lines = body.split(newline);
|
|
120
|
+
const output = [];
|
|
121
|
+
let inFence = false;
|
|
122
|
+
let fenceChar = null;
|
|
123
|
+
let fenceLength = 0;
|
|
124
|
+
const isClosingFence = (line) => {
|
|
125
|
+
if (!fenceChar) return false;
|
|
126
|
+
const trimmed = line.trim();
|
|
127
|
+
if (trimmed.length < fenceLength) return false;
|
|
128
|
+
for (const char of trimmed) {
|
|
129
|
+
if (char !== fenceChar) return false;
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
};
|
|
133
|
+
const isFenceMarkerChar = (char) => char === "`" || char === "~";
|
|
134
|
+
const parseFence = (line) => {
|
|
135
|
+
let index = 0;
|
|
136
|
+
while (index < line.length && (line[index] === " " || line[index] === " "))
|
|
137
|
+
index++;
|
|
138
|
+
const indent = line.slice(0, index);
|
|
139
|
+
const markerChar = line[index];
|
|
140
|
+
if (!isFenceMarkerChar(markerChar))
|
|
141
|
+
return null;
|
|
142
|
+
let markerEnd = index;
|
|
143
|
+
while (markerEnd < line.length && line[markerEnd] === markerChar)
|
|
144
|
+
markerEnd++;
|
|
145
|
+
const markerLength = markerEnd - index;
|
|
146
|
+
if (markerLength < 3)
|
|
147
|
+
return null;
|
|
148
|
+
return {
|
|
149
|
+
indent,
|
|
150
|
+
markerChar,
|
|
151
|
+
markerLength,
|
|
152
|
+
info: line.slice(markerEnd).trim()
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
const isMermaidFence = (info) => {
|
|
156
|
+
if (!info) return false;
|
|
157
|
+
const lower = info.toLowerCase();
|
|
158
|
+
if (!lower.startsWith("mermaid")) return false;
|
|
159
|
+
const next = lower.slice("mermaid".length, "mermaid".length + 1);
|
|
160
|
+
return next === "" || /\s/.test(next) || next === "{" || next === "[";
|
|
161
|
+
};
|
|
162
|
+
const buildComponentTag = (encoded) => `<${componentName} :config="${frontmatterConfigKey}" code="${encoded}"></${componentName}>`;
|
|
163
|
+
for (let index = 0; index < lines.length; index++) {
|
|
164
|
+
const line = lines[index];
|
|
165
|
+
if (inFence) {
|
|
166
|
+
output.push(line);
|
|
167
|
+
if (isClosingFence(line)) {
|
|
168
|
+
inFence = false;
|
|
169
|
+
fenceChar = null;
|
|
170
|
+
fenceLength = 0;
|
|
171
|
+
}
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const fence = parseFence(line);
|
|
175
|
+
if (!fence) {
|
|
176
|
+
output.push(line);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (!isMermaidFence(fence.info)) {
|
|
180
|
+
inFence = true;
|
|
181
|
+
fenceChar = fence.markerChar;
|
|
182
|
+
fenceLength = fence.markerLength;
|
|
183
|
+
output.push(line);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
let closingIndex = -1;
|
|
187
|
+
for (let j = index + 1; j < lines.length; j++) {
|
|
188
|
+
const trimmed = lines[j].trim();
|
|
189
|
+
if (trimmed.length < fence.markerLength) continue;
|
|
190
|
+
let isFence = true;
|
|
191
|
+
for (const char of trimmed) {
|
|
192
|
+
if (char !== fence.markerChar) {
|
|
193
|
+
isFence = false;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (isFence) {
|
|
198
|
+
closingIndex = j;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (closingIndex === -1) {
|
|
203
|
+
output.push(line);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const rawCode = lines.slice(index + 1, closingIndex).join(newline);
|
|
119
207
|
const code = rawCode.trim();
|
|
120
|
-
if (!code)
|
|
208
|
+
if (!code) {
|
|
209
|
+
output.push(...lines.slice(index, closingIndex + 1));
|
|
210
|
+
index = closingIndex;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
121
213
|
const encoded = encodeURIComponent(code);
|
|
122
|
-
|
|
123
|
-
|
|
214
|
+
output.push(`${fence.indent}${buildComponentTag(encoded)}`);
|
|
215
|
+
index = closingIndex;
|
|
216
|
+
}
|
|
217
|
+
return output.join(newline);
|
|
124
218
|
}
|
|
125
219
|
|
|
126
220
|
export { module$1 as default, transformMermaidCodeBlocks };
|
|
@@ -298,6 +298,7 @@ watch(decodedCode, (newCode) => {
|
|
|
298
298
|
<div
|
|
299
299
|
v-else
|
|
300
300
|
class="mermaid-wrapper"
|
|
301
|
+
:class="{ 'ncm-is-loading': isLoading && !hasRenderedOnce }"
|
|
301
302
|
>
|
|
302
303
|
<div ref="mermaidContainer">
|
|
303
304
|
<!-- Initially show the slot's <pre><code> for SSR; client-side rendering will replace it with the mermaid SVG -->
|
|
@@ -337,5 +338,5 @@ watch(decodedCode, (newCode) => {
|
|
|
337
338
|
</template>
|
|
338
339
|
|
|
339
340
|
<style scoped>
|
|
340
|
-
.mermaid-wrapper{position:relative;text-align:center}.mermaid-wrapper :deep(code),.mermaid-wrapper :deep(pre){margin:0;
|
|
341
|
+
.mermaid-wrapper{--ncm-loading-min-height:160px;position:relative;text-align:center}.mermaid-wrapper.ncm-is-loading{min-height:var(--ncm-loading-min-height,160px)}.mermaid-wrapper :deep(code),.mermaid-wrapper :deep(pre){height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border:0;clip-path:inset(50%);white-space:nowrap}.mermaid-wrapper :deep(svg){display:inline-block;vertical-align:middle}.mermaid-error-default{text-align:center}.mermaid:not([data-processed]){color:transparent;min-height:10px}.mermaid-wrapper :deep(.edgeLabel),.mermaid-wrapper :deep(.label),.mermaid-wrapper :deep(.nodeLabel){overflow:visible!important;white-space:normal!important}.mermaid-wrapper :deep(foreignObject){overflow:visible!important}
|
|
341
342
|
</style>
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<svg
|
|
3
|
-
class="spinner"
|
|
3
|
+
class="ncm-spinner"
|
|
4
4
|
viewBox="0 0 50 50"
|
|
5
5
|
>
|
|
6
6
|
<circle
|
|
7
|
-
class="
|
|
7
|
+
class="ncm-spinner__path"
|
|
8
8
|
cx="25"
|
|
9
9
|
cy="25"
|
|
10
10
|
r="20"
|
|
11
11
|
fill="none"
|
|
12
12
|
stroke-width="5"
|
|
13
|
+
stroke="rgb(197, 197, 197)"
|
|
14
|
+
stroke-linecap="round"
|
|
15
|
+
stroke-dasharray="1 150"
|
|
16
|
+
stroke-dashoffset="0"
|
|
13
17
|
/>
|
|
14
18
|
</svg>
|
|
15
19
|
</template>
|
|
@@ -20,6 +24,6 @@ export default {
|
|
|
20
24
|
};
|
|
21
25
|
</script>
|
|
22
26
|
|
|
23
|
-
<style>
|
|
24
|
-
.spinner{--radius:20px;animation:rotate 2s linear infinite;height:calc(var(--radius)*2);left:50%;margin:calc(var(--radius)*-1) 0 0 calc(var(--radius)*-1);position:absolute;top:50%;width:calc(var(--radius)*2);z-index:2}.
|
|
27
|
+
<style scoped>
|
|
28
|
+
.ncm-spinner{--radius:20px;animation:rotate 2s linear infinite;height:calc(var(--radius)*2);left:50%;margin:calc(var(--radius)*-1) 0 0 calc(var(--radius)*-1);position:absolute;top:50%;transform-origin:50% 50%;width:calc(var(--radius)*2);z-index:2}.ncm-spinner__path{animation:dash 1.5s ease-in-out infinite}@keyframes rotate{to{transform:rotate(1turn)}}@keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}to{stroke-dasharray:90,150;stroke-dashoffset:-124}}
|
|
25
29
|
</style>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barzhsieh/nuxt-content-mermaid",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "A Nuxt module designed for integrating Mermaid with `@nuxt/content`. It automatically converts `mermaid` code blocks in Markdown into responsive chart components, and supports lazy loading and dark/light theme switching.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nuxt",
|