@limcpf/everything-is-a-markdown 0.6.3 → 0.6.5
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 +2 -1
- package/README.md +2 -1
- package/package.json +1 -1
- package/src/build.ts +1 -1
- package/src/runtime/app.css +19 -3
- package/src/runtime/app.js +51 -25
package/README.ko.md
CHANGED
|
@@ -193,7 +193,8 @@ flowchart LR
|
|
|
193
193
|
|
|
194
194
|
문서 전환 시에도 해당 블록을 다시 렌더링합니다.
|
|
195
195
|
Mermaid fence는 일반 코드 블록 UI와 분리된 전용 컨테이너(`.mermaid-block`)에서 렌더링되므로 코드 헤더/파일명/복사 버튼이 표시되지 않습니다.
|
|
196
|
-
렌더된 SVG는 데스크톱/모바일 모두에서
|
|
196
|
+
렌더된 SVG는 데스크톱/모바일 모두에서 중앙 정렬되며 `min(100%, 720px)` 기준으로 자동 축소됩니다.
|
|
197
|
+
본문 이미지도 동일한 폭 정책을 적용해 글 읽기 흐름을 유지합니다.
|
|
197
198
|
설정에서 Mermaid를 비활성화하거나 CDN 로드가 실패하면, 같은 컨테이너 안에서 소스 코드 텍스트를 유지하고 하단에 경고 메시지를 표시합니다.
|
|
198
199
|
|
|
199
200
|
`blog.config.ts`에서 설정:
|
package/README.md
CHANGED
|
@@ -193,7 +193,8 @@ flowchart LR
|
|
|
193
193
|
|
|
194
194
|
Rendering happens in the browser on first load and when navigating to another document.
|
|
195
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
|
|
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.
|
|
197
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.
|
|
198
199
|
|
|
199
200
|
Configuration options (`blog.config.ts`):
|
package/package.json
CHANGED
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/runtime/app.css
CHANGED
|
@@ -66,6 +66,9 @@
|
|
|
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;
|
|
70
|
+
--mermaid-wide-max-width: 640px;
|
|
71
|
+
--mermaid-tall-max-height: 560px;
|
|
69
72
|
--desktop-sidebar-default: 420px;
|
|
70
73
|
--desktop-sidebar-min: 320px;
|
|
71
74
|
--desktop-viewer-min: 680px;
|
|
@@ -1101,6 +1104,9 @@ body.mobile-toggle-left .mobile-menu-toggle {
|
|
|
1101
1104
|
.viewer-content .mermaid-block {
|
|
1102
1105
|
margin: 1.5rem 0;
|
|
1103
1106
|
padding: 16px 18px;
|
|
1107
|
+
max-width: min(100%, var(--content-visual-max-width));
|
|
1108
|
+
margin-left: auto;
|
|
1109
|
+
margin-right: auto;
|
|
1104
1110
|
border-radius: 12px;
|
|
1105
1111
|
border: 1px solid var(--latte-surface0);
|
|
1106
1112
|
background: linear-gradient(180deg, var(--latte-base), var(--latte-mantle));
|
|
@@ -1135,12 +1141,20 @@ body.mobile-toggle-left .mobile-menu-toggle {
|
|
|
1135
1141
|
|
|
1136
1142
|
.viewer-content .mermaid-block pre.mermaid[data-mermaid-rendered="true"] svg {
|
|
1137
1143
|
display: block;
|
|
1138
|
-
width:
|
|
1139
|
-
max-width: 100
|
|
1144
|
+
width: auto;
|
|
1145
|
+
max-width: min(100%, var(--content-visual-max-width));
|
|
1140
1146
|
height: auto;
|
|
1141
1147
|
margin: 0 auto;
|
|
1142
1148
|
}
|
|
1143
1149
|
|
|
1150
|
+
.viewer-content .mermaid-block.is-wide pre.mermaid[data-mermaid-rendered="true"] svg {
|
|
1151
|
+
max-width: min(100%, var(--mermaid-wide-max-width));
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
.viewer-content .mermaid-block.is-tall pre.mermaid[data-mermaid-rendered="true"] svg {
|
|
1155
|
+
max-height: min(var(--mermaid-tall-max-height), 68vh);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1144
1158
|
.viewer-content .mermaid-block .mermaid-render-error {
|
|
1145
1159
|
margin: 12px 0 0;
|
|
1146
1160
|
padding: 10px 12px;
|
|
@@ -1200,8 +1214,10 @@ body.mobile-toggle-left .mobile-menu-toggle {
|
|
|
1200
1214
|
/* Images */
|
|
1201
1215
|
.viewer-content img {
|
|
1202
1216
|
display: block;
|
|
1203
|
-
|
|
1217
|
+
width: auto;
|
|
1218
|
+
max-width: min(100%, var(--content-visual-max-width));
|
|
1204
1219
|
height: auto;
|
|
1220
|
+
margin: 0 auto;
|
|
1205
1221
|
border-radius: 8px;
|
|
1206
1222
|
border: 1px solid var(--latte-surface0);
|
|
1207
1223
|
}
|
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";
|
|
@@ -21,6 +22,10 @@ const MERMAID_SELECTOR = "pre.mermaid";
|
|
|
21
22
|
const MERMAID_ERROR_CLASS = "mermaid-render-error";
|
|
22
23
|
const MERMAID_THEME_VALIDATION_RE = /^[a-zA-Z][a-zA-Z0-9._-]*$/;
|
|
23
24
|
const MERMAID_URL_VALIDATION_RE = /^(https?:\/\/|\/|\.{1,2}\/)[^\s"'<>]+$/;
|
|
25
|
+
const MERMAID_WIDE_RATIO = 2.4;
|
|
26
|
+
const MERMAID_TALL_RATIO = 0.85;
|
|
27
|
+
const MERMAID_BLOCK_WIDE_CLASS = "is-wide";
|
|
28
|
+
const MERMAID_BLOCK_TALL_CLASS = "is-tall";
|
|
24
29
|
const mermaidRuntime = {
|
|
25
30
|
initialized: false,
|
|
26
31
|
loadingPromise: null,
|
|
@@ -103,44 +108,52 @@ function showMermaidError(preview, message) {
|
|
|
103
108
|
preview.parentElement.appendChild(createMermaidLoadError(message));
|
|
104
109
|
}
|
|
105
110
|
|
|
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
111
|
function normalizeRenderedMermaidSvg(block) {
|
|
129
112
|
if (!(block instanceof HTMLElement)) {
|
|
130
113
|
return;
|
|
131
114
|
}
|
|
132
115
|
|
|
116
|
+
const container = block.parentElement instanceof HTMLElement ? block.parentElement : null;
|
|
133
117
|
const svg = block.querySelector("svg");
|
|
134
118
|
if (!(svg instanceof SVGElement)) {
|
|
135
119
|
return;
|
|
136
120
|
}
|
|
137
121
|
|
|
138
|
-
|
|
122
|
+
if (container) {
|
|
123
|
+
container.classList.remove(MERMAID_BLOCK_WIDE_CLASS, MERMAID_BLOCK_TALL_CLASS);
|
|
124
|
+
}
|
|
125
|
+
|
|
139
126
|
svg.style.display = "block";
|
|
140
|
-
svg.style.width = "
|
|
127
|
+
svg.style.width = "auto";
|
|
141
128
|
svg.style.height = "auto";
|
|
142
129
|
svg.style.margin = "0 auto";
|
|
143
|
-
svg.style.maxWidth =
|
|
130
|
+
svg.style.maxWidth = "min(100%, var(--content-visual-max-width, 720px))";
|
|
131
|
+
svg.style.removeProperty("max-height");
|
|
132
|
+
|
|
133
|
+
const viewBox = svg.viewBox?.baseVal;
|
|
134
|
+
const intrinsicWidth =
|
|
135
|
+
viewBox && Number.isFinite(viewBox.width) && viewBox.width > 0
|
|
136
|
+
? viewBox.width
|
|
137
|
+
: Number.parseFloat(svg.getAttribute("width") ?? "");
|
|
138
|
+
const intrinsicHeight =
|
|
139
|
+
viewBox && Number.isFinite(viewBox.height) && viewBox.height > 0
|
|
140
|
+
? viewBox.height
|
|
141
|
+
: Number.parseFloat(svg.getAttribute("height") ?? "");
|
|
142
|
+
|
|
143
|
+
if (!(intrinsicWidth > 0) || !(intrinsicHeight > 0)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const aspectRatio = intrinsicWidth / intrinsicHeight;
|
|
148
|
+
if (container && aspectRatio >= MERMAID_WIDE_RATIO) {
|
|
149
|
+
container.classList.add(MERMAID_BLOCK_WIDE_CLASS);
|
|
150
|
+
svg.style.maxWidth = "min(100%, var(--mermaid-wide-max-width, 640px))";
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (container && aspectRatio <= MERMAID_TALL_RATIO) {
|
|
154
|
+
container.classList.add(MERMAID_BLOCK_TALL_CLASS);
|
|
155
|
+
svg.style.maxHeight = "min(var(--mermaid-tall-max-height, 560px), 68vh)";
|
|
156
|
+
}
|
|
144
157
|
}
|
|
145
158
|
|
|
146
159
|
function parseMermaidNodes() {
|
|
@@ -156,6 +169,7 @@ function resetMermaidNodes(nodes) {
|
|
|
156
169
|
for (const node of nodes) {
|
|
157
170
|
node.removeAttribute("data-mermaid-rendered");
|
|
158
171
|
if (node.parentElement instanceof HTMLElement) {
|
|
172
|
+
node.parentElement.classList.remove(MERMAID_BLOCK_WIDE_CLASS, MERMAID_BLOCK_TALL_CLASS);
|
|
159
173
|
removeMermaidErrorMessage(node.parentElement);
|
|
160
174
|
}
|
|
161
175
|
}
|
|
@@ -1101,7 +1115,16 @@ function renderBacklinks(doc, pathBase) {
|
|
|
1101
1115
|
return html;
|
|
1102
1116
|
}
|
|
1103
1117
|
|
|
1118
|
+
function setAppReadyState(state) {
|
|
1119
|
+
if (!(document.documentElement instanceof HTMLElement)) {
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
document.documentElement.setAttribute(APP_READY_STATE_ATTR, state);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1104
1125
|
async function start() {
|
|
1126
|
+
setAppReadyState("booting");
|
|
1127
|
+
|
|
1105
1128
|
const treeRoot = document.getElementById("tree-root");
|
|
1106
1129
|
const appRoot = document.querySelector(".app-root");
|
|
1107
1130
|
const splitter = document.getElementById("app-splitter");
|
|
@@ -1922,6 +1945,7 @@ async function start() {
|
|
|
1922
1945
|
const initialRoute = currentRoute === "/" ? pickHomeRoute(view) : currentRoute;
|
|
1923
1946
|
handleLayoutChange();
|
|
1924
1947
|
await state.navigate(initialRoute, currentRoute === "/" && initialRoute !== "/");
|
|
1948
|
+
setAppReadyState("ready");
|
|
1925
1949
|
|
|
1926
1950
|
window.addEventListener("popstate", async () => {
|
|
1927
1951
|
await state.navigate(resolveRouteFromLocation(view.routeMap, pathBase), false);
|
|
@@ -1929,6 +1953,8 @@ async function start() {
|
|
|
1929
1953
|
}
|
|
1930
1954
|
|
|
1931
1955
|
start().catch((error) => {
|
|
1956
|
+
setAppReadyState("error");
|
|
1957
|
+
|
|
1932
1958
|
const contentEl = document.getElementById("viewer-content");
|
|
1933
1959
|
if (contentEl) {
|
|
1934
1960
|
const message = error instanceof Error ? error.message : String(error);
|