@limcpf/everything-is-a-markdown 0.4.3 → 0.5.1
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/build.ts +23 -2
- package/src/config.ts +6 -0
- package/src/runtime/app.css +6 -2
- package/src/runtime/app.js +21 -2
- package/src/template.ts +10 -3
- package/src/types.ts +2 -0
package/package.json
CHANGED
package/src/build.ts
CHANGED
|
@@ -804,6 +804,7 @@ function buildManifest(docs: DocRecord[], tree: TreeNode[], options: BuildOption
|
|
|
804
804
|
|
|
805
805
|
return {
|
|
806
806
|
generatedAt: new Date().toISOString(),
|
|
807
|
+
siteTitle: resolveSiteTitle(options),
|
|
807
808
|
defaultBranch: DEFAULT_BRANCH,
|
|
808
809
|
branches,
|
|
809
810
|
ui: {
|
|
@@ -816,6 +817,24 @@ function buildManifest(docs: DocRecord[], tree: TreeNode[], options: BuildOption
|
|
|
816
817
|
};
|
|
817
818
|
}
|
|
818
819
|
|
|
820
|
+
function resolveSiteTitle(options: BuildOptions): string {
|
|
821
|
+
const value = options.siteTitle ?? options.seo?.siteName ?? options.seo?.defaultTitle ?? DEFAULT_SITE_TITLE;
|
|
822
|
+
const trimmed = value.trim();
|
|
823
|
+
return trimmed.length > 0 ? trimmed : DEFAULT_SITE_TITLE;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function composeDocumentTitle(pageTitle: string, siteTitle: string): string {
|
|
827
|
+
const left = pageTitle.trim();
|
|
828
|
+
const right = siteTitle.trim();
|
|
829
|
+
if (!left) {
|
|
830
|
+
return right || DEFAULT_SITE_TITLE;
|
|
831
|
+
}
|
|
832
|
+
if (!right || left === right) {
|
|
833
|
+
return left;
|
|
834
|
+
}
|
|
835
|
+
return `${left} - ${right}`;
|
|
836
|
+
}
|
|
837
|
+
|
|
819
838
|
function pickSeoImageDefaults(
|
|
820
839
|
seo: BuildOptions["seo"],
|
|
821
840
|
): { social: string | null; og: string | null; twitter: string | null } {
|
|
@@ -948,11 +967,13 @@ async function writeRuntimeAssets(context: OutputWriteContext): Promise<RuntimeA
|
|
|
948
967
|
}
|
|
949
968
|
|
|
950
969
|
function buildShellMeta(route: string, doc: DocRecord | null, options: BuildOptions): AppShellMeta {
|
|
951
|
-
const defaultTitle = options.seo?.defaultTitle ?? DEFAULT_SITE_TITLE;
|
|
970
|
+
const defaultTitle = options.seo?.defaultTitle ?? options.siteTitle ?? DEFAULT_SITE_TITLE;
|
|
971
|
+
const siteTitle = resolveSiteTitle(options);
|
|
952
972
|
const defaultDescription = options.seo?.defaultDescription ?? DEFAULT_SITE_DESCRIPTION;
|
|
953
973
|
const description = typeof doc?.description === "string" && doc.description.trim().length > 0 ? doc.description.trim() : undefined;
|
|
954
974
|
const canonicalUrl = options.seo ? buildCanonicalUrl(route, options.seo) : undefined;
|
|
955
|
-
const
|
|
975
|
+
const baseTitle = doc?.title ?? defaultTitle;
|
|
976
|
+
const title = composeDocumentTitle(baseTitle, siteTitle);
|
|
956
977
|
const imageDefaults = pickSeoImageDefaults(options.seo);
|
|
957
978
|
const ogImage = imageDefaults.og ?? imageDefaults.social ?? undefined;
|
|
958
979
|
const twitterImage = imageDefaults.twitter ?? imageDefaults.social ?? undefined;
|
package/src/config.ts
CHANGED
|
@@ -170,6 +170,11 @@ export function resolveBuildOptions(
|
|
|
170
170
|
const cliExclude = cli.exclude ?? [];
|
|
171
171
|
const mergedExclude = Array.from(new Set([...DEFAULTS.exclude, ...cfgExclude, ...cliExclude]));
|
|
172
172
|
const seo = normalizeSeoConfig(userConfig.seo);
|
|
173
|
+
const siteTitleRaw = userConfig.seo?.siteName ?? userConfig.seo?.defaultTitle;
|
|
174
|
+
const siteTitle =
|
|
175
|
+
typeof siteTitleRaw === "string" && siteTitleRaw.trim().length > 0
|
|
176
|
+
? siteTitleRaw.trim()
|
|
177
|
+
: undefined;
|
|
173
178
|
const configPinnedMenu = normalizePinnedMenu(userConfig.pinnedMenu, "[config]");
|
|
174
179
|
const resolvedPinnedMenu = pinnedMenu ?? configPinnedMenu;
|
|
175
180
|
|
|
@@ -179,6 +184,7 @@ export function resolveBuildOptions(
|
|
|
179
184
|
exclude: mergedExclude,
|
|
180
185
|
newWithinDays: cli.newWithinDays ?? userConfig.ui?.newWithinDays ?? DEFAULTS.newWithinDays,
|
|
181
186
|
recentLimit: cli.recentLimit ?? userConfig.ui?.recentLimit ?? DEFAULTS.recentLimit,
|
|
187
|
+
siteTitle,
|
|
182
188
|
pinnedMenu: resolvedPinnedMenu,
|
|
183
189
|
wikilinks: userConfig.markdown?.wikilinks ?? DEFAULTS.wikilinks,
|
|
184
190
|
imagePolicy: userConfig.markdown?.images ?? DEFAULTS.imagePolicy,
|
package/src/runtime/app.css
CHANGED
|
@@ -62,6 +62,8 @@
|
|
|
62
62
|
--accent-strong: #6732ca;
|
|
63
63
|
--badge-new-bg: var(--latte-red);
|
|
64
64
|
--badge-new-fg: #ffffff;
|
|
65
|
+
--mobile-toggle-bg: var(--latte-mauve);
|
|
66
|
+
--mobile-toggle-fg: #ffffff;
|
|
65
67
|
--desktop-sidebar-default: 420px;
|
|
66
68
|
--desktop-sidebar-min: 320px;
|
|
67
69
|
--desktop-viewer-min: 680px;
|
|
@@ -107,6 +109,8 @@
|
|
|
107
109
|
--accent-strong: #dfcbff;
|
|
108
110
|
--badge-new-bg: var(--mocha-red);
|
|
109
111
|
--badge-new-fg: var(--mocha-crust);
|
|
112
|
+
--mobile-toggle-bg: var(--mocha-mauve);
|
|
113
|
+
--mobile-toggle-fg: var(--mocha-crust);
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
* {
|
|
@@ -743,8 +747,8 @@ a:hover {
|
|
|
743
747
|
gap: 6px;
|
|
744
748
|
border: 0;
|
|
745
749
|
border-radius: 999px;
|
|
746
|
-
background: var(--
|
|
747
|
-
color:
|
|
750
|
+
background: var(--mobile-toggle-bg);
|
|
751
|
+
color: var(--mobile-toggle-fg);
|
|
748
752
|
min-height: 48px;
|
|
749
753
|
padding: 0 16px;
|
|
750
754
|
font-size: 0.9rem;
|
package/src/runtime/app.js
CHANGED
|
@@ -11,6 +11,7 @@ const DESKTOP_VIEWER_MIN = 680;
|
|
|
11
11
|
const DESKTOP_SPLITTER_WIDTH = 10;
|
|
12
12
|
const DESKTOP_SPLITTER_STEP = 24;
|
|
13
13
|
const DEFAULT_BRANCH = "dev";
|
|
14
|
+
const DEFAULT_SITE_TITLE = "File-System Blog";
|
|
14
15
|
const BRANCH_KEY = "fsblog.branch";
|
|
15
16
|
const FOCUSABLE_SELECTOR =
|
|
16
17
|
'a[href], button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
@@ -130,6 +131,23 @@ function resolveRouteFromLocation(routeMap) {
|
|
|
130
131
|
return direct;
|
|
131
132
|
}
|
|
132
133
|
|
|
134
|
+
function resolveSiteTitle(manifest) {
|
|
135
|
+
const value = typeof manifest?.siteTitle === "string" ? manifest.siteTitle.trim() : "";
|
|
136
|
+
return value || DEFAULT_SITE_TITLE;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function composeDocumentTitle(pageTitle, siteTitle) {
|
|
140
|
+
const left = String(pageTitle ?? "").trim();
|
|
141
|
+
const right = String(siteTitle ?? "").trim();
|
|
142
|
+
if (!left) {
|
|
143
|
+
return right || DEFAULT_SITE_TITLE;
|
|
144
|
+
}
|
|
145
|
+
if (!right || left === right) {
|
|
146
|
+
return left;
|
|
147
|
+
}
|
|
148
|
+
return `${left} - ${right}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
133
151
|
function formatMetaDateTime(value) {
|
|
134
152
|
const parsed = new Date(value);
|
|
135
153
|
if (!Number.isFinite(parsed.getTime())) {
|
|
@@ -1127,6 +1145,7 @@ async function start() {
|
|
|
1127
1145
|
}
|
|
1128
1146
|
manifest = await manifestRes.json();
|
|
1129
1147
|
}
|
|
1148
|
+
const siteTitle = resolveSiteTitle(manifest);
|
|
1130
1149
|
const defaultBranch = normalizeBranch(manifest.defaultBranch) || DEFAULT_BRANCH;
|
|
1131
1150
|
const availableBranchSet = new Set([defaultBranch]);
|
|
1132
1151
|
for (const doc of manifest.docs) {
|
|
@@ -1296,7 +1315,7 @@ async function start() {
|
|
|
1296
1315
|
|
|
1297
1316
|
if (shouldUseInitialView) {
|
|
1298
1317
|
hasHydratedInitialView = true;
|
|
1299
|
-
document.title =
|
|
1318
|
+
document.title = composeDocumentTitle(initialViewData.title, siteTitle);
|
|
1300
1319
|
if (viewerEl instanceof HTMLElement) {
|
|
1301
1320
|
viewerEl.scrollTo(0, 0);
|
|
1302
1321
|
}
|
|
@@ -1320,7 +1339,7 @@ async function start() {
|
|
|
1320
1339
|
|
|
1321
1340
|
navEl.innerHTML = renderNav(view.docs, view.docIndexById, id);
|
|
1322
1341
|
|
|
1323
|
-
document.title =
|
|
1342
|
+
document.title = composeDocumentTitle(doc.title, siteTitle);
|
|
1324
1343
|
if (viewerEl instanceof HTMLElement) {
|
|
1325
1344
|
viewerEl.scrollTo(0, 0);
|
|
1326
1345
|
}
|
package/src/template.ts
CHANGED
|
@@ -183,6 +183,9 @@ export function renderAppShellHtml(
|
|
|
183
183
|
const initialManifestScript = renderInitialManifestScript(manifest);
|
|
184
184
|
const symbolFontStylesheet =
|
|
185
185
|
"https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap";
|
|
186
|
+
const appTitle = typeof manifest?.siteTitle === "string" && manifest.siteTitle.trim().length > 0
|
|
187
|
+
? manifest.siteTitle.trim()
|
|
188
|
+
: DEFAULT_TITLE;
|
|
186
189
|
const initialTitle = initialView ? escapeHtmlAttribute(initialView.title) : "문서를 선택하세요";
|
|
187
190
|
const initialBreadcrumb = initialView ? initialView.breadcrumbHtml : "";
|
|
188
191
|
const initialMeta = initialView ? initialView.metaHtml : "";
|
|
@@ -199,7 +202,9 @@ export function renderAppShellHtml(
|
|
|
199
202
|
${headMeta}
|
|
200
203
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
201
204
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
202
|
-
<link rel="
|
|
205
|
+
<link rel="preload" as="style" href="${escapeHtmlAttribute(symbolFontStylesheet)}" />
|
|
206
|
+
<link rel="stylesheet" href="${escapeHtmlAttribute(symbolFontStylesheet)}" media="print" onload="this.media='all'" />
|
|
207
|
+
<noscript><link rel="stylesheet" href="${escapeHtmlAttribute(symbolFontStylesheet)}" /></noscript>
|
|
203
208
|
<link rel="stylesheet" href="${escapeHtmlAttribute(assets.cssHref)}" />
|
|
204
209
|
</head>
|
|
205
210
|
<body>
|
|
@@ -211,7 +216,7 @@ ${headMeta}
|
|
|
211
216
|
<div class="sidebar-header">
|
|
212
217
|
<h1 class="sidebar-title">
|
|
213
218
|
<span class="material-symbols-outlined icon-terminal">terminal</span>
|
|
214
|
-
|
|
219
|
+
${escapeHtmlAttribute(appTitle)}
|
|
215
220
|
</h1>
|
|
216
221
|
<button id="sidebar-close" class="sidebar-close" type="button" aria-label="탐색기 닫기">
|
|
217
222
|
<span class="material-symbols-outlined">close</span>
|
|
@@ -330,7 +335,9 @@ export function render404Html(assets: AppShellAssets = DEFAULT_ASSETS): string {
|
|
|
330
335
|
<title>404 - File-System Blog</title>
|
|
331
336
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
332
337
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
333
|
-
<link rel="
|
|
338
|
+
<link rel="preload" as="style" href="${escapeHtmlAttribute(symbolFontStylesheet)}" />
|
|
339
|
+
<link rel="stylesheet" href="${escapeHtmlAttribute(symbolFontStylesheet)}" media="print" onload="this.media='all'" />
|
|
340
|
+
<noscript><link rel="stylesheet" href="${escapeHtmlAttribute(symbolFontStylesheet)}" /></noscript>
|
|
334
341
|
<link rel="stylesheet" href="${escapeHtmlAttribute(assets.cssHref)}" />
|
|
335
342
|
</head>
|
|
336
343
|
<body>
|
package/src/types.ts
CHANGED
|
@@ -65,6 +65,7 @@ export interface BuildOptions {
|
|
|
65
65
|
exclude: string[];
|
|
66
66
|
newWithinDays: number;
|
|
67
67
|
recentLimit: number;
|
|
68
|
+
siteTitle?: string;
|
|
68
69
|
pinnedMenu: PinnedMenuOption | null;
|
|
69
70
|
wikilinks: boolean;
|
|
70
71
|
imagePolicy: ImagePolicy;
|
|
@@ -123,6 +124,7 @@ export type TreeNode = FolderNode | FileNode;
|
|
|
123
124
|
|
|
124
125
|
export interface Manifest {
|
|
125
126
|
generatedAt: string;
|
|
127
|
+
siteTitle: string;
|
|
126
128
|
defaultBranch: string;
|
|
127
129
|
branches: string[];
|
|
128
130
|
ui: {
|