@limcpf/everything-is-a-markdown 0.6.1 → 0.6.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.ko.md +4 -1
- package/README.md +4 -1
- package/package.json +40 -40
- package/src/markdown.ts +2 -14
- package/src/runtime/app.css +35 -10
- package/src/runtime/app.js +58 -8
package/README.ko.md
CHANGED
|
@@ -191,7 +191,10 @@ flowchart LR
|
|
|
191
191
|
```
|
|
192
192
|
````
|
|
193
193
|
|
|
194
|
-
문서 전환 시에도 해당 블록을 다시 렌더링합니다.
|
|
194
|
+
문서 전환 시에도 해당 블록을 다시 렌더링합니다.
|
|
195
|
+
Mermaid fence는 일반 코드 블록 UI와 분리된 전용 컨테이너(`.mermaid-block`)에서 렌더링되므로 코드 헤더/파일명/복사 버튼이 표시되지 않습니다.
|
|
196
|
+
렌더된 SVG는 데스크톱/모바일 모두에서 컨테이너 기준으로 중앙 정렬되고 폭이 자동 보정됩니다.
|
|
197
|
+
설정에서 Mermaid를 비활성화하거나 CDN 로드가 실패하면, 같은 컨테이너 안에서 소스 코드 텍스트를 유지하고 하단에 경고 메시지를 표시합니다.
|
|
195
198
|
|
|
196
199
|
`blog.config.ts`에서 설정:
|
|
197
200
|
|
package/README.md
CHANGED
|
@@ -191,7 +191,10 @@ flowchart LR
|
|
|
191
191
|
```
|
|
192
192
|
````
|
|
193
193
|
|
|
194
|
-
Rendering happens in the browser on first load and when navigating to another document.
|
|
194
|
+
Rendering happens in the browser on first load and when navigating to another document.
|
|
195
|
+
Mermaid fences are rendered inside a dedicated diagram container (`.mermaid-block`) instead of the regular code block UI, so they do not show code headers, filenames, or copy buttons.
|
|
196
|
+
Rendered SVG output is centered and constrained to the container width on both desktop and mobile.
|
|
197
|
+
If Mermaid is disabled in config or CDN loading fails, the source block is shown as-is in the same container and a warning message appears below it.
|
|
195
198
|
|
|
196
199
|
Configuration options (`blog.config.ts`):
|
|
197
200
|
|
package/package.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
2
|
+
"name": "@limcpf/everything-is-a-markdown",
|
|
3
|
+
"version": "0.6.3",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"eiam": "src/cli.ts"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"README.md",
|
|
13
|
+
"bun.lock",
|
|
14
|
+
"tsconfig.json"
|
|
15
|
+
],
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"blog": "bun run src/cli.ts",
|
|
21
|
+
"build": "bun run src/cli.ts build",
|
|
22
|
+
"dev": "bun run src/cli.ts dev",
|
|
23
|
+
"clean": "bun run src/cli.ts clean",
|
|
24
|
+
"lint:md:publish": "bun run scripts/lint-published-markdown.ts",
|
|
25
|
+
"test:e2e": "playwright test",
|
|
26
|
+
"test:e2e:focus-trap": "playwright test tests/e2e/mobile-sidebar-focus-trap.spec.ts"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"chokidar": "^4.0.3",
|
|
30
|
+
"gray-matter": "^4.0.3",
|
|
31
|
+
"markdown-it": "^14.1.0",
|
|
32
|
+
"picomatch": "^4.0.2",
|
|
33
|
+
"shiki": "^3.2.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@playwright/test": "^1.58.2",
|
|
37
|
+
"@types/markdown-it": "^14.1.2",
|
|
38
|
+
"@types/picomatch": "^4.0.2",
|
|
39
|
+
"bun-types": "^1.3.8",
|
|
40
|
+
"markdownlint": "^0.40.0"
|
|
41
|
+
}
|
|
42
42
|
}
|
package/src/markdown.ts
CHANGED
|
@@ -167,21 +167,9 @@ function createMarkdownIt<L extends string, T extends string>(
|
|
|
167
167
|
|
|
168
168
|
if (lang === MERMAID_LANG) {
|
|
169
169
|
const source = escapeHtmlText(token.content);
|
|
170
|
-
|
|
171
|
-
return `<div class="code-block mermaid-block">
|
|
172
|
-
<div class="code-header">
|
|
173
|
-
<div class="code-dots">
|
|
174
|
-
<span class="dot dot-red"></span>
|
|
175
|
-
<span class="dot dot-yellow"></span>
|
|
176
|
-
<span class="dot dot-green"></span>
|
|
177
|
-
</div>
|
|
178
|
-
<span class="code-filename">${filename}</span>
|
|
179
|
-
<button class="code-copy" title="Copy code" data-code="${escapeHtmlAttr(token.content)}">
|
|
180
|
-
<span class="material-symbols-outlined">content_copy</span>
|
|
181
|
-
</button>
|
|
182
|
-
</div>
|
|
170
|
+
return `<figure class="mermaid-block">
|
|
183
171
|
<pre class="mermaid">${source}</pre>
|
|
184
|
-
</
|
|
172
|
+
</figure>`;
|
|
185
173
|
}
|
|
186
174
|
|
|
187
175
|
let codeHtml: string;
|
package/src/runtime/app.css
CHANGED
|
@@ -1098,29 +1098,54 @@ body.mobile-toggle-left .mobile-menu-toggle {
|
|
|
1098
1098
|
tab-size: 2;
|
|
1099
1099
|
}
|
|
1100
1100
|
|
|
1101
|
-
.viewer-content .
|
|
1101
|
+
.viewer-content .mermaid-block {
|
|
1102
|
+
margin: 1.5rem 0;
|
|
1103
|
+
padding: 16px 18px;
|
|
1104
|
+
border-radius: 12px;
|
|
1105
|
+
border: 1px solid var(--latte-surface0);
|
|
1106
|
+
background: linear-gradient(180deg, var(--latte-base), var(--latte-mantle));
|
|
1107
|
+
box-shadow: 0 3px 10px rgba(15, 23, 42, 0.08);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
.viewer-content .mermaid-block pre.mermaid {
|
|
1102
1111
|
margin: 0;
|
|
1112
|
+
padding: 0;
|
|
1103
1113
|
border: none;
|
|
1104
1114
|
border-radius: 0;
|
|
1105
|
-
|
|
1115
|
+
box-shadow: none;
|
|
1106
1116
|
background: transparent !important;
|
|
1107
|
-
color:
|
|
1117
|
+
color: var(--latte-subtext1);
|
|
1118
|
+
font-family: ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
1119
|
+
font-size: 0.9rem;
|
|
1120
|
+
line-height: 1.6;
|
|
1108
1121
|
white-space: pre-wrap;
|
|
1109
1122
|
word-break: break-word;
|
|
1123
|
+
overflow-wrap: anywhere;
|
|
1124
|
+
overflow: visible;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
.viewer-content .mermaid-block pre.mermaid[data-mermaid-rendered="true"] {
|
|
1128
|
+
display: flex;
|
|
1129
|
+
justify-content: center;
|
|
1130
|
+
align-items: center;
|
|
1131
|
+
color: inherit;
|
|
1132
|
+
font-family: inherit;
|
|
1133
|
+
white-space: normal;
|
|
1110
1134
|
}
|
|
1111
1135
|
|
|
1112
|
-
.viewer-content .
|
|
1136
|
+
.viewer-content .mermaid-block pre.mermaid[data-mermaid-rendered="true"] svg {
|
|
1137
|
+
display: block;
|
|
1113
1138
|
width: 100%;
|
|
1139
|
+
max-width: 100%;
|
|
1114
1140
|
height: auto;
|
|
1115
|
-
|
|
1141
|
+
margin: 0 auto;
|
|
1116
1142
|
}
|
|
1117
1143
|
|
|
1118
|
-
.viewer-content .mermaid-render-error {
|
|
1119
|
-
margin: 0;
|
|
1120
|
-
padding: 10px
|
|
1144
|
+
.viewer-content .mermaid-block .mermaid-render-error {
|
|
1145
|
+
margin: 12px 0 0;
|
|
1146
|
+
padding: 10px 12px;
|
|
1121
1147
|
border: 1px solid rgba(244, 63, 94, 0.35);
|
|
1122
|
-
border-radius:
|
|
1123
|
-
border-top: none;
|
|
1148
|
+
border-radius: 8px;
|
|
1124
1149
|
color: var(--latte-red);
|
|
1125
1150
|
background: rgba(244, 63, 94, 0.08);
|
|
1126
1151
|
font-size: 0.84rem;
|
package/src/runtime/app.js
CHANGED
|
@@ -103,6 +103,46 @@ function showMermaidError(preview, message) {
|
|
|
103
103
|
preview.parentElement.appendChild(createMermaidLoadError(message));
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
function parseSvgSize(value) {
|
|
107
|
+
const normalized = typeof value === "string" ? value.trim() : "";
|
|
108
|
+
if (!normalized) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (normalized.endsWith("%")) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const match = normalized.match(/^(\d+(?:\.\d+)?)/);
|
|
116
|
+
if (!match) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const parsed = Number(match[1]);
|
|
121
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return parsed;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function normalizeRenderedMermaidSvg(block) {
|
|
129
|
+
if (!(block instanceof HTMLElement)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const svg = block.querySelector("svg");
|
|
134
|
+
if (!(svg instanceof SVGElement)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const intrinsicWidth = parseSvgSize(svg.getAttribute("width"));
|
|
139
|
+
svg.style.display = "block";
|
|
140
|
+
svg.style.width = "100%";
|
|
141
|
+
svg.style.height = "auto";
|
|
142
|
+
svg.style.margin = "0 auto";
|
|
143
|
+
svg.style.maxWidth = intrinsicWidth ? `${intrinsicWidth}px` : "100%";
|
|
144
|
+
}
|
|
145
|
+
|
|
106
146
|
function parseMermaidNodes() {
|
|
107
147
|
const contentEl = document.getElementById("viewer-content");
|
|
108
148
|
if (!(contentEl instanceof HTMLElement)) {
|
|
@@ -226,15 +266,25 @@ async function renderMermaidBlocks(config) {
|
|
|
226
266
|
}
|
|
227
267
|
return;
|
|
228
268
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
269
|
+
|
|
270
|
+
for (const block of blocks) {
|
|
271
|
+
try {
|
|
272
|
+
if (typeof mermaid.run === "function") {
|
|
273
|
+
await mermaid.run({ nodes: [block] });
|
|
274
|
+
normalizeRenderedMermaidSvg(block);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (typeof mermaid.init === "function") {
|
|
278
|
+
await mermaid.init({ startOnLoad: false }, [block]);
|
|
279
|
+
normalizeRenderedMermaidSvg(block);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
throw new Error("Mermaid 렌더러 API가 존재하지 않습니다.");
|
|
283
|
+
} catch (error) {
|
|
284
|
+
const message = `Mermaid 렌더링 실패: ${error instanceof Error ? error.message : String(error)}`;
|
|
285
|
+
showMermaidError(block, message);
|
|
286
|
+
}
|
|
236
287
|
}
|
|
237
|
-
throw new Error("Mermaid 렌더러 API가 존재하지 않습니다.");
|
|
238
288
|
} catch (error) {
|
|
239
289
|
const message = `Mermaid 렌더링 실패: ${error instanceof Error ? error.message : String(error)}`;
|
|
240
290
|
for (const block of blocks) {
|