@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@limcpf/everything-is-a-markdown",
3
- "version": "0.4.3",
3
+ "version": "0.5.1",
4
4
  "license": "MIT",
5
5
  "private": false,
6
6
  "type": "module",
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 title = doc?.title ?? defaultTitle;
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,
@@ -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(--latte-mauve);
747
- color: #fff;
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;
@@ -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 = `${initialViewData.title} - File-System Blog`;
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 = `${doc.title} - File-System Blog`;
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="stylesheet" href="${escapeHtmlAttribute(symbolFontStylesheet)}" />
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
- ~/dev-blog
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="stylesheet" href="${escapeHtmlAttribute(symbolFontStylesheet)}" />
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: {