mdorigin 0.1.7 → 0.2.0

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.
@@ -1,11 +1,12 @@
1
1
  import path from 'node:path';
2
+ import { isIgnoredContentName } from './content-store.js';
2
3
  import { inferDirectoryContentType } from './content-type.js';
3
4
  import { getDirectoryIndexCandidates } from './directory-index.js';
4
5
  import { extractManagedIndexEntries, getDocumentSummary, getDocumentTitle as getParsedDocumentTitle, parseMarkdownDocument, stripManagedIndexBlock, stripManagedIndexLinks, } from './markdown.js';
5
6
  import { applyIndexTransforms, renderFooterOverride, renderHeaderOverride, renderPageWithPlugins, transformHtmlWithPlugins, } from './extensions.js';
6
7
  import { handleApiRoute } from './api.js';
7
8
  import { normalizeRequestPath, resolveRequest } from './router.js';
8
- import { escapeHtml, renderCatalogArticleItems, renderDocument, } from '../html/template.js';
9
+ import { escapeHtml, renderListingArticleItems, renderDocument, } from '../html/template.js';
9
10
  export async function handleSiteRequest(store, pathname, options) {
10
11
  const plugins = options.plugins ?? [];
11
12
  const searchEnabled = options.searchApi !== undefined;
@@ -21,7 +22,7 @@ export async function handleSiteRequest(store, pathname, options) {
21
22
  return renderSitemap(store, options);
22
23
  }
23
24
  const resolved = resolveRequest(pathname);
24
- const catalogFragmentRequest = getCatalogFragmentRequest(options.searchParams);
25
+ const listingFragmentRequest = getListingFragmentRequest(options.searchParams);
25
26
  const negotiatedMarkdown = shouldServeMarkdownForRequest(resolved, options.acceptHeader);
26
27
  if (resolved.kind === 'not-found' || !resolved.sourcePath) {
27
28
  const aliasRedirect = await tryRedirectAlias(store, pathname, options);
@@ -82,21 +83,16 @@ export async function handleSiteRequest(store, pathname, options) {
82
83
  : isRootHomeRequest(resolved.requestPath) && navigation.items.length > 0
83
84
  ? stripManagedIndexLinks(entry.text, new Set(navigation.items.map((item) => item.href)))
84
85
  : entry.text;
85
- const catalogEntries = options.siteConfig.template === 'catalog'
86
- ? await applyIndexTransforms(extractManagedIndexEntries(renderedBody), plugins, {
87
- mode: 'render',
88
- requestPath: resolved.requestPath,
89
- sourcePath: resolved.sourcePath,
90
- siteConfig: options.siteConfig,
91
- })
92
- : [];
93
- if (catalogFragmentRequest !== null &&
94
- options.siteConfig.template === 'catalog') {
95
- return renderCatalogPostsFragment(catalogEntries, catalogFragmentRequest);
96
- }
97
- const documentBody = options.siteConfig.template === 'catalog'
98
- ? stripManagedIndexBlock(renderedBody)
99
- : renderedBody;
86
+ const listingEntries = await applyIndexTransforms(extractManagedIndexEntries(renderedBody), plugins, {
87
+ mode: 'render',
88
+ requestPath: resolved.requestPath,
89
+ sourcePath: resolved.sourcePath,
90
+ siteConfig: options.siteConfig,
91
+ });
92
+ if (listingFragmentRequest !== null && listingEntries.length > 0) {
93
+ return renderListingPostsFragment(listingEntries, listingFragmentRequest);
94
+ }
95
+ const documentBody = listingEntries.length > 0 ? stripManagedIndexBlock(renderedBody) : renderedBody;
100
96
  const renderedParsed = documentBody === entry.text
101
97
  ? parsed
102
98
  : await parseMarkdownDocument(resolved.sourcePath, documentBody);
@@ -107,18 +103,19 @@ export async function handleSiteRequest(store, pathname, options) {
107
103
  renderedParsed,
108
104
  siteConfig: options.siteConfig,
109
105
  topNav: navigation.items,
110
- catalogEntries,
106
+ listingEntries,
111
107
  searchEnabled,
112
108
  plugins,
113
109
  varyOnAccept: shouldVaryOnAccept(resolved),
114
110
  });
115
111
  }
116
- function getCatalogFragmentRequest(searchParams) {
117
- if (searchParams?.get('catalog-format') !== 'posts') {
112
+ function getListingFragmentRequest(searchParams) {
113
+ const format = searchParams?.get('listing-format') ?? searchParams?.get('catalog-format');
114
+ if (format !== 'posts') {
118
115
  return null;
119
116
  }
120
- const offset = normalizeNonNegativeInteger(searchParams.get('catalog-offset'));
121
- const limit = normalizePositiveInteger(searchParams.get('catalog-limit'));
117
+ const offset = normalizeNonNegativeInteger(searchParams?.get('listing-offset') ?? searchParams?.get('catalog-offset') ?? null);
118
+ const limit = normalizePositiveInteger(searchParams?.get('listing-limit') ?? searchParams?.get('catalog-limit') ?? null);
122
119
  if (offset === null || limit === null) {
123
120
  return null;
124
121
  }
@@ -140,7 +137,7 @@ function normalizePositiveInteger(value) {
140
137
  }
141
138
  function buildPageRenderModel(options) {
142
139
  return {
143
- kind: options.siteConfig.template === 'catalog' ? 'catalog' : 'document',
140
+ kind: options.listingEntries.length > 0 ? 'listing' : 'page',
144
141
  requestPath: options.resolvedRequestPath,
145
142
  sourcePath: options.sourcePath,
146
143
  siteTitle: options.siteConfig.siteTitle,
@@ -158,8 +155,6 @@ function buildPageRenderModel(options) {
158
155
  date: options.siteConfig.showDate === false ? undefined : options.parsed.meta.date,
159
156
  showSummary: options.siteConfig.showSummary,
160
157
  showDate: options.siteConfig.showDate,
161
- theme: options.siteConfig.theme,
162
- template: options.siteConfig.template,
163
158
  topNav: options.topNav,
164
159
  footerNav: options.siteConfig.footerNav,
165
160
  footerText: options.siteConfig.footerText,
@@ -169,10 +164,10 @@ function buildPageRenderModel(options) {
169
164
  stylesheetContent: options.siteConfig.stylesheetContent,
170
165
  canonicalPath: getCanonicalHtmlPathForContentPath(options.sourcePath),
171
166
  alternateMarkdownPath: getMarkdownRequestPathForContentPath(options.sourcePath),
172
- catalogEntries: options.catalogEntries,
173
- catalogRequestPath: options.resolvedRequestPath,
174
- catalogInitialPostCount: options.siteConfig.catalogInitialPostCount,
175
- catalogLoadMoreStep: options.siteConfig.catalogLoadMoreStep,
167
+ listingEntries: options.listingEntries,
168
+ listingRequestPath: options.resolvedRequestPath,
169
+ listingInitialPostCount: options.siteConfig.listingInitialPostCount,
170
+ listingLoadMoreStep: options.siteConfig.listingLoadMoreStep,
176
171
  searchEnabled: options.searchEnabled,
177
172
  };
178
173
  }
@@ -184,7 +179,7 @@ async function renderStructuredPage(options) {
184
179
  parsed: options.parsed,
185
180
  siteConfig: options.siteConfig,
186
181
  topNav: options.topNav,
187
- catalogEntries: options.catalogEntries,
182
+ listingEntries: options.listingEntries,
188
183
  searchEnabled: options.searchEnabled,
189
184
  });
190
185
  const renderContext = {
@@ -198,7 +193,7 @@ async function renderStructuredPage(options) {
198
193
  };
199
194
  const headerHtml = await renderHeaderOverride(options.plugins, currentContext);
200
195
  const footerHtml = await renderFooterOverride(options.plugins, currentContext);
201
- return (renderDocument({
196
+ return renderDocument({
202
197
  siteTitle: currentPage.siteTitle,
203
198
  siteDescription: currentPage.siteDescription,
204
199
  siteUrl: currentPage.siteUrl,
@@ -211,8 +206,6 @@ async function renderStructuredPage(options) {
211
206
  date: currentPage.date,
212
207
  showSummary: currentPage.showSummary,
213
208
  showDate: currentPage.showDate,
214
- theme: currentPage.theme,
215
- template: currentPage.template,
216
209
  topNav: currentPage.topNav,
217
210
  footerNav: currentPage.footerNav,
218
211
  footerText: currentPage.footerText,
@@ -221,14 +214,14 @@ async function renderStructuredPage(options) {
221
214
  stylesheetContent: currentPage.stylesheetContent,
222
215
  canonicalPath: currentPage.canonicalPath,
223
216
  alternateMarkdownPath: currentPage.alternateMarkdownPath,
224
- catalogEntries: currentPage.catalogEntries,
225
- catalogRequestPath: currentPage.catalogRequestPath,
226
- catalogInitialPostCount: currentPage.catalogInitialPostCount,
227
- catalogLoadMoreStep: currentPage.catalogLoadMoreStep,
217
+ listingEntries: currentPage.listingEntries,
218
+ listingRequestPath: currentPage.listingRequestPath,
219
+ listingInitialPostCount: currentPage.listingInitialPostCount,
220
+ listingLoadMoreStep: currentPage.listingLoadMoreStep,
228
221
  searchEnabled: currentPage.searchEnabled,
229
222
  headerHtml,
230
223
  footerHtml,
231
- }));
224
+ });
232
225
  });
233
226
  const finalHtml = await transformHtmlWithPlugins(renderedPage.html, options.plugins, {
234
227
  page: renderedPage.page,
@@ -242,7 +235,7 @@ async function renderStructuredPage(options) {
242
235
  body: finalHtml,
243
236
  };
244
237
  }
245
- function renderCatalogPostsFragment(entries, request) {
238
+ function renderListingPostsFragment(entries, request) {
246
239
  const articles = entries.filter((entry) => entry.kind === 'article');
247
240
  const visibleArticles = articles.slice(request.offset, request.offset + request.limit);
248
241
  const nextOffset = request.offset + visibleArticles.length;
@@ -252,7 +245,7 @@ function renderCatalogPostsFragment(entries, request) {
252
245
  'content-type': 'application/json; charset=utf-8',
253
246
  },
254
247
  body: JSON.stringify({
255
- itemsHtml: renderCatalogArticleItems(visibleArticles),
248
+ itemsHtml: renderListingArticleItems(visibleArticles),
256
249
  hasMore: nextOffset < articles.length,
257
250
  nextOffset,
258
251
  }),
@@ -424,8 +417,6 @@ async function renderDirectoryListing(store, requestPath, siteConfig, searchEnab
424
417
  body,
425
418
  showSummary: false,
426
419
  showDate: false,
427
- theme: siteConfig.theme,
428
- template: siteConfig.template,
429
420
  topNav: navigation.items,
430
421
  footerNav: siteConfig.footerNav,
431
422
  footerText: siteConfig.footerText,
@@ -484,22 +475,17 @@ async function tryRenderAlternateDirectoryIndex(store, requestPath, options) {
484
475
  : isRootHomeRequest(requestPath) && navigation.items.length > 0
485
476
  ? stripManagedIndexLinks(entry.text, new Set(navigation.items.map((item) => item.href)))
486
477
  : entry.text;
487
- const catalogEntries = options.siteConfig.template === 'catalog'
488
- ? await applyIndexTransforms(extractManagedIndexEntries(renderedBody), plugins, {
489
- mode: 'render',
490
- requestPath,
491
- sourcePath: candidatePath,
492
- siteConfig: options.siteConfig,
493
- })
494
- : [];
495
- const catalogFragmentRequest = getCatalogFragmentRequest(options.searchParams);
496
- if (catalogFragmentRequest !== null &&
497
- options.siteConfig.template === 'catalog') {
498
- return renderCatalogPostsFragment(catalogEntries, catalogFragmentRequest);
499
- }
500
- const documentBody = options.siteConfig.template === 'catalog'
501
- ? stripManagedIndexBlock(renderedBody)
502
- : renderedBody;
478
+ const listingEntries = await applyIndexTransforms(extractManagedIndexEntries(renderedBody), plugins, {
479
+ mode: 'render',
480
+ requestPath,
481
+ sourcePath: candidatePath,
482
+ siteConfig: options.siteConfig,
483
+ });
484
+ const listingFragmentRequest = getListingFragmentRequest(options.searchParams);
485
+ if (listingFragmentRequest !== null && listingEntries.length > 0) {
486
+ return renderListingPostsFragment(listingEntries, listingFragmentRequest);
487
+ }
488
+ const documentBody = listingEntries.length > 0 ? stripManagedIndexBlock(renderedBody) : renderedBody;
503
489
  const renderedParsed = documentBody === entry.text
504
490
  ? parsed
505
491
  : await parseMarkdownDocument(candidatePath, documentBody);
@@ -510,7 +496,7 @@ async function tryRenderAlternateDirectoryIndex(store, requestPath, options) {
510
496
  renderedParsed,
511
497
  siteConfig: options.siteConfig,
512
498
  topNav: navigation.items,
513
- catalogEntries,
499
+ listingEntries,
514
500
  searchEnabled: options.searchApi !== undefined,
515
501
  plugins,
516
502
  });
@@ -750,7 +736,7 @@ async function inspectDirectoryShape(store, directoryPath) {
750
736
  let hasExtraMarkdownFiles = false;
751
737
  let hasAssetFiles = false;
752
738
  for (const entry of entries) {
753
- if (entry.name.startsWith('.')) {
739
+ if (isIgnoredContentName(entry.name)) {
754
740
  continue;
755
741
  }
756
742
  if (entry.kind === 'directory') {
@@ -1,7 +1,5 @@
1
1
  import type { ContentStore } from './content-store.js';
2
2
  import type { MdoPlugin } from './extensions.js';
3
- import type { TemplateName } from '../html/template-kind.js';
4
- import type { BuiltInThemeName } from '../html/theme.js';
5
3
  export interface SiteNavItem {
6
4
  label: string;
7
5
  href: string;
@@ -29,16 +27,14 @@ export interface SiteConfig {
29
27
  showDate?: boolean;
30
28
  showSummary?: boolean;
31
29
  stylesheet?: string;
32
- theme?: BuiltInThemeName;
33
- template?: TemplateName;
34
30
  topNav?: SiteNavItem[];
35
31
  footerNav?: SiteNavItem[];
36
32
  footerText?: string;
37
33
  socialLinks?: SiteSocialLink[];
38
34
  editLink?: EditLinkConfig;
39
35
  showHomeIndex?: boolean;
40
- catalogInitialPostCount?: number;
41
- catalogLoadMoreStep?: number;
36
+ listingInitialPostCount?: number;
37
+ listingLoadMoreStep?: number;
42
38
  }
43
39
  export interface UserSiteConfig extends SiteConfig {
44
40
  plugins?: MdoPlugin[];
@@ -52,16 +48,14 @@ export interface ResolvedSiteConfig {
52
48
  logo?: SiteLogo;
53
49
  showDate: boolean;
54
50
  showSummary: boolean;
55
- theme: BuiltInThemeName;
56
- template: TemplateName;
57
51
  topNav: SiteNavItem[];
58
52
  footerNav: SiteNavItem[];
59
53
  footerText?: string;
60
54
  socialLinks: SiteSocialLink[];
61
55
  editLink?: EditLinkConfig;
62
56
  showHomeIndex: boolean;
63
- catalogInitialPostCount: number;
64
- catalogLoadMoreStep: number;
57
+ listingInitialPostCount: number;
58
+ listingLoadMoreStep: number;
65
59
  stylesheetContent?: string;
66
60
  siteTitleConfigured: boolean;
67
61
  siteDescriptionConfigured: boolean;
@@ -14,6 +14,7 @@ export async function loadUserSiteConfig(options = {}) {
14
14
  ? path.resolve(cwd, options.configPath)
15
15
  : await resolveDefaultConfigPath(cwd, rootDir);
16
16
  const parsedConfig = await loadConfigSource(configFilePath);
17
+ const legacyConfig = parsedConfig;
17
18
  const stylesheetPath = parsedConfig.stylesheet
18
19
  ? path.resolve(path.dirname(configFilePath), parsedConfig.stylesheet)
19
20
  : null;
@@ -34,8 +35,6 @@ export async function loadUserSiteConfig(options = {}) {
34
35
  logo: normalizeLogo(parsedConfig.logo),
35
36
  showDate: parsedConfig.showDate ?? true,
36
37
  showSummary: parsedConfig.showSummary ?? true,
37
- theme: isBuiltInThemeName(parsedConfig.theme) ? parsedConfig.theme : 'paper',
38
- template: isTemplateName(parsedConfig.template) ? parsedConfig.template : 'document',
39
38
  topNav: normalizeTopNav(parsedConfig.topNav),
40
39
  footerNav: normalizeTopNav(parsedConfig.footerNav),
41
40
  footerText: typeof parsedConfig.footerText === 'string' && parsedConfig.footerText !== ''
@@ -46,13 +45,14 @@ export async function loadUserSiteConfig(options = {}) {
46
45
  showHomeIndex: typeof parsedConfig.showHomeIndex === 'boolean'
47
46
  ? parsedConfig.showHomeIndex
48
47
  : normalizeTopNav(parsedConfig.topNav).length === 0,
49
- catalogInitialPostCount: normalizePositiveInteger(parsedConfig.catalogInitialPostCount, 10),
50
- catalogLoadMoreStep: normalizePositiveInteger(parsedConfig.catalogLoadMoreStep, 10),
48
+ listingInitialPostCount: normalizePositiveInteger(parsedConfig.listingInitialPostCount ?? legacyConfig.catalogInitialPostCount, 10),
49
+ listingLoadMoreStep: normalizePositiveInteger(parsedConfig.listingLoadMoreStep ?? legacyConfig.catalogLoadMoreStep, 10),
51
50
  stylesheetContent,
52
51
  siteTitleConfigured: typeof parsedConfig.siteTitle === 'string' && parsedConfig.siteTitle !== '',
53
52
  siteDescriptionConfigured: typeof parsedConfig.siteDescription === 'string' &&
54
53
  parsedConfig.siteDescription !== '',
55
54
  };
55
+ warnOnLegacyConfig(legacyConfig, configFilePath);
56
56
  return {
57
57
  siteConfig,
58
58
  plugins: Array.isArray(parsedConfig.plugins) ? parsedConfig.plugins : [],
@@ -93,12 +93,6 @@ async function resolveDefaultConfigPath(cwd, rootDir) {
93
93
  }
94
94
  return (await findConfigPath(cwd)) ?? path.join(cwd, 'mdorigin.config.json');
95
95
  }
96
- function isBuiltInThemeName(value) {
97
- return value === 'paper' || value === 'atlas' || value === 'gazette';
98
- }
99
- function isTemplateName(value) {
100
- return value === 'document' || value === 'catalog';
101
- }
102
96
  function isNodeNotFound(error) {
103
97
  return (typeof error === 'object' &&
104
98
  error !== null &&
@@ -275,3 +269,17 @@ function normalizeSiteHref(value) {
275
269
  }
276
270
  return `/${value.replace(/^\.?\//, '')}`;
277
271
  }
272
+ function warnOnLegacyConfig(config, configFilePath) {
273
+ if ('theme' in config) {
274
+ console.warn(`[mdorigin] ${configFilePath}: "theme" is deprecated and ignored. mdorigin now uses a single built-in atlas presentation.`);
275
+ }
276
+ if ('template' in config) {
277
+ console.warn(`[mdorigin] ${configFilePath}: "template" is deprecated and ignored. Listing behavior is now part of the default presentation.`);
278
+ }
279
+ if ('catalogInitialPostCount' in config) {
280
+ console.warn(`[mdorigin] ${configFilePath}: "catalogInitialPostCount" is deprecated. Use "listingInitialPostCount" instead.`);
281
+ }
282
+ if ('catalogLoadMoreStep' in config) {
283
+ console.warn(`[mdorigin] ${configFilePath}: "catalogLoadMoreStep" is deprecated. Use "listingLoadMoreStep" instead.`);
284
+ }
285
+ }
@@ -1,7 +1,5 @@
1
1
  import type { SiteLogo, SiteNavItem, SiteSocialLink } from '../core/site-config.js';
2
2
  import type { ManagedIndexEntry } from '../core/markdown.js';
3
- import type { TemplateName } from './template-kind.js';
4
- import { type BuiltInThemeName } from './theme.js';
5
3
  export interface RenderDocumentOptions {
6
4
  siteTitle: string;
7
5
  siteDescription?: string;
@@ -15,8 +13,6 @@ export interface RenderDocumentOptions {
15
13
  date?: string;
16
14
  showSummary?: boolean;
17
15
  showDate?: boolean;
18
- theme: BuiltInThemeName;
19
- template: TemplateName;
20
16
  topNav?: SiteNavItem[];
21
17
  footerNav?: SiteNavItem[];
22
18
  footerText?: string;
@@ -25,14 +21,14 @@ export interface RenderDocumentOptions {
25
21
  stylesheetContent?: string;
26
22
  canonicalPath?: string;
27
23
  alternateMarkdownPath?: string;
28
- catalogEntries?: ManagedIndexEntry[];
29
- catalogRequestPath?: string;
30
- catalogInitialPostCount?: number;
31
- catalogLoadMoreStep?: number;
24
+ listingEntries?: ManagedIndexEntry[];
25
+ listingRequestPath?: string;
26
+ listingInitialPostCount?: number;
27
+ listingLoadMoreStep?: number;
32
28
  searchEnabled?: boolean;
33
29
  headerHtml?: string;
34
30
  footerHtml?: string;
35
31
  }
36
32
  export declare function renderDocument(options: RenderDocumentOptions): string;
37
33
  export declare function escapeHtml(value: string): string;
38
- export declare function renderCatalogArticleItems(entries: readonly ManagedIndexEntry[]): string;
34
+ export declare function renderListingArticleItems(entries: readonly ManagedIndexEntry[]): string;
@@ -1,4 +1,4 @@
1
- import { getBuiltInThemeStyles } from './theme.js';
1
+ import { getDefaultThemeStyles } from './theme.js';
2
2
  export function renderDocument(options) {
3
3
  const title = escapeHtml(options.title);
4
4
  const siteTitle = escapeHtml(options.siteTitle);
@@ -25,7 +25,7 @@ export function renderDocument(options) {
25
25
  const alternateMarkdownMeta = options.alternateMarkdownPath
26
26
  ? `<link rel="alternate" type="text/markdown" href="${escapeHtml(options.alternateMarkdownPath)}">`
27
27
  : '';
28
- const stylesheetBlock = `<style>${getBuiltInThemeStyles(options.theme)}${options.stylesheetContent ? `\n${options.stylesheetContent}` : ''}</style>`;
28
+ const stylesheetBlock = `<style>${getDefaultThemeStyles()}${options.stylesheetContent ? `\n${options.stylesheetContent}` : ''}</style>`;
29
29
  const navBlock = options.topNav && options.topNav.length > 0
30
30
  ? `<nav class="site-nav"><ul>${options.topNav
31
31
  .map((item) => `<li><a href="${escapeHtml(item.href)}">${escapeHtml(item.label)}</a></li>`)
@@ -69,20 +69,26 @@ export function renderDocument(options) {
69
69
  const editLinkBlock = options.editLinkHref
70
70
  ? `<a class="site-footer__edit-link" href="${escapeHtml(options.editLinkHref)}">Edit this page</a>`
71
71
  : '';
72
+ const markdownViewBlock = options.alternateMarkdownPath
73
+ ? `<a class="site-footer__markdown-link" href="${escapeHtml(options.alternateMarkdownPath)}" aria-label="View Markdown source">MD View</a>`
74
+ : '';
75
+ const footerActionsBlock = markdownViewBlock || editLinkBlock
76
+ ? `<div class="site-footer__actions">${markdownViewBlock}${editLinkBlock}</div>`
77
+ : '';
72
78
  const footerTextBlock = options.footerText
73
79
  ? `<p class="site-footer__text">${escapeHtml(options.footerText)}</p>`
74
80
  : '';
75
- const footerMetaBlock = socialLinksBlock || editLinkBlock
76
- ? `<div class="site-footer__meta">${socialLinksBlock}${editLinkBlock}</div>`
81
+ const footerMetaBlock = socialLinksBlock || footerActionsBlock
82
+ ? `<div class="site-footer__meta">${socialLinksBlock}${footerActionsBlock}</div>`
77
83
  : '';
78
84
  const footerBlock = footerNavBlock || footerTextBlock || footerMetaBlock
79
85
  ? `<footer class="site-footer"><div class="site-footer__inner">${footerNavBlock}${footerTextBlock}${footerMetaBlock}</div></footer>`
80
86
  : '';
81
- const articleBody = options.template === 'catalog'
82
- ? renderCatalogArticle(options.body, options.catalogEntries ?? [], {
83
- requestPath: options.catalogRequestPath ?? '/',
84
- initialPostCount: options.catalogInitialPostCount ?? 10,
85
- loadMoreStep: options.catalogLoadMoreStep ?? 10,
87
+ const articleBody = options.listingEntries && options.listingEntries.length > 0
88
+ ? renderListingArticle(options.body, options.listingEntries, {
89
+ requestPath: options.listingRequestPath ?? '/',
90
+ initialPostCount: options.listingInitialPostCount ?? 10,
91
+ loadMoreStep: options.listingLoadMoreStep ?? 10,
86
92
  })
87
93
  : options.body;
88
94
  const searchScript = options.searchEnabled ? renderSearchScript() : '';
@@ -103,7 +109,7 @@ export function renderDocument(options) {
103
109
  alternateMarkdownMeta,
104
110
  stylesheetBlock,
105
111
  '</head>',
106
- `<body data-theme="${options.theme}" data-template="${options.template}">`,
112
+ '<body>',
107
113
  headerBlock,
108
114
  '<main>',
109
115
  `<article>${articleBody}</article>`,
@@ -153,7 +159,7 @@ function renderSocialIcon(icon) {
153
159
  function iconSvg(pathData) {
154
160
  return `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="${pathData}"></path></svg>`;
155
161
  }
156
- function renderCatalogArticle(body, entries, options) {
162
+ function renderListingArticle(body, entries, options) {
157
163
  if (entries.length === 0) {
158
164
  return body;
159
165
  }
@@ -164,10 +170,10 @@ function renderCatalogArticle(body, entries, options) {
164
170
  const shouldLoadMore = articles.length > visibleArticles.length;
165
171
  return [
166
172
  `<div class="catalog-page__body">${body}</div>`,
167
- '<section class="catalog-page" aria-label="Catalog">',
168
- directories.length > 0 ? renderCatalogDirectories(directories) : '',
173
+ '<section class="catalog-page" aria-label="Content listing">',
174
+ directories.length > 0 ? renderListingDirectories(directories) : '',
169
175
  articles.length > 0
170
- ? renderCatalogArticles(visibleArticles, {
176
+ ? renderListingArticles(visibleArticles, {
171
177
  requestPath: options.requestPath,
172
178
  nextOffset: visibleArticles.length,
173
179
  loadMoreStep: options.loadMoreStep,
@@ -175,10 +181,10 @@ function renderCatalogArticle(body, entries, options) {
175
181
  })
176
182
  : '',
177
183
  '</section>',
178
- shouldLoadMore ? renderCatalogLoadMoreScript() : '',
184
+ shouldLoadMore ? renderListingLoadMoreScript() : '',
179
185
  ].join('');
180
186
  }
181
- function renderCatalogDirectories(entries) {
187
+ function renderListingDirectories(entries) {
182
188
  return [
183
189
  '<div class="catalog-list catalog-list--directories">',
184
190
  ...entries.map((entry) => `<a class="catalog-item catalog-item--directory" href="${escapeHtml(entry.href)}"><strong class="catalog-item__title">${escapeHtml(entry.title)}</strong>${entry.detail
@@ -187,28 +193,28 @@ function renderCatalogDirectories(entries) {
187
193
  '</div>',
188
194
  ].join('');
189
195
  }
190
- export function renderCatalogArticleItems(entries) {
196
+ export function renderListingArticleItems(entries) {
191
197
  return entries
192
198
  .map((entry) => `<a class="catalog-item" href="${escapeHtml(entry.href)}"><strong class="catalog-item__title">${escapeHtml(entry.title)}</strong>${entry.detail
193
199
  ? `<span class="catalog-item__detail">${escapeHtml(entry.detail)}</span>`
194
200
  : ''}</a>`)
195
201
  .join('');
196
202
  }
197
- function renderCatalogArticles(entries, options) {
203
+ function renderListingArticles(entries, options) {
198
204
  return [
199
- '<div class="catalog-list" data-catalog-articles>',
200
- renderCatalogArticleItems(entries),
205
+ '<div class="catalog-list" data-listing-articles>',
206
+ renderListingArticleItems(entries),
201
207
  '</div>',
202
208
  options.hasMore
203
- ? `<div class="catalog-load-more"><button type="button" class="catalog-load-more__button" data-catalog-load-more data-request-path="${escapeHtml(options.requestPath)}" data-next-offset="${escapeHtml(String(options.nextOffset))}" data-load-more-step="${escapeHtml(String(options.loadMoreStep))}">Load more</button></div>`
209
+ ? `<div class="catalog-load-more"><button type="button" class="catalog-load-more__button" data-listing-load-more data-request-path="${escapeHtml(options.requestPath)}" data-next-offset="${escapeHtml(String(options.nextOffset))}" data-load-more-step="${escapeHtml(String(options.loadMoreStep))}">Load more</button></div>`
204
210
  : '',
205
211
  ].join('');
206
212
  }
207
- function renderCatalogLoadMoreScript() {
213
+ function renderListingLoadMoreScript() {
208
214
  return `<script>
209
215
  (() => {
210
- const button = document.querySelector('[data-catalog-load-more]');
211
- const list = document.querySelector('[data-catalog-articles]');
216
+ const button = document.querySelector('[data-listing-load-more]');
217
+ const list = document.querySelector('[data-listing-articles]');
212
218
  if (!(button instanceof HTMLButtonElement) || !(list instanceof HTMLElement)) {
213
219
  return;
214
220
  }
@@ -227,9 +233,9 @@ function renderCatalogLoadMoreScript() {
227
233
 
228
234
  try {
229
235
  const url = new URL(requestPath, window.location.origin);
230
- url.searchParams.set('catalog-format', 'posts');
231
- url.searchParams.set('catalog-offset', nextOffset);
232
- url.searchParams.set('catalog-limit', loadMoreStep);
236
+ url.searchParams.set('listing-format', 'posts');
237
+ url.searchParams.set('listing-offset', nextOffset);
238
+ url.searchParams.set('listing-limit', loadMoreStep);
233
239
 
234
240
  const response = await fetch(url.toString(), {
235
241
  headers: { Accept: 'application/json' },
@@ -1,2 +1 @@
1
- export type BuiltInThemeName = 'paper' | 'atlas' | 'gazette';
2
- export declare function getBuiltInThemeStyles(theme: BuiltInThemeName): string;
1
+ export declare function getDefaultThemeStyles(): string;