@limcpf/everything-is-a-markdown 0.6.2 → 0.6.4
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 +5 -1
- package/README.md +5 -1
- package/package.json +40 -40
- package/src/build.ts +1 -1
- package/src/markdown.ts +2 -14
- package/src/runtime/app.css +40 -12
- package/src/runtime/app.js +32 -0
package/README.ko.md
CHANGED
|
@@ -191,7 +191,11 @@ flowchart LR
|
|
|
191
191
|
```
|
|
192
192
|
````
|
|
193
193
|
|
|
194
|
-
문서 전환 시에도 해당 블록을 다시 렌더링합니다.
|
|
194
|
+
문서 전환 시에도 해당 블록을 다시 렌더링합니다.
|
|
195
|
+
Mermaid fence는 일반 코드 블록 UI와 분리된 전용 컨테이너(`.mermaid-block`)에서 렌더링되므로 코드 헤더/파일명/복사 버튼이 표시되지 않습니다.
|
|
196
|
+
렌더된 SVG는 데스크톱/모바일 모두에서 중앙 정렬되며 `min(100%, 720px)` 기준으로 자동 축소됩니다.
|
|
197
|
+
본문 이미지도 동일한 폭 정책을 적용해 글 읽기 흐름을 유지합니다.
|
|
198
|
+
설정에서 Mermaid를 비활성화하거나 CDN 로드가 실패하면, 같은 컨테이너 안에서 소스 코드 텍스트를 유지하고 하단에 경고 메시지를 표시합니다.
|
|
195
199
|
|
|
196
200
|
`blog.config.ts`에서 설정:
|
|
197
201
|
|
package/README.md
CHANGED
|
@@ -191,7 +191,11 @@ 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 automatically constrained to `min(100%, 720px)` on both desktop and mobile.
|
|
197
|
+
Regular content images inside the viewer follow the same width policy to keep reading flow stable.
|
|
198
|
+
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
199
|
|
|
196
200
|
Configuration options (`blog.config.ts`):
|
|
197
201
|
|
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.4",
|
|
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/build.ts
CHANGED
|
@@ -979,7 +979,7 @@ async function writeOutputIfChanged(
|
|
|
979
979
|
|
|
980
980
|
const outputPath = path.join(context.outDir, relOutputPath);
|
|
981
981
|
const unchanged = context.previousHashes[relOutputPath] === outputHash;
|
|
982
|
-
if (unchanged) {
|
|
982
|
+
if (unchanged && (await Bun.file(outputPath).exists())) {
|
|
983
983
|
return;
|
|
984
984
|
}
|
|
985
985
|
|
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
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
--mobile-toggle-fg: #ffffff;
|
|
67
67
|
--content-heading-accent-soft: rgba(136, 57, 239, 0.32);
|
|
68
68
|
--content-heading-subtle: var(--latte-subtext1);
|
|
69
|
+
--content-visual-max-width: 720px;
|
|
69
70
|
--desktop-sidebar-default: 420px;
|
|
70
71
|
--desktop-sidebar-min: 320px;
|
|
71
72
|
--desktop-viewer-min: 680px;
|
|
@@ -1098,29 +1099,54 @@ body.mobile-toggle-left .mobile-menu-toggle {
|
|
|
1098
1099
|
tab-size: 2;
|
|
1099
1100
|
}
|
|
1100
1101
|
|
|
1101
|
-
.viewer-content .
|
|
1102
|
+
.viewer-content .mermaid-block {
|
|
1103
|
+
margin: 1.5rem 0;
|
|
1104
|
+
padding: 16px 18px;
|
|
1105
|
+
border-radius: 12px;
|
|
1106
|
+
border: 1px solid var(--latte-surface0);
|
|
1107
|
+
background: linear-gradient(180deg, var(--latte-base), var(--latte-mantle));
|
|
1108
|
+
box-shadow: 0 3px 10px rgba(15, 23, 42, 0.08);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
.viewer-content .mermaid-block pre.mermaid {
|
|
1102
1112
|
margin: 0;
|
|
1113
|
+
padding: 0;
|
|
1103
1114
|
border: none;
|
|
1104
1115
|
border-radius: 0;
|
|
1105
|
-
|
|
1116
|
+
box-shadow: none;
|
|
1106
1117
|
background: transparent !important;
|
|
1107
|
-
color:
|
|
1118
|
+
color: var(--latte-subtext1);
|
|
1119
|
+
font-family: ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
1120
|
+
font-size: 0.9rem;
|
|
1121
|
+
line-height: 1.6;
|
|
1108
1122
|
white-space: pre-wrap;
|
|
1109
1123
|
word-break: break-word;
|
|
1124
|
+
overflow-wrap: anywhere;
|
|
1125
|
+
overflow: visible;
|
|
1110
1126
|
}
|
|
1111
1127
|
|
|
1112
|
-
.viewer-content .
|
|
1113
|
-
|
|
1114
|
-
|
|
1128
|
+
.viewer-content .mermaid-block pre.mermaid[data-mermaid-rendered="true"] {
|
|
1129
|
+
display: flex;
|
|
1130
|
+
justify-content: center;
|
|
1131
|
+
align-items: center;
|
|
1132
|
+
color: inherit;
|
|
1133
|
+
font-family: inherit;
|
|
1134
|
+
white-space: normal;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
.viewer-content .mermaid-block pre.mermaid[data-mermaid-rendered="true"] svg {
|
|
1115
1138
|
display: block;
|
|
1139
|
+
width: auto;
|
|
1140
|
+
max-width: min(100%, var(--content-visual-max-width));
|
|
1141
|
+
height: auto;
|
|
1142
|
+
margin: 0 auto;
|
|
1116
1143
|
}
|
|
1117
1144
|
|
|
1118
|
-
.viewer-content .mermaid-render-error {
|
|
1119
|
-
margin: 0;
|
|
1120
|
-
padding: 10px
|
|
1145
|
+
.viewer-content .mermaid-block .mermaid-render-error {
|
|
1146
|
+
margin: 12px 0 0;
|
|
1147
|
+
padding: 10px 12px;
|
|
1121
1148
|
border: 1px solid rgba(244, 63, 94, 0.35);
|
|
1122
|
-
border-radius:
|
|
1123
|
-
border-top: none;
|
|
1149
|
+
border-radius: 8px;
|
|
1124
1150
|
color: var(--latte-red);
|
|
1125
1151
|
background: rgba(244, 63, 94, 0.08);
|
|
1126
1152
|
font-size: 0.84rem;
|
|
@@ -1175,8 +1201,10 @@ body.mobile-toggle-left .mobile-menu-toggle {
|
|
|
1175
1201
|
/* Images */
|
|
1176
1202
|
.viewer-content img {
|
|
1177
1203
|
display: block;
|
|
1178
|
-
|
|
1204
|
+
width: auto;
|
|
1205
|
+
max-width: min(100%, var(--content-visual-max-width));
|
|
1179
1206
|
height: auto;
|
|
1207
|
+
margin: 0 auto;
|
|
1180
1208
|
border-radius: 8px;
|
|
1181
1209
|
border: 1px solid var(--latte-surface0);
|
|
1182
1210
|
}
|
package/src/runtime/app.js
CHANGED
|
@@ -13,6 +13,7 @@ const DESKTOP_SPLITTER_STEP = 24;
|
|
|
13
13
|
const DEFAULT_BRANCH = "dev";
|
|
14
14
|
const DEFAULT_SITE_TITLE = "File-System Blog";
|
|
15
15
|
const BRANCH_KEY = "fsblog.branch";
|
|
16
|
+
const APP_READY_STATE_ATTR = "data-app-ready";
|
|
16
17
|
const FOCUSABLE_SELECTOR =
|
|
17
18
|
'a[href], button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
18
19
|
const MERMAID_CDN = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js";
|
|
@@ -103,6 +104,23 @@ function showMermaidError(preview, message) {
|
|
|
103
104
|
preview.parentElement.appendChild(createMermaidLoadError(message));
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
function normalizeRenderedMermaidSvg(block) {
|
|
108
|
+
if (!(block instanceof HTMLElement)) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const svg = block.querySelector("svg");
|
|
113
|
+
if (!(svg instanceof SVGElement)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
svg.style.display = "block";
|
|
118
|
+
svg.style.width = "auto";
|
|
119
|
+
svg.style.height = "auto";
|
|
120
|
+
svg.style.margin = "0 auto";
|
|
121
|
+
svg.style.maxWidth = "min(100%, var(--content-visual-max-width, 720px))";
|
|
122
|
+
}
|
|
123
|
+
|
|
106
124
|
function parseMermaidNodes() {
|
|
107
125
|
const contentEl = document.getElementById("viewer-content");
|
|
108
126
|
if (!(contentEl instanceof HTMLElement)) {
|
|
@@ -231,10 +249,12 @@ async function renderMermaidBlocks(config) {
|
|
|
231
249
|
try {
|
|
232
250
|
if (typeof mermaid.run === "function") {
|
|
233
251
|
await mermaid.run({ nodes: [block] });
|
|
252
|
+
normalizeRenderedMermaidSvg(block);
|
|
234
253
|
continue;
|
|
235
254
|
}
|
|
236
255
|
if (typeof mermaid.init === "function") {
|
|
237
256
|
await mermaid.init({ startOnLoad: false }, [block]);
|
|
257
|
+
normalizeRenderedMermaidSvg(block);
|
|
238
258
|
continue;
|
|
239
259
|
}
|
|
240
260
|
throw new Error("Mermaid 렌더러 API가 존재하지 않습니다.");
|
|
@@ -1059,7 +1079,16 @@ function renderBacklinks(doc, pathBase) {
|
|
|
1059
1079
|
return html;
|
|
1060
1080
|
}
|
|
1061
1081
|
|
|
1082
|
+
function setAppReadyState(state) {
|
|
1083
|
+
if (!(document.documentElement instanceof HTMLElement)) {
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
document.documentElement.setAttribute(APP_READY_STATE_ATTR, state);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1062
1089
|
async function start() {
|
|
1090
|
+
setAppReadyState("booting");
|
|
1091
|
+
|
|
1063
1092
|
const treeRoot = document.getElementById("tree-root");
|
|
1064
1093
|
const appRoot = document.querySelector(".app-root");
|
|
1065
1094
|
const splitter = document.getElementById("app-splitter");
|
|
@@ -1880,6 +1909,7 @@ async function start() {
|
|
|
1880
1909
|
const initialRoute = currentRoute === "/" ? pickHomeRoute(view) : currentRoute;
|
|
1881
1910
|
handleLayoutChange();
|
|
1882
1911
|
await state.navigate(initialRoute, currentRoute === "/" && initialRoute !== "/");
|
|
1912
|
+
setAppReadyState("ready");
|
|
1883
1913
|
|
|
1884
1914
|
window.addEventListener("popstate", async () => {
|
|
1885
1915
|
await state.navigate(resolveRouteFromLocation(view.routeMap, pathBase), false);
|
|
@@ -1887,6 +1917,8 @@ async function start() {
|
|
|
1887
1917
|
}
|
|
1888
1918
|
|
|
1889
1919
|
start().catch((error) => {
|
|
1920
|
+
setAppReadyState("error");
|
|
1921
|
+
|
|
1890
1922
|
const contentEl = document.getElementById("viewer-content");
|
|
1891
1923
|
if (contentEl) {
|
|
1892
1924
|
const message = error instanceof Error ? error.message : String(error);
|