@pagenary/publisher 2026.5.4 → 2026.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagenary/publisher",
3
- "version": "2026.5.4",
3
+ "version": "2026.6.0",
4
4
  "type": "module",
5
5
  "description": "Multi-tenant static publishing component for Pagenary platform.",
6
6
  "license": "AGPL-3.0-or-later",
@@ -1672,6 +1672,35 @@ async function ensureJavascriptModule(sourcePath, targetPath) {
1672
1672
  await fsp.copyFile(sourcePath, targetPath);
1673
1673
  }
1674
1674
 
1675
+ async function readMarkdownMetadata(sourcePath) {
1676
+ const raw = await fsp.readFile(sourcePath, 'utf8');
1677
+ const { data, body } = parseFrontmatter(raw);
1678
+ return {
1679
+ title: data.title || firstHeadingFromMarkdown(body) || null,
1680
+ summary: data.summary || data.description || '',
1681
+ date: data.date || null,
1682
+ reading_time: estimateReadingTime(body)
1683
+ };
1684
+ }
1685
+
1686
+ function firstHeadingFromMarkdown(body) {
1687
+ const match = body.match(/^#\s+(.+)$/m);
1688
+ return match ? match[1].trim() : null;
1689
+ }
1690
+
1691
+ function estimateReadingTime(body) {
1692
+ const words = String(body || '').trim().split(/\s+/).filter(Boolean);
1693
+ return Math.max(1, Math.ceil(words.length / 200));
1694
+ }
1695
+
1696
+ async function readContentMetadata(sourcePath) {
1697
+ const ext = path.extname(sourcePath).toLowerCase();
1698
+ if (ext === '.md' || ext === '.markdown') {
1699
+ return readMarkdownMetadata(sourcePath);
1700
+ }
1701
+ return {};
1702
+ }
1703
+
1675
1704
  // ============================================================================
1676
1705
  // Internal Link Transformation (ADR-011)
1677
1706
  // ============================================================================
@@ -1992,6 +2021,46 @@ function deriveSectionId(parentId, name, isIndex = false) {
1992
2021
  return stem;
1993
2022
  }
1994
2023
 
2024
+ function routePath(value) {
2025
+ return String(value || '').replace(/^\/+|\/+$/g, '');
2026
+ }
2027
+
2028
+ function collectionForRelPath(relPath, collections = []) {
2029
+ const normalized = relPath.split(path.sep).join('/');
2030
+ return collections.find((collection) => {
2031
+ const collectionPath = routePath(collection.path);
2032
+ return collectionPath && (normalized === collectionPath || normalized.startsWith(`${collectionPath}/`));
2033
+ }) || null;
2034
+ }
2035
+
2036
+ function decorateCollectionEntry(entry, metadata, collection) {
2037
+ if (!collection || !entry) return entry;
2038
+ entry.collection = routePath(collection.path);
2039
+ entry.showDate = collection.showDate === true;
2040
+ entry.showSummary = collection.showSummary === true;
2041
+ entry.showReadingTime = collection.showReadingTime !== false;
2042
+ if (metadata.date) entry.date = metadata.date;
2043
+ if (metadata.reading_time) entry.reading_time = metadata.reading_time;
2044
+ return entry;
2045
+ }
2046
+
2047
+ function sortCollectionEntries(entries, collection) {
2048
+ if (!collection || !Array.isArray(entries)) return entries;
2049
+ const sortBy = collection.sortBy || 'date';
2050
+ const dir = (collection.order || 'desc').toLowerCase() === 'asc' ? 1 : -1;
2051
+ entries.sort((a, b) => {
2052
+ const av = a?.[sortBy];
2053
+ const bv = b?.[sortBy];
2054
+ if (av == null && bv == null) return 0;
2055
+ if (av == null) return 1;
2056
+ if (bv == null) return -1;
2057
+ if (av < bv) return -1 * dir;
2058
+ if (av > bv) return 1 * dir;
2059
+ return 0;
2060
+ });
2061
+ return entries;
2062
+ }
2063
+
1995
2064
  /**
1996
2065
  * Encode section ID for use in output filename
1997
2066
  * Replaces / with -- to create flat output structure
@@ -2107,19 +2176,21 @@ async function scanContentDirectory(dirPath, parentId, context, depth = 0) {
2107
2176
  const indexFile = files.find(f => /^index\.(md|markdown|html|htm|js|mjs)$/i.test(f.name));
2108
2177
  if (indexFile) {
2109
2178
  const sectionId = parentId || path.basename(dirPath);
2110
- const title = manifest?.title || humanizeTitle(path.basename(dirPath));
2111
- const summary = manifest?.summary || '';
2112
-
2113
- // Calculate relative path from content root
2114
2179
  const relPath = path.relative(context.contentRoot, path.join(dirPath, indexFile.name));
2180
+ const metadata = await readContentMetadata(path.join(dirPath, indexFile.name));
2181
+ const collection = collectionForRelPath(relPath, context.collections);
2182
+ const title = manifest?.title || metadata.title || humanizeTitle(path.basename(dirPath));
2183
+ const summary = manifest?.summary || metadata.summary || '';
2115
2184
 
2116
- sections.push({
2185
+ const sectionEntry = {
2117
2186
  id: sectionId,
2118
2187
  title,
2119
2188
  summary,
2120
2189
  file: relPath,
2121
2190
  _isIndex: true
2122
- });
2191
+ };
2192
+ decorateCollectionEntry(sectionEntry, metadata, collection);
2193
+ sections.push(sectionEntry);
2123
2194
  }
2124
2195
 
2125
2196
  // Process other content files
@@ -2131,10 +2202,12 @@ async function scanContentDirectory(dirPath, parentId, context, depth = 0) {
2131
2202
  s.id === sectionId || s.file === file.name || s.id === file.name.replace(/\.[^.]+$/, '')
2132
2203
  );
2133
2204
 
2134
- const title = manifestEntry?.title || humanizeTitle(file.name);
2135
- const summary = manifestEntry?.summary || '';
2136
- const type = manifestEntry?.type || null;
2137
2205
  const relPath = path.relative(context.contentRoot, path.join(dirPath, file.name));
2206
+ const metadata = await readContentMetadata(path.join(dirPath, file.name));
2207
+ const collection = collectionForRelPath(relPath, context.collections);
2208
+ const title = manifestEntry?.title || metadata.title || humanizeTitle(file.name);
2209
+ const summary = manifestEntry?.summary || metadata.summary || '';
2210
+ const type = manifestEntry?.type || null;
2138
2211
 
2139
2212
  const sectionEntry = {
2140
2213
  id: sectionId,
@@ -2142,6 +2215,7 @@ async function scanContentDirectory(dirPath, parentId, context, depth = 0) {
2142
2215
  summary,
2143
2216
  file: relPath
2144
2217
  };
2218
+ decorateCollectionEntry(sectionEntry, metadata, collection);
2145
2219
  if (type) sectionEntry.type = type;
2146
2220
  sections.push(sectionEntry);
2147
2221
  }
@@ -2170,6 +2244,7 @@ async function scanContentDirectory(dirPath, parentId, context, depth = 0) {
2170
2244
  if (indexEntry) {
2171
2245
  // Remove index from subsections, it becomes the group itself
2172
2246
  const otherSections = subsections.filter(s => !s._isIndex);
2247
+ sortCollectionEntries(otherSections, collectionForRelPath(path.relative(context.contentRoot, subdirPath), context.collections));
2173
2248
  const entry = {
2174
2249
  id: subdirId,
2175
2250
  title,
@@ -2185,7 +2260,7 @@ async function scanContentDirectory(dirPath, parentId, context, depth = 0) {
2185
2260
  id: subdirId,
2186
2261
  title,
2187
2262
  summary,
2188
- subsections
2263
+ subsections: sortCollectionEntries(subsections, collectionForRelPath(path.relative(context.contentRoot, subdirPath), context.collections))
2189
2264
  };
2190
2265
  if (collapsed) entry.collapsed = true;
2191
2266
  sections.push(entry);
@@ -2441,7 +2516,7 @@ async function materializeScannedSections(sections, context) {
2441
2516
  const processed = [];
2442
2517
 
2443
2518
  for (const section of sections) {
2444
- const { id, title, summary, file, subsections, url, type, collapsed } = section;
2519
+ const { id, title, summary, file, subsections, url, type, collapsed, _isIndex, ...metadata } = section;
2445
2520
 
2446
2521
  // Pass through external links without processing
2447
2522
  if (url) {
@@ -2499,21 +2574,22 @@ async function materializeScannedSections(sections, context) {
2499
2574
  id,
2500
2575
  title,
2501
2576
  summary,
2502
- module: `./sections/${outFile}`,
2577
+ ...metadata,
2578
+ module: `/sections/${outFile}`,
2503
2579
  subsections: processedSubsections
2504
2580
  };
2505
2581
  if (type) entry.type = type;
2506
2582
  if (collapsed) entry.collapsed = true;
2507
2583
  processed.push(entry);
2508
2584
  } else {
2509
- const entry = { id, title, summary, module: `./sections/${outFile}` };
2585
+ const entry = { id, title, summary, ...metadata, module: `/sections/${outFile}` };
2510
2586
  if (type) entry.type = type;
2511
2587
  if (collapsed) entry.collapsed = true;
2512
2588
  processed.push(entry);
2513
2589
  }
2514
2590
  } else if (processedSubsections && processedSubsections.length > 0) {
2515
2591
  // Group without its own content
2516
- const entry = { id, title, summary, subsections: processedSubsections };
2592
+ const entry = { id, title, summary, ...metadata, subsections: processedSubsections };
2517
2593
  if (type) entry.type = type;
2518
2594
  if (collapsed) entry.collapsed = true;
2519
2595
  processed.push(entry);
@@ -2575,6 +2651,9 @@ function applyManifestHierarchy(scannedSections, rootManifest) {
2575
2651
  title: mEntry.title || scanned?.title || mEntry.id,
2576
2652
  summary: mEntry.summary || scanned?.summary || ''
2577
2653
  };
2654
+ for (const key of ['collection', 'showDate', 'showSummary', 'showReadingTime', 'date', 'reading_time']) {
2655
+ if (scanned?.[key] !== undefined) node[key] = scanned[key];
2656
+ }
2578
2657
 
2579
2658
  if (mEntry.collapsed) node.collapsed = true;
2580
2659
  if (mEntry.type) node.type = mEntry.type;
@@ -2671,6 +2750,7 @@ async function processNestedContent(sourceDir, distDir, tenantId, contentRoot, o
2671
2750
  keepFiles,
2672
2751
  leafOrder: [],
2673
2752
  siteConfig,
2753
+ collections: Array.isArray(config.collections) ? config.collections : [],
2674
2754
  // Link transformation context (populated after scan)
2675
2755
  sectionIndex: null,
2676
2756
  linkWarnings,
@@ -2779,7 +2859,7 @@ async function materializeSectionModule(entry, context) {
2779
2859
  }
2780
2860
 
2781
2861
  const ext = path.extname(sourcePath).toLowerCase();
2782
- const outFile = `${id}.js`;
2862
+ const outFile = `${encodePathForFilename(id)}.js`;
2783
2863
  const targetPath = path.join(context.sectionsDir, outFile);
2784
2864
  context.keepFiles.add(outFile);
2785
2865
 
@@ -2807,7 +2887,7 @@ async function materializeSectionModule(entry, context) {
2807
2887
  return null;
2808
2888
  }
2809
2889
 
2810
- return `./sections/${outFile}`;
2890
+ return `/sections/${outFile}`;
2811
2891
  }
2812
2892
 
2813
2893
  function buildManifestModuleSource(manifestEntries, defaultSection, siteConfig = {}, exportConfig = {}) {
@@ -3339,7 +3419,7 @@ async function processIncrementalManifest(sourceDir, distDir, tenantId, changedF
3339
3419
  const ext = path.extname(sourcePath).toLowerCase();
3340
3420
  // Use manifest section ID if available, otherwise fall back to filename
3341
3421
  const sectionId = fileToSectionId.get(relPath) || path.basename(relPath, ext);
3342
- const targetPath = path.join(sectionsDir, `${sectionId}.js`);
3422
+ const targetPath = path.join(sectionsDir, `${encodePathForFilename(sectionId)}.js`);
3343
3423
 
3344
3424
  try {
3345
3425
  if (ext === '.md' || ext === '.markdown') {
@@ -3349,7 +3429,8 @@ async function processIncrementalManifest(sourceDir, distDir, tenantId, changedF
3349
3429
  contentRoot: contentRoot.basePath || contentDir,
3350
3430
  sectionIndex,
3351
3431
  linkWarnings,
3352
- strictLinks: options.strictLinks !== false
3432
+ strictLinks: options.strictLinks !== false,
3433
+ collections: Array.isArray(config.collections) ? config.collections : []
3353
3434
  };
3354
3435
  await ensureMarkdownModule(sourcePath, targetPath, linkContext);
3355
3436
  console.log(` ↳ updated: ${sectionId} (markdown)`);
package/site/app.js CHANGED
@@ -1 +1 @@
1
- import{MANIFEST as e,DEFAULT_SECTION as t,findSection as n,getAdjacentSections as a,SITE_CONFIG as s,EXPORT_CONFIG as o}from"./manifest.js";import{updateMetaTags as i}from"./seo.js";import{escapeRegExp as l,searchContent as c,flattenManifest as r,findPreferredIndex as d}from"./lib/search.js";import{resolveTarget as m,resolveEntry as p}from"./lib/router.js";import{composeExportDocument as u,collectExportableSections as v}from"./lib/export.js";import{renderMermaidBlocks as h}from"./mermaid-init.js";import{highlightCodeBlocks as f}from"./syntax-highlight.js";const y=document.getElementById("app"),g=document.getElementById("nav"),b=document.getElementById("year"),E=document.getElementById("exportBtn"),L=document.getElementById("commandToggle"),T=document.getElementById("commandPalette"),x=document.getElementById("commandInput"),N=document.getElementById("commandList"),w=document.getElementById("mobileMenuToggle"),C=document.querySelector(".sidebar"),k="docs-toolkit-command-query",$=new Map,H=new Map,I=new Map,M=new Set;let A=[],S=0,P=!1,q=(localStorage.getItem(k)||"").trim(),B=!1;function R(e,t){const n=document.createElement("a");return n.href=e.url,n.target="_blank",n.rel="noopener noreferrer",n.className=`${t} nav-external`,n.title=e.summary||e.title,n.innerHTML=`\n <span class="nav-title">${e.title}<span class="nav-external-icon" aria-label="(opens in new tab)">↗</span></span>\n ${e.summary?`<span class="nav-summary">${e.summary}</span>`:""}\n `,n}function F(e,t={}){const{scrollToHighlight:a=!1}=t,{targetId:s,parentId:o}=function(e){return m(e,n)}(e);P&&_(),o&&j(o,!0),B=a||Boolean(q),location.hash.replace("#","")===s?O():location.hash=`#${s}`}function D(){return location.hash.replace("#","")||t}async function O(){const e=D(),t=p(e,n);if(!t)return;const{entry:o,targetId:l,parentId:c}=t;l===e?(c&&j(c,!0),function(e,t=null){H.forEach(e=>{e.setAttribute("aria-current","false")});const n=H.get(e);if(n&&n.setAttribute("aria-current","page"),t){const e=H.get(t);e&&e.setAttribute("aria-current","page")}}(o.id,c),await async function(e){if(!e)return;const t=await import(e.module),n=t.load||t.default;if("function"!=typeof n)return void(y.innerHTML='<article class="section"><p>Section failed to load.</p></article>');const o=await n();y.innerHTML=o.html||"",await h(y),await f(y),function(e){const t=y.querySelector(".bottom-nav");if(t&&t.remove(),"never"===s.bottomNav)return;const n=(s.bottomNavSections||[]).some(t=>e.startsWith(t)),o="mobile"===s.bottomNav&&!n,{prev:i,next:l}=a(e);if(!i&&!l)return;const c=document.createElement("nav");if(c.className="bottom-nav",o&&c.classList.add("mobile-only"),i){const e=document.createElement("div");e.className="bottom-nav-item bottom-nav-prev",e.innerHTML='<span class="bottom-nav-chevron">‹</span>';const t=document.createElement("a");t.href=`#${i.id}`,t.className="bottom-nav-link",t.title=`Previous: ${i.title}`,t.textContent=i.title,t.addEventListener("click",e=>{e.preventDefault(),F(i.id)}),e.appendChild(t),c.appendChild(e)}else{const e=document.createElement("div");e.className="bottom-nav-spacer",c.appendChild(e)}if(l){const e=document.createElement("div");e.className="bottom-nav-item bottom-nav-next";const t=document.createElement("a");t.href=`#${l.id}`,t.className="bottom-nav-link",t.title=`Next: ${l.title}`,t.textContent=l.title,t.addEventListener("click",e=>{e.preventDefault(),F(l.id)}),e.appendChild(t),e.innerHTML+='<span class="bottom-nav-chevron">›</span>',c.appendChild(e)}(y.querySelector("section")||y).appendChild(c)}(e.id),y.scrollTop=0,window.scrollTo(0,0),"function"==typeof o.afterRender&&o.afterRender(y),i({title:e.title,description:e.summary,siteTitle:s.siteTitle,siteUrl:s.siteUrl,sectionId:e.id,ogImage:e.ogImage||s.ogImage}),$.set(e.id,Date.now());const l=B;B=!1,K(l),requestAnimationFrame(()=>y.focus())}(o)):location.replace(`#${l}`)}function j(e,t){if(!e)return;const n=I.get(e);t?(M.add(e),n&&n.group.classList.add("expanded")):(M.delete(e),n&&n.group.classList.remove("expanded"))}function W(){if(!T||!x)return;P=!0,T.hidden=!1;const e=q;x.value=e,z(e),requestAnimationFrame(()=>{x.focus(),e&&x.select()})}function _(){T&&x&&(P=!1,T.hidden=!0,x.blur())}!function(){g.innerHTML="",H.clear(),I.clear();let t=M.size>0;e.forEach((e,n)=>{if(e.url){const t=R(e,"nav-leaf");return void g.appendChild(t)}if(e.subsections&&e.subsections.length){const n=document.createElement("div");n.className="nav-group";const a=Boolean(e.module),s=document.createElement("button");s.type="button",s.className="nav-parent"+(a?" nav-parent-with-content":""),s.dataset.section=e.id,s.title=e.summary,a?(s.innerHTML=`\n <span class="nav-title-link">${e.title}</span>\n <span class="nav-expand-toggle" aria-label="Expand"></span>\n ${e.summary?`<span class="nav-summary">${e.summary}</span>`:""}\n `,s.querySelector(".nav-title-link").addEventListener("click",t=>{t.stopPropagation(),F(e.id,{scrollToHighlight:Boolean(q)})}),s.querySelector(".nav-expand-toggle").addEventListener("click",t=>{t.stopPropagation();const n=!M.has(e.id);j(e.id,n)}),s.addEventListener("click",t=>{if(t.target===s){const t=!M.has(e.id);j(e.id,t)}})):(s.innerHTML=`\n <span class="nav-title">${e.title}</span>\n ${e.summary?`<span class="nav-summary">${e.summary}</span>`:""}\n `,s.addEventListener("click",()=>{const t=!M.has(e.id);j(e.id,t)}));const o=document.createElement("div");o.className="nav-sublist",e.subsections.forEach(e=>{if(e.url){const t=R(e,"nav-item");return void o.appendChild(t)}if(e.subsections&&e.subsections.length){const t=document.createElement("div");t.className="nav-group nav-group-nested";const n=document.createElement("button");n.type="button",n.className="nav-parent nav-parent-nested",n.dataset.section=e.id,n.title=e.summary||e.title,n.innerHTML=`<span class="nav-title">${e.title}</span>`,n.addEventListener("click",()=>{const t=!M.has(e.id);j(e.id,t)});const a=document.createElement("div");a.className="nav-sublist nav-sublist-nested",e.subsections.forEach(e=>{if(e.url){const t=R(e,"nav-item");return void a.appendChild(t)}if(e.subsections&&e.subsections.length){const t=document.createElement("div");t.className="nav-group nav-group-deep";const n=document.createElement("button");n.type="button",n.className="nav-parent nav-parent-deep",n.dataset.section=e.id,n.title=e.summary||e.title,n.innerHTML=`<span class="nav-title">${e.title}</span>`,n.addEventListener("click",()=>{const t=!M.has(e.id);j(e.id,t)});const s=document.createElement("div");s.className="nav-sublist nav-sublist-deep",e.subsections.forEach(e=>{if(e.url){const t=R(e,"nav-item");return void s.appendChild(t)}if(e.subsections&&e.subsections.length){const t=document.createElement("div");t.className="nav-group nav-group-ultra";const n=document.createElement("button");n.type="button",n.className="nav-parent nav-parent-ultra",n.dataset.section=e.id,n.title=e.summary||e.title,n.innerHTML=`<span class="nav-title">${e.title}</span>`,n.addEventListener("click",()=>{const t=!M.has(e.id);j(e.id,t)});const a=document.createElement("div");a.className="nav-sublist nav-sublist-ultra",e.subsections.forEach(e=>{if(e.url){const t=R(e,"nav-item");return void a.appendChild(t)}const t=document.createElement("button");t.type="button",t.className="nav-item nav-item-ultra"+(e.type?` nav-type-${e.type}`:""),t.dataset.section=e.id,t.title=e.summary||e.title,t.innerHTML=`\n <span class="nav-title">${e.title}</span>\n <span class="nav-summary">${e.summary||""}</span>\n `,t.addEventListener("click",()=>F(e.id,{scrollToHighlight:Boolean(q)})),a.appendChild(t),H.set(e.id,t)}),t.append(n,a),s.appendChild(t),H.set(e.id,n),I.set(e.id,{group:t,button:n,list:a});const o=M.has(e.id)&&!e.collapsed;return void j(e.id,o)}const t=document.createElement("button");t.type="button",t.className="nav-item nav-item-deep"+(e.type?` nav-type-${e.type}`:""),t.dataset.section=e.id,t.title=e.summary||e.title,t.innerHTML=`\n <span class="nav-title">${e.title}${"press-release"===e.type?'<span class="nav-type-icon" aria-label="Press Release"></span>':""}</span>\n <span class="nav-summary">${e.summary||""}</span>\n `,t.addEventListener("click",()=>F(e.id,{scrollToHighlight:Boolean(q)})),s.appendChild(t),H.set(e.id,t)}),t.append(n,s),a.appendChild(t),H.set(e.id,n),I.set(e.id,{group:t,button:n,list:s});const o=M.has(e.id)&&!e.collapsed;return void j(e.id,o)}const t=document.createElement("button");t.type="button",t.className="nav-item nav-item-nested"+(e.type?` nav-type-${e.type}`:""),t.dataset.section=e.id,t.title=e.summary||e.title,t.innerHTML=`\n <span class="nav-title">${e.title}${"press-release"===e.type?'<span class="nav-type-icon" aria-label="Press Release"></span>':""}</span>\n <span class="nav-summary">${e.summary||""}</span>\n `,t.addEventListener("click",()=>F(e.id,{scrollToHighlight:Boolean(q)})),a.appendChild(t),H.set(e.id,t)}),t.append(n,a),o.appendChild(t),H.set(e.id,n),I.set(e.id,{group:t,button:n,list:a});const s=M.has(e.id)&&!e.collapsed;return void j(e.id,s)}const t=document.createElement("button");t.type="button",t.className="nav-item"+(e.type?` nav-type-${e.type}`:""),t.dataset.section=e.id,t.title=e.summary,t.innerHTML=`\n <span class="nav-title">${e.title}${"press-release"===e.type?'<span class="nav-type-icon" aria-label="Press Release"></span>':""}</span>\n <span class="nav-summary">${e.summary}</span>\n `,t.addEventListener("click",()=>F(e.id,{scrollToHighlight:Boolean(q)})),o.appendChild(t),H.set(e.id,t)}),n.append(s,o),g.appendChild(n),H.set(e.id,s),I.set(e.id,{group:n,button:s,list:o});const i=!e.collapsed&&(M.has(e.id)||!t&&!M.size);j(e.id,i),i&&(t=!0)}else{const t=document.createElement("button");t.type="button",t.className="nav-leaf"+(e.type?` nav-type-${e.type}`:""),t.dataset.section=e.id,t.title=e.summary,t.innerHTML=`\n <span class="nav-title">${e.title}${"press-release"===e.type?'<span class="nav-type-icon" aria-label="Press Release"></span>':""}</span>\n <span class="nav-summary">${e.summary}</span>\n `,t.addEventListener("click",()=>F(e.id,{scrollToHighlight:Boolean(q)})),g.appendChild(t),H.set(e.id,t)}})}(),q&&(B=!0),window.addEventListener("hashchange",()=>{q&&(B=!0),O()}),b.textContent=(new Date).getFullYear(),O(),L&&T&&x&&N&&(L.addEventListener("click",()=>{P?_():W()}),x.addEventListener("input",()=>{const e=x.value;J(e,!0),z(e)}),x.addEventListener("keydown",e=>{const t=A.length-1;if("ArrowDown"===e.key)e.preventDefault(),S=Math.min(t,S+1),X();else if("ArrowUp"===e.key)e.preventDefault(),S=Math.max(0,S-1),X();else if("Enter"===e.key){e.preventDefault();const t=A[S];t&&(J(x.value,!0),F(t.id,{scrollToHighlight:!0}),_())}else"Escape"===e.key&&(e.preventDefault(),_())}),N.addEventListener("click",e=>{const t=e.target.closest("[data-section]");if(!t)return;const n=t.dataset.section;n&&(J(x.value,!0),F(n,{scrollToHighlight:!0}),_())}),T.addEventListener("click",e=>{e.target===T&&_()}),window.addEventListener("keydown",e=>{const t=e.target,n=t&&("INPUT"===t.tagName||"TEXTAREA"===t.tagName||t.isContentEditable),a=e.metaKey||e.ctrlKey;"k"===e.key.toLowerCase()&&a||"/"===e.key&&!n?(e.preventDefault(),P?_():W()):"Escape"===e.key&&P&&(e.preventDefault(),_())}));let V=null,U=!1;async function z(t){N&&(!U&&t.trim()&&(U=!0,N.innerHTML='<li class="cmd-item cmd-loading">Indexing content...</li>'),clearTimeout(V),V=setTimeout(async()=>{A=await c(e,t);const n=D();S=d(A,n),function(){if(N){if(N.innerHTML="",!A.length){const e=document.createElement("li");return e.className="cmd-item",e.setAttribute("aria-selected","false"),e.textContent="No matches.",void N.appendChild(e)}A.forEach(e=>{const t=document.createElement("li");t.className="cmd-item",t.dataset.section=e.id,t.setAttribute("role","option");const n=document.createElement("span");if(n.className="cmd-item-title",n.textContent=e.title,e.group){const t=document.createElement("span");t.className="cmd-item-group",t.textContent=e.group,n.prepend(t)}const a=document.createElement("span");a.className="cmd-item-summary",a.textContent=e.summary||"",t.append(n,a),N.appendChild(t)})}}(),X(),U=!1},t.trim()?150:0))}function X(){N&&Array.from(N.children).forEach((e,t)=>{const n=t===S&&A.length;e.setAttribute("aria-selected",n?"true":"false"),n&&e.scrollIntoView({block:"nearest"})})}function G(e){const t=document.createElement("div");t.innerHTML=e,t.querySelectorAll("script").forEach(e=>e.remove()),t.querySelectorAll("button").forEach(e=>e.removeAttribute("onclick")),t.querySelectorAll("mark.hl").forEach(e=>{const t=document.createTextNode(e.textContent||"");e.replaceWith(t)});const n=t.querySelector("section");return n?n.innerHTML:t.innerHTML}function J(e,t=!1){q=e.trim(),t&&(q?localStorage.setItem(k,q):localStorage.removeItem(k)),K()}function K(e=!1){y&&function(e,t,{scrollToFirst:n=!1}={}){if(!e)return;if(function(e){e&&e.querySelectorAll("mark.hl").forEach(e=>{const t=document.createTextNode(e.textContent||"");e.replaceWith(t)})}(e),!t)return;const a=t.split(/\s+/).map(e=>e.trim()).filter(Boolean);if(!a.length)return;const s=a.map(e=>e.toLowerCase()),o=new Set(["SCRIPT","STYLE","CODE","PRE"]),i=document.createTreeWalker(e,NodeFilter.SHOW_TEXT,{acceptNode(e){if(!e.nodeValue||!e.nodeValue.trim())return NodeFilter.FILTER_REJECT;const t=e.parentNode;return t&&o.has(t.tagName)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}}),c=[];let r;for(;r=i.nextNode();){const e=r.nodeValue.toLowerCase();s.some(t=>e.includes(t))&&c.push(r)}const d=new RegExp(`(${a.map(l).join("|")})`,"gi");c.forEach(e=>{const t=e.nodeValue,n=[];let a=0;t.replace(d,(e,s,o)=>{o>a&&n.push(document.createTextNode(t.slice(a,o)));const i=document.createElement("mark");return i.className="hl",i.textContent=e,n.push(i),a=o+e.length,e}),a<t.length&&n.push(document.createTextNode(t.slice(a)));const s=document.createDocumentFragment();n.forEach(e=>s.appendChild(e)),e.parentNode.replaceChild(s,e)}),n&&requestAnimationFrame(()=>{const t=e.querySelector("mark.hl");t&&t.scrollIntoView({behavior:"smooth",block:"center"})})}(y,q,{scrollToFirst:e})}E&&E.addEventListener("click",function(){const t=document.createElement("div");t.className="export-options-overlay",t.innerHTML='\n <div class="export-options-modal">\n <div class="export-options-header">EXPORT OPTIONS</div>\n <div class="export-options-buttons">\n <button type="button" class="export-option-btn" data-scope="page">\n <span class="export-option-title">Current Page</span>\n <span class="export-option-desc">Export only this section</span>\n </button>\n <button type="button" class="export-option-btn" data-scope="site">\n <span class="export-option-title">Entire Site</span>\n <span class="export-option-desc">Export all documentation</span>\n </button>\n </div>\n <button type="button" class="export-cancel-btn">Cancel</button>\n </div>\n ',document.body.appendChild(t),setTimeout(()=>t.classList.add("active"),10);const n=()=>{t.classList.remove("active"),setTimeout(()=>t.remove(),200)};t.querySelector(".export-cancel-btn").addEventListener("click",n),t.addEventListener("click",e=>{e.target===t&&n()}),t.querySelectorAll(".export-option-btn").forEach(t=>{t.addEventListener("click",()=>{const a=t.dataset.scope;n(),async function(t="site"){if(!E)return;const n=E.innerHTML,a=window.open("","_blank","width=1,height=1,left=0,top=0");if(!a||a.closed||void 0===a.closed)return void confirm("Pop-ups are blocked. Please allow pop-ups for this site to export the document.\n\nWould you like to try again after enabling pop-ups?");a.close(),E.disabled=!0;const s=document.createElement("div");s.className="export-loading-overlay",s.innerHTML='\n <div class="export-loading-modal">\n <div class="export-loading-header">\n <div class="export-loading-title">COMPILING DOCUMENTATION</div>\n <div class="export-loading-subtitle">Assembling all sections into unified document</div>\n </div>\n <div class="export-loading-progress">\n <div class="export-loading-bar">\n <div class="export-loading-fill"></div>\n </div>\n <div class="export-loading-status-container">\n <div class="export-loading-status">Initializing...</div>\n </div>\n </div>\n <div class="export-loading-scanner">\n <div class="scanner-line"></div>\n </div>\n </div>\n ',document.body.appendChild(s),setTimeout(()=>s.classList.add("active"),10);const i=s.querySelector(".export-loading-fill"),l=s.querySelector(".export-loading-status");try{let n;if("page"===t){const t=D(),a=v(e).find(e=>e.id===t);n=a?[a]:[]}else n=v(e);if(0===n.length)return alert("No content available to export."),s.remove(),void(E.disabled=!1);const a=[],c=n.length;let r=0;for(const e of n){r++;const n=r/c*100;i.style.width=`${n}%`,l.textContent="page"===t?`Exporting: ${e.title}`:`Processing section ${r} of ${c}: ${e.title}`,await new Promise(e=>setTimeout(e,50));try{const t=await import(e.module),n=t.load||t.default;if("function"!=typeof n)continue;const s=G((await n()).html||"");a.push({section:e,html:s})}catch(t){console.error("Failed to include section in export",e.id,t)}}l.textContent="Generating document...",await new Promise(e=>setTimeout(e,200));const d=u(a,o);l.textContent="Opening document viewer...",await new Promise(e=>setTimeout(e,100));const m=window.open("","_blank","width=900,height=860,scrollbars=yes,resizable=yes");if(!m)return alert("Please allow pop-ups to export the document."),void s.remove();m.document.open(),m.document.write(d),m.document.close(),m.focus(),s.classList.remove("active"),setTimeout(()=>s.remove(),300)}catch(e){console.error("Export failed",e),alert("Export failed. Check console for details."),s.remove()}finally{E.disabled=!1,E.innerHTML=n}}(a)})})}),w&&C&&(w.addEventListener("click",()=>{C.classList.contains("mobile-open")?(C.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),w.setAttribute("aria-expanded","false")):(C.classList.add("mobile-open"),document.body.classList.add("menu-open"),w.setAttribute("aria-expanded","true"))}),g.addEventListener("click",e=>{if(window.innerWidth<=960){const t=e.target.closest(".nav-item, .nav-leaf, .nav-parent");t&&(t.classList.contains("nav-item")||t.classList.contains("nav-leaf"))&&(C.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),w.setAttribute("aria-expanded","false"))}}),document.addEventListener("click",e=>{window.innerWidth<=960&&C.classList.contains("mobile-open")&&!C.contains(e.target)&&!w.contains(e.target)&&(C.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),w.setAttribute("aria-expanded","false"))}));
1
+ import{MANIFEST as e,DEFAULT_SECTION as t,findSection as n,getAdjacentSections as a,SITE_CONFIG as s,EXPORT_CONFIG as o}from"./manifest.js";import{updateMetaTags as i}from"./seo.js";import{escapeRegExp as l,searchContent as r,flattenManifest as c,findPreferredIndex as d}from"./lib/search.js";import{resolveTarget as m,resolveEntry as p}from"./lib/router.js";import{composeExportDocument as u,collectExportableSections as v}from"./lib/export.js";import{renderMermaidBlocks as h}from"./mermaid-init.js";import{highlightCodeBlocks as f}from"./syntax-highlight.js";const y=document.getElementById("app"),g=document.getElementById("nav"),b=document.getElementById("year"),E=document.getElementById("exportBtn"),L=document.getElementById("commandToggle"),T=document.getElementById("commandPalette"),x=document.getElementById("commandInput"),w=document.getElementById("commandList"),N=document.getElementById("mobileMenuToggle"),C=document.querySelector(".sidebar"),k="docs-toolkit-command-query",$=new Map,H=new Map,I=new Map,M=new Set;let S=[],A=0,P=!1,q=(localStorage.getItem(k)||"").trim(),B=!1;function D(e,t){const n=document.createElement("a");return n.href=e.url,n.target="_blank",n.rel="noopener noreferrer",n.className=`${t} nav-external`,n.title=e.summary||e.title,n.innerHTML=`\n <span class="nav-title">${e.title}<span class="nav-external-icon" aria-label="(opens in new tab)">↗</span></span>\n ${e.summary?`<span class="nav-summary">${e.summary}</span>`:""}\n `,n}function R(e,t={}){const{scrollToHighlight:a=!1}=t,{targetId:s,parentId:o}=function(e){return m(e,n)}(e);P&&W(),o&&O(o,!0),B=a||Boolean(q),location.hash.replace("#","")===s?j():location.hash=`#${s}`}function F(){return location.hash.replace("#","")||t}async function j(){const e=F(),t=p(e,n);if(!t)return;const{entry:o,targetId:l,parentId:r}=t;l===e?(r&&O(r,!0),function(e,t=null){H.forEach(e=>{e.setAttribute("aria-current","false")});const n=H.get(e);if(n&&n.setAttribute("aria-current","page"),t){const e=H.get(t);e&&e.setAttribute("aria-current","page")}}(o.id,r),await async function(e){if(!e)return;const t=await import(e.module),n=t.load||t.default;if("function"!=typeof n)return void(y.innerHTML='<article class="section"><p>Section failed to load.</p></article>');const o=await n();y.innerHTML=o.html||"",function(e){if(!e||!e.showDate&&!e.showReadingTime&&!e.showSummary)return;const t=(y.querySelector(".doc-content")||y.querySelector("article, section")||y).querySelector("h1");if(!t)return;const n=[];e.showDate&&e.date&&n.push(function(e){const t=String(e||"").trim();if(!t)return"";const n=/^\d{4}-\d{2}-\d{2}$/.test(t)?`${t}T00:00:00Z`:t,a=new Date(n);return Number.isNaN(a.getTime())?t:new Intl.DateTimeFormat(void 0,{year:"numeric",month:"long",day:"numeric",timeZone:"UTC"}).format(a)}(e.date)),e.showReadingTime&&e.reading_time&&n.push(`${e.reading_time} min read`);let a=t;if(n.length>0){const e=document.createElement("p");e.className="doc-meta",e.textContent=n.join(" · "),t.after(e),a=e}if(e.showSummary&&e.summary){const t=document.createElement("p");t.className="doc-summary",t.textContent=e.summary,a.after(t)}}(e),await h(y),await f(y),function(e){const t=y.querySelector(".bottom-nav");if(t&&t.remove(),"never"===s.bottomNav)return;const n=(s.bottomNavSections||[]).some(t=>e.startsWith(t)),o="mobile"===s.bottomNav&&!n,{prev:i,next:l}=a(e);if(!i&&!l)return;const r=document.createElement("nav");if(r.className="bottom-nav",o&&r.classList.add("mobile-only"),i){const e=document.createElement("div");e.className="bottom-nav-item bottom-nav-prev",e.innerHTML='<span class="bottom-nav-chevron">‹</span>';const t=document.createElement("a");t.href=`#${i.id}`,t.className="bottom-nav-link",t.title=`Previous: ${i.title}`,t.textContent=i.title,t.addEventListener("click",e=>{e.preventDefault(),R(i.id)}),e.appendChild(t),r.appendChild(e)}else{const e=document.createElement("div");e.className="bottom-nav-spacer",r.appendChild(e)}if(l){const e=document.createElement("div");e.className="bottom-nav-item bottom-nav-next";const t=document.createElement("a");t.href=`#${l.id}`,t.className="bottom-nav-link",t.title=`Next: ${l.title}`,t.textContent=l.title,t.addEventListener("click",e=>{e.preventDefault(),R(l.id)}),e.appendChild(t),e.innerHTML+='<span class="bottom-nav-chevron">›</span>',r.appendChild(e)}(y.querySelector("section")||y).appendChild(r)}(e.id),y.scrollTop=0,window.scrollTo(0,0),"function"==typeof o.afterRender&&o.afterRender(y),i({title:e.title,description:e.summary,siteTitle:s.siteTitle,siteUrl:s.siteUrl,sectionId:e.id,ogImage:e.ogImage||s.ogImage}),$.set(e.id,Date.now());const l=B;B=!1,K(l),requestAnimationFrame(()=>y.focus())}(o)):location.replace(`#${l}`)}function O(e,t){if(!e)return;const n=I.get(e);t?(M.add(e),n&&n.group.classList.add("expanded")):(M.delete(e),n&&n.group.classList.remove("expanded"))}function _(){if(!T||!x)return;P=!0,T.hidden=!1;const e=q;x.value=e,z(e),requestAnimationFrame(()=>{x.focus(),e&&x.select()})}function W(){T&&x&&(P=!1,T.hidden=!0,x.blur())}!function(){g.innerHTML="",H.clear(),I.clear();let t=M.size>0;e.forEach((e,n)=>{if(e.url){const t=D(e,"nav-leaf");return void g.appendChild(t)}if(e.subsections&&e.subsections.length){const n=document.createElement("div");n.className="nav-group";const a=Boolean(e.module),s=document.createElement("button");s.type="button",s.className="nav-parent"+(a?" nav-parent-with-content":""),s.dataset.section=e.id,s.title=e.summary,a?(s.innerHTML=`\n <span class="nav-title-link">${e.title}</span>\n <span class="nav-expand-toggle" aria-label="Expand"></span>\n ${e.summary?`<span class="nav-summary">${e.summary}</span>`:""}\n `,s.querySelector(".nav-title-link").addEventListener("click",t=>{t.stopPropagation(),R(e.id,{scrollToHighlight:Boolean(q)})}),s.querySelector(".nav-expand-toggle").addEventListener("click",t=>{t.stopPropagation();const n=!M.has(e.id);O(e.id,n)}),s.addEventListener("click",t=>{if(t.target===s){const t=!M.has(e.id);O(e.id,t)}})):(s.innerHTML=`\n <span class="nav-title">${e.title}</span>\n ${e.summary?`<span class="nav-summary">${e.summary}</span>`:""}\n `,s.addEventListener("click",()=>{const t=!M.has(e.id);O(e.id,t)}));const o=document.createElement("div");o.className="nav-sublist",e.subsections.forEach(e=>{if(e.url){const t=D(e,"nav-item");return void o.appendChild(t)}if(e.subsections&&e.subsections.length){const t=document.createElement("div");t.className="nav-group nav-group-nested";const n=document.createElement("button");n.type="button",n.className="nav-parent nav-parent-nested",n.dataset.section=e.id,n.title=e.summary||e.title,n.innerHTML=`<span class="nav-title">${e.title}</span>`,n.addEventListener("click",()=>{const t=!M.has(e.id);O(e.id,t)});const a=document.createElement("div");a.className="nav-sublist nav-sublist-nested",e.subsections.forEach(e=>{if(e.url){const t=D(e,"nav-item");return void a.appendChild(t)}if(e.subsections&&e.subsections.length){const t=document.createElement("div");t.className="nav-group nav-group-deep";const n=document.createElement("button");n.type="button",n.className="nav-parent nav-parent-deep",n.dataset.section=e.id,n.title=e.summary||e.title,n.innerHTML=`<span class="nav-title">${e.title}</span>`,n.addEventListener("click",()=>{const t=!M.has(e.id);O(e.id,t)});const s=document.createElement("div");s.className="nav-sublist nav-sublist-deep",e.subsections.forEach(e=>{if(e.url){const t=D(e,"nav-item");return void s.appendChild(t)}if(e.subsections&&e.subsections.length){const t=document.createElement("div");t.className="nav-group nav-group-ultra";const n=document.createElement("button");n.type="button",n.className="nav-parent nav-parent-ultra",n.dataset.section=e.id,n.title=e.summary||e.title,n.innerHTML=`<span class="nav-title">${e.title}</span>`,n.addEventListener("click",()=>{const t=!M.has(e.id);O(e.id,t)});const a=document.createElement("div");a.className="nav-sublist nav-sublist-ultra",e.subsections.forEach(e=>{if(e.url){const t=D(e,"nav-item");return void a.appendChild(t)}const t=document.createElement("button");t.type="button",t.className="nav-item nav-item-ultra"+(e.type?` nav-type-${e.type}`:""),t.dataset.section=e.id,t.title=e.summary||e.title,t.innerHTML=`\n <span class="nav-title">${e.title}</span>\n <span class="nav-summary">${e.summary||""}</span>\n `,t.addEventListener("click",()=>R(e.id,{scrollToHighlight:Boolean(q)})),a.appendChild(t),H.set(e.id,t)}),t.append(n,a),s.appendChild(t),H.set(e.id,n),I.set(e.id,{group:t,button:n,list:a});const o=M.has(e.id)&&!e.collapsed;return void O(e.id,o)}const t=document.createElement("button");t.type="button",t.className="nav-item nav-item-deep"+(e.type?` nav-type-${e.type}`:""),t.dataset.section=e.id,t.title=e.summary||e.title,t.innerHTML=`\n <span class="nav-title">${e.title}${"press-release"===e.type?'<span class="nav-type-icon" aria-label="Press Release"></span>':""}</span>\n <span class="nav-summary">${e.summary||""}</span>\n `,t.addEventListener("click",()=>R(e.id,{scrollToHighlight:Boolean(q)})),s.appendChild(t),H.set(e.id,t)}),t.append(n,s),a.appendChild(t),H.set(e.id,n),I.set(e.id,{group:t,button:n,list:s});const o=M.has(e.id)&&!e.collapsed;return void O(e.id,o)}const t=document.createElement("button");t.type="button",t.className="nav-item nav-item-nested"+(e.type?` nav-type-${e.type}`:""),t.dataset.section=e.id,t.title=e.summary||e.title,t.innerHTML=`\n <span class="nav-title">${e.title}${"press-release"===e.type?'<span class="nav-type-icon" aria-label="Press Release"></span>':""}</span>\n <span class="nav-summary">${e.summary||""}</span>\n `,t.addEventListener("click",()=>R(e.id,{scrollToHighlight:Boolean(q)})),a.appendChild(t),H.set(e.id,t)}),t.append(n,a),o.appendChild(t),H.set(e.id,n),I.set(e.id,{group:t,button:n,list:a});const s=M.has(e.id)&&!e.collapsed;return void O(e.id,s)}const t=document.createElement("button");t.type="button",t.className="nav-item"+(e.type?` nav-type-${e.type}`:""),t.dataset.section=e.id,t.title=e.summary,t.innerHTML=`\n <span class="nav-title">${e.title}${"press-release"===e.type?'<span class="nav-type-icon" aria-label="Press Release"></span>':""}</span>\n <span class="nav-summary">${e.summary}</span>\n `,t.addEventListener("click",()=>R(e.id,{scrollToHighlight:Boolean(q)})),o.appendChild(t),H.set(e.id,t)}),n.append(s,o),g.appendChild(n),H.set(e.id,s),I.set(e.id,{group:n,button:s,list:o});const i=!e.collapsed&&(M.has(e.id)||!t&&!M.size);O(e.id,i),i&&(t=!0)}else{const t=document.createElement("button");t.type="button",t.className="nav-leaf"+(e.type?` nav-type-${e.type}`:""),t.dataset.section=e.id,t.title=e.summary,t.innerHTML=`\n <span class="nav-title">${e.title}${"press-release"===e.type?'<span class="nav-type-icon" aria-label="Press Release"></span>':""}</span>\n <span class="nav-summary">${e.summary}</span>\n `,t.addEventListener("click",()=>R(e.id,{scrollToHighlight:Boolean(q)})),g.appendChild(t),H.set(e.id,t)}})}(),q&&(B=!0),window.addEventListener("hashchange",()=>{q&&(B=!0),j()}),b.textContent=(new Date).getFullYear(),j(),L&&T&&x&&w&&(L.addEventListener("click",()=>{P?W():_()}),x.addEventListener("input",()=>{const e=x.value;J(e,!0),z(e)}),x.addEventListener("keydown",e=>{const t=S.length-1;if("ArrowDown"===e.key)e.preventDefault(),A=Math.min(t,A+1),X();else if("ArrowUp"===e.key)e.preventDefault(),A=Math.max(0,A-1),X();else if("Enter"===e.key){e.preventDefault();const t=S[A];t&&(J(x.value,!0),R(t.id,{scrollToHighlight:!0}),W())}else"Escape"===e.key&&(e.preventDefault(),W())}),w.addEventListener("click",e=>{const t=e.target.closest("[data-section]");if(!t)return;const n=t.dataset.section;n&&(J(x.value,!0),R(n,{scrollToHighlight:!0}),W())}),T.addEventListener("click",e=>{e.target===T&&W()}),window.addEventListener("keydown",e=>{const t=e.target,n=t&&("INPUT"===t.tagName||"TEXTAREA"===t.tagName||t.isContentEditable),a=e.metaKey||e.ctrlKey;"k"===e.key.toLowerCase()&&a||"/"===e.key&&!n?(e.preventDefault(),P?W():_()):"Escape"===e.key&&P&&(e.preventDefault(),W())}));let U=null,V=!1;async function z(t){w&&(!V&&t.trim()&&(V=!0,w.innerHTML='<li class="cmd-item cmd-loading">Indexing content...</li>'),clearTimeout(U),U=setTimeout(async()=>{S=await r(e,t);const n=F();A=d(S,n),function(){if(w){if(w.innerHTML="",!S.length){const e=document.createElement("li");return e.className="cmd-item",e.setAttribute("aria-selected","false"),e.textContent="No matches.",void w.appendChild(e)}S.forEach(e=>{const t=document.createElement("li");t.className="cmd-item",t.dataset.section=e.id,t.setAttribute("role","option");const n=document.createElement("span");if(n.className="cmd-item-title",n.textContent=e.title,e.group){const t=document.createElement("span");t.className="cmd-item-group",t.textContent=e.group,n.prepend(t)}const a=document.createElement("span");a.className="cmd-item-summary",a.textContent=e.summary||"",t.append(n,a),w.appendChild(t)})}}(),X(),V=!1},t.trim()?150:0))}function X(){w&&Array.from(w.children).forEach((e,t)=>{const n=t===A&&S.length;e.setAttribute("aria-selected",n?"true":"false"),n&&e.scrollIntoView({block:"nearest"})})}function G(e){const t=document.createElement("div");t.innerHTML=e,t.querySelectorAll("script").forEach(e=>e.remove()),t.querySelectorAll("button").forEach(e=>e.removeAttribute("onclick")),t.querySelectorAll("mark.hl").forEach(e=>{const t=document.createTextNode(e.textContent||"");e.replaceWith(t)});const n=t.querySelector("section");return n?n.innerHTML:t.innerHTML}function J(e,t=!1){q=e.trim(),t&&(q?localStorage.setItem(k,q):localStorage.removeItem(k)),K()}function K(e=!1){y&&function(e,t,{scrollToFirst:n=!1}={}){if(!e)return;if(function(e){e&&e.querySelectorAll("mark.hl").forEach(e=>{const t=document.createTextNode(e.textContent||"");e.replaceWith(t)})}(e),!t)return;const a=t.split(/\s+/).map(e=>e.trim()).filter(Boolean);if(!a.length)return;const s=a.map(e=>e.toLowerCase()),o=new Set(["SCRIPT","STYLE","CODE","PRE"]),i=document.createTreeWalker(e,NodeFilter.SHOW_TEXT,{acceptNode(e){if(!e.nodeValue||!e.nodeValue.trim())return NodeFilter.FILTER_REJECT;const t=e.parentNode;return t&&o.has(t.tagName)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}}),r=[];let c;for(;c=i.nextNode();){const e=c.nodeValue.toLowerCase();s.some(t=>e.includes(t))&&r.push(c)}const d=new RegExp(`(${a.map(l).join("|")})`,"gi");r.forEach(e=>{const t=e.nodeValue,n=[];let a=0;t.replace(d,(e,s,o)=>{o>a&&n.push(document.createTextNode(t.slice(a,o)));const i=document.createElement("mark");return i.className="hl",i.textContent=e,n.push(i),a=o+e.length,e}),a<t.length&&n.push(document.createTextNode(t.slice(a)));const s=document.createDocumentFragment();n.forEach(e=>s.appendChild(e)),e.parentNode.replaceChild(s,e)}),n&&requestAnimationFrame(()=>{const t=e.querySelector("mark.hl");t&&t.scrollIntoView({behavior:"smooth",block:"center"})})}(y,q,{scrollToFirst:e})}E&&E.addEventListener("click",function(){const t=document.createElement("div");t.className="export-options-overlay",t.innerHTML='\n <div class="export-options-modal">\n <div class="export-options-header">EXPORT OPTIONS</div>\n <div class="export-options-buttons">\n <button type="button" class="export-option-btn" data-scope="page">\n <span class="export-option-title">Current Page</span>\n <span class="export-option-desc">Export only this section</span>\n </button>\n <button type="button" class="export-option-btn" data-scope="site">\n <span class="export-option-title">Entire Site</span>\n <span class="export-option-desc">Export all documentation</span>\n </button>\n </div>\n <button type="button" class="export-cancel-btn">Cancel</button>\n </div>\n ',document.body.appendChild(t),setTimeout(()=>t.classList.add("active"),10);const n=()=>{t.classList.remove("active"),setTimeout(()=>t.remove(),200)};t.querySelector(".export-cancel-btn").addEventListener("click",n),t.addEventListener("click",e=>{e.target===t&&n()}),t.querySelectorAll(".export-option-btn").forEach(t=>{t.addEventListener("click",()=>{const a=t.dataset.scope;n(),async function(t="site"){if(!E)return;const n=E.innerHTML,a=window.open("","_blank","width=1,height=1,left=0,top=0");if(!a||a.closed||void 0===a.closed)return void confirm("Pop-ups are blocked. Please allow pop-ups for this site to export the document.\n\nWould you like to try again after enabling pop-ups?");a.close(),E.disabled=!0;const s=document.createElement("div");s.className="export-loading-overlay",s.innerHTML='\n <div class="export-loading-modal">\n <div class="export-loading-header">\n <div class="export-loading-title">COMPILING DOCUMENTATION</div>\n <div class="export-loading-subtitle">Assembling all sections into unified document</div>\n </div>\n <div class="export-loading-progress">\n <div class="export-loading-bar">\n <div class="export-loading-fill"></div>\n </div>\n <div class="export-loading-status-container">\n <div class="export-loading-status">Initializing...</div>\n </div>\n </div>\n <div class="export-loading-scanner">\n <div class="scanner-line"></div>\n </div>\n </div>\n ',document.body.appendChild(s),setTimeout(()=>s.classList.add("active"),10);const i=s.querySelector(".export-loading-fill"),l=s.querySelector(".export-loading-status");try{let n;if("page"===t){const t=F(),a=v(e).find(e=>e.id===t);n=a?[a]:[]}else n=v(e);if(0===n.length)return alert("No content available to export."),s.remove(),void(E.disabled=!1);const a=[],r=n.length;let c=0;for(const e of n){c++;const n=c/r*100;i.style.width=`${n}%`,l.textContent="page"===t?`Exporting: ${e.title}`:`Processing section ${c} of ${r}: ${e.title}`,await new Promise(e=>setTimeout(e,50));try{const t=await import(e.module),n=t.load||t.default;if("function"!=typeof n)continue;const s=G((await n()).html||"");a.push({section:e,html:s})}catch(t){console.error("Failed to include section in export",e.id,t)}}l.textContent="Generating document...",await new Promise(e=>setTimeout(e,200));const d=u(a,o);l.textContent="Opening document viewer...",await new Promise(e=>setTimeout(e,100));const m=window.open("","_blank","width=900,height=860,scrollbars=yes,resizable=yes");if(!m)return alert("Please allow pop-ups to export the document."),void s.remove();m.document.open(),m.document.write(d),m.document.close(),m.focus(),s.classList.remove("active"),setTimeout(()=>s.remove(),300)}catch(e){console.error("Export failed",e),alert("Export failed. Check console for details."),s.remove()}finally{E.disabled=!1,E.innerHTML=n}}(a)})})}),N&&C&&(N.addEventListener("click",()=>{C.classList.contains("mobile-open")?(C.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),N.setAttribute("aria-expanded","false")):(C.classList.add("mobile-open"),document.body.classList.add("menu-open"),N.setAttribute("aria-expanded","true"))}),g.addEventListener("click",e=>{if(window.innerWidth<=960){const t=e.target.closest(".nav-item, .nav-leaf, .nav-parent");t&&(t.classList.contains("nav-item")||t.classList.contains("nav-leaf"))&&(C.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),N.setAttribute("aria-expanded","false"))}}),document.addEventListener("click",e=>{window.innerWidth<=960&&C.classList.contains("mobile-open")&&!C.contains(e.target)&&!N.contains(e.target)&&(C.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),N.setAttribute("aria-expanded","false"))}));
package/site/index.html CHANGED
@@ -5,9 +5,9 @@
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1" />
6
6
  <title>Pagenary Docs</title>
7
7
  <meta name="description" content="Pagenary developer documentation — building, configuring, deploying, and extending the multi-tenant documentation publisher, published with Pagenary itself." />
8
- <link rel="icon" type="image/png" href="./favicon.png" />
9
- <link rel="stylesheet" href="./styles.css" />
10
- <meta name="x-build" content="2026-05-28T16:02:34.220Z" />
8
+ <link rel="icon" type="image/png" href="/favicon.png" />
9
+ <link rel="stylesheet" href="/styles.css" />
10
+ <meta name="x-build" content="2026-06-13T16:49:25.891Z" />
11
11
  </head>
12
12
  <body>
13
13
  <a class="skip-link" href="#app">Skip to content</a>
@@ -52,6 +52,6 @@
52
52
  <ul id="commandList" class="cmd-list" role="listbox"></ul>
53
53
  </div>
54
54
  </div>
55
- <script type="module" src="./app.js"></script>
55
+ <script type="module" src="/app.js"></script>
56
56
  </body>
57
57
  </html>
@@ -1 +1 @@
1
- export function sectionEntry(t,n){const e="string"==typeof t?t:t.id,r=n(e,"string"==typeof t?{}:{title:t.title,summary:t.summary});return{id:e,title:r.title,summary:r.summary,module:`./sections/${e}.js`}}export function groupEntry(t,n){const{id:e,title:r,summary:i,sections:s}=t;return{id:e,title:r,summary:i,subsections:s.map(t=>sectionEntry(t,n))}}export function buildSectionIndex(t){const n=new Map;function e(t,r=null){r&&(t.parentId=r),n.set(t.id,t),Array.isArray(t.subsections)&&t.subsections.forEach(n=>e(n,t.id))}return t.forEach(t=>e(t)),n}export function createFindSection(t){return function(n){return t.get(n)||null}}
1
+ export function sectionEntry(t,n){const e="string"==typeof t?t:t.id,r=n(e,"string"==typeof t?{}:{title:t.title,summary:t.summary});return{id:e,title:r.title,summary:r.summary,module:`/sections/${e}.js`}}export function groupEntry(t,n){const{id:e,title:r,summary:i,sections:s}=t;return{id:e,title:r,summary:i,subsections:s.map(t=>sectionEntry(t,n))}}export function buildSectionIndex(t){const n=new Map;function e(t,r=null){r&&(t.parentId=r),n.set(t.id,t),Array.isArray(t.subsections)&&t.subsections.forEach(n=>e(n,t.id))}return t.forEach(t=>e(t)),n}export function createFindSection(t){return function(n){return t.get(n)||null}}
package/site/manifest.js CHANGED
@@ -3,7 +3,7 @@ export const MANIFEST = [
3
3
  "id": "welcome",
4
4
  "title": "Welcome",
5
5
  "summary": "What Pagenary is and how this dogfooded portal is built.",
6
- "module": "./sections/welcome.js"
6
+ "module": "/sections/welcome.js"
7
7
  },
8
8
  {
9
9
  "id": "getting-started",
@@ -14,7 +14,7 @@ export const MANIFEST = [
14
14
  "id": "quickstart",
15
15
  "title": "Quickstart",
16
16
  "summary": "Install, build the default bundle, and serve it locally.",
17
- "module": "./sections/quickstart.js"
17
+ "module": "/sections/quickstart.js"
18
18
  }
19
19
  ]
20
20
  },
@@ -27,19 +27,19 @@ export const MANIFEST = [
27
27
  "id": "developer-guide",
28
28
  "title": "Developer Guide",
29
29
  "summary": "Project layout, scripts, and the content authoring workflow.",
30
- "module": "./sections/developer-guide.js"
30
+ "module": "/sections/developer-guide.js"
31
31
  },
32
32
  {
33
33
  "id": "tenant-config",
34
34
  "title": "Tenant Configuration",
35
35
  "summary": "Every config.json option: branding, theming, SEO, and export.",
36
- "module": "./sections/tenant-config.js"
36
+ "module": "/sections/tenant-config.js"
37
37
  },
38
38
  {
39
39
  "id": "extending",
40
40
  "title": "Extending",
41
41
  "summary": "Add section templates, content types, and build behaviors.",
42
- "module": "./sections/extending.js"
42
+ "module": "/sections/extending.js"
43
43
  }
44
44
  ]
45
45
  },
@@ -52,25 +52,25 @@ export const MANIFEST = [
52
52
  "id": "architecture",
53
53
  "title": "Architecture",
54
54
  "summary": "The static SPA pattern, build pipeline, and tenant content model.",
55
- "module": "./sections/architecture.js"
55
+ "module": "/sections/architecture.js"
56
56
  },
57
57
  {
58
58
  "id": "api",
59
59
  "title": "API Reference",
60
60
  "summary": "Module-level documentation for the publisher internals.",
61
- "module": "./sections/api.js"
61
+ "module": "/sections/api.js"
62
62
  },
63
63
  {
64
64
  "id": "deployment",
65
65
  "title": "Deployment",
66
66
  "summary": "Hosting the static output and multi-tenant domain routing.",
67
- "module": "./sections/deployment.js"
67
+ "module": "/sections/deployment.js"
68
68
  },
69
69
  {
70
70
  "id": "seo-strategy",
71
71
  "title": "SEO Strategy",
72
72
  "summary": "Metadata, hash-routing considerations, and discoverability.",
73
- "module": "./sections/seo-strategy.js"
73
+ "module": "/sections/seo-strategy.js"
74
74
  }
75
75
  ]
76
76
  }
@@ -27,7 +27,7 @@
27
27
  "headline": "API Reference",
28
28
  "description": "Module-level documentation for the publisher internals.",
29
29
  "url": "https://docs.pagenary.com/pages/api.html",
30
- "dateModified": "2026-05-28",
30
+ "dateModified": "2026-06-13",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/api.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Architecture",
28
28
  "description": "The static SPA pattern, build pipeline, and tenant content model.",
29
29
  "url": "https://docs.pagenary.com/pages/architecture.html",
30
- "dateModified": "2026-05-28",
30
+ "dateModified": "2026-06-13",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/architecture.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Deployment",
28
28
  "description": "Hosting the static output and multi-tenant domain routing.",
29
29
  "url": "https://docs.pagenary.com/pages/deployment.html",
30
- "dateModified": "2026-05-28",
30
+ "dateModified": "2026-06-13",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/deployment.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Developer Guide",
28
28
  "description": "Project layout, scripts, and the content authoring workflow.",
29
29
  "url": "https://docs.pagenary.com/pages/developer-guide.html",
30
- "dateModified": "2026-05-28",
30
+ "dateModified": "2026-06-13",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/developer-guide.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Extending",
28
28
  "description": "Add section templates, content types, and build behaviors.",
29
29
  "url": "https://docs.pagenary.com/pages/extending.html",
30
- "dateModified": "2026-05-28",
30
+ "dateModified": "2026-06-13",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/extending.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Quickstart",
28
28
  "description": "Install, build the default bundle, and serve it locally.",
29
29
  "url": "https://docs.pagenary.com/pages/quickstart.html",
30
- "dateModified": "2026-05-28",
30
+ "dateModified": "2026-06-13",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/quickstart.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "SEO Strategy",
28
28
  "description": "Metadata, hash-routing considerations, and discoverability.",
29
29
  "url": "https://docs.pagenary.com/pages/seo-strategy.html",
30
- "dateModified": "2026-05-28",
30
+ "dateModified": "2026-06-13",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/seo-strategy.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Tenant Configuration",
28
28
  "description": "Every config.json option: branding, theming, SEO, and export.",
29
29
  "url": "https://docs.pagenary.com/pages/tenant-config.html",
30
- "dateModified": "2026-05-28",
30
+ "dateModified": "2026-06-13",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/tenant-config.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Welcome",
28
28
  "description": "What Pagenary is and how this dogfooded portal is built.",
29
29
  "url": "https://docs.pagenary.com/pages/welcome.html",
30
- "dateModified": "2026-05-28",
30
+ "dateModified": "2026-06-13",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/welcome.html"
package/site/robots.txt CHANGED
@@ -1,5 +1,5 @@
1
1
  # Pagenary Docs
2
- # Generated: 2026-05-28T16:02:34.576Z
2
+ # Generated: 2026-06-13T16:49:26.262Z
3
3
 
4
4
  User-agent: *
5
5
  Allow: /
package/site/sitemap.xml CHANGED
@@ -2,61 +2,61 @@
2
2
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
3
3
  <url>
4
4
  <loc>https://docs.pagenary.com/</loc>
5
- <lastmod>2026-05-28</lastmod>
5
+ <lastmod>2026-06-13</lastmod>
6
6
  <changefreq>weekly</changefreq>
7
7
  <priority>1.0</priority>
8
8
  </url>
9
9
  <url>
10
10
  <loc>https://docs.pagenary.com/pages/welcome.html</loc>
11
- <lastmod>2026-05-28</lastmod>
11
+ <lastmod>2026-06-13</lastmod>
12
12
  <changefreq>monthly</changefreq>
13
13
  <priority>0.8</priority>
14
14
  </url>
15
15
  <url>
16
16
  <loc>https://docs.pagenary.com/pages/quickstart.html</loc>
17
- <lastmod>2026-05-28</lastmod>
17
+ <lastmod>2026-06-13</lastmod>
18
18
  <changefreq>monthly</changefreq>
19
19
  <priority>0.6</priority>
20
20
  </url>
21
21
  <url>
22
22
  <loc>https://docs.pagenary.com/pages/developer-guide.html</loc>
23
- <lastmod>2026-05-28</lastmod>
23
+ <lastmod>2026-06-13</lastmod>
24
24
  <changefreq>monthly</changefreq>
25
25
  <priority>0.6</priority>
26
26
  </url>
27
27
  <url>
28
28
  <loc>https://docs.pagenary.com/pages/tenant-config.html</loc>
29
- <lastmod>2026-05-28</lastmod>
29
+ <lastmod>2026-06-13</lastmod>
30
30
  <changefreq>monthly</changefreq>
31
31
  <priority>0.6</priority>
32
32
  </url>
33
33
  <url>
34
34
  <loc>https://docs.pagenary.com/pages/extending.html</loc>
35
- <lastmod>2026-05-28</lastmod>
35
+ <lastmod>2026-06-13</lastmod>
36
36
  <changefreq>monthly</changefreq>
37
37
  <priority>0.6</priority>
38
38
  </url>
39
39
  <url>
40
40
  <loc>https://docs.pagenary.com/pages/architecture.html</loc>
41
- <lastmod>2026-05-28</lastmod>
41
+ <lastmod>2026-06-13</lastmod>
42
42
  <changefreq>monthly</changefreq>
43
43
  <priority>0.6</priority>
44
44
  </url>
45
45
  <url>
46
46
  <loc>https://docs.pagenary.com/pages/api.html</loc>
47
- <lastmod>2026-05-28</lastmod>
47
+ <lastmod>2026-06-13</lastmod>
48
48
  <changefreq>monthly</changefreq>
49
49
  <priority>0.6</priority>
50
50
  </url>
51
51
  <url>
52
52
  <loc>https://docs.pagenary.com/pages/deployment.html</loc>
53
- <lastmod>2026-05-28</lastmod>
53
+ <lastmod>2026-06-13</lastmod>
54
54
  <changefreq>monthly</changefreq>
55
55
  <priority>0.6</priority>
56
56
  </url>
57
57
  <url>
58
58
  <loc>https://docs.pagenary.com/pages/seo-strategy.html</loc>
59
- <lastmod>2026-05-28</lastmod>
59
+ <lastmod>2026-06-13</lastmod>
60
60
  <changefreq>monthly</changefreq>
61
61
  <priority>0.6</priority>
62
62
  </url>
package/site/styles.css CHANGED
@@ -684,6 +684,19 @@ mark.hl {
684
684
  color: var(--ink);
685
685
  }
686
686
 
687
+ .doc-content .doc-meta {
688
+ color: var(--muted);
689
+ font-size: 0.85rem;
690
+ letter-spacing: 0;
691
+ text-transform: uppercase;
692
+ }
693
+
694
+ .doc-content .doc-summary {
695
+ color: var(--muted);
696
+ font-size: 1.05rem;
697
+ line-height: 1.6;
698
+ }
699
+
687
700
  .doc-content ul,
688
701
  .doc-content ol {
689
702
  margin: 0;
package/src/app.js CHANGED
@@ -380,6 +380,7 @@ async function loadSection(entry) {
380
380
  }
381
381
  const payload = await loader();
382
382
  app.innerHTML = payload.html || '';
383
+ renderEntryMetadata(entry);
383
384
 
384
385
  // Render any mermaid diagrams in the content
385
386
  await renderMermaidBlocks(app);
@@ -412,6 +413,51 @@ async function loadSection(entry) {
412
413
  focusCanvas();
413
414
  }
414
415
 
416
+ function renderEntryMetadata(entry) {
417
+ if (!entry || (!entry.showDate && !entry.showReadingTime && !entry.showSummary)) return;
418
+ const content = app.querySelector('.doc-content') || app.querySelector('article, section') || app;
419
+ const heading = content.querySelector('h1');
420
+ if (!heading) return;
421
+
422
+ const fragments = [];
423
+ if (entry.showDate && entry.date) {
424
+ fragments.push(formatEntryDate(entry.date));
425
+ }
426
+ if (entry.showReadingTime && entry.reading_time) {
427
+ fragments.push(`${entry.reading_time} min read`);
428
+ }
429
+
430
+ let insertAfter = heading;
431
+ if (fragments.length > 0) {
432
+ const meta = document.createElement('p');
433
+ meta.className = 'doc-meta';
434
+ meta.textContent = fragments.join(' · ');
435
+ heading.after(meta);
436
+ insertAfter = meta;
437
+ }
438
+
439
+ if (entry.showSummary && entry.summary) {
440
+ const summary = document.createElement('p');
441
+ summary.className = 'doc-summary';
442
+ summary.textContent = entry.summary;
443
+ insertAfter.after(summary);
444
+ }
445
+ }
446
+
447
+ function formatEntryDate(value) {
448
+ const raw = String(value || '').trim();
449
+ if (!raw) return '';
450
+ const normalized = /^\d{4}-\d{2}-\d{2}$/.test(raw) ? `${raw}T00:00:00Z` : raw;
451
+ const date = new Date(normalized);
452
+ if (Number.isNaN(date.getTime())) return raw;
453
+ return new Intl.DateTimeFormat(undefined, {
454
+ year: 'numeric',
455
+ month: 'long',
456
+ day: 'numeric',
457
+ timeZone: 'UTC'
458
+ }).format(date);
459
+ }
460
+
415
461
  /**
416
462
  * Render bottom page navigation (prev/next links)
417
463
  * Visibility controlled by SITE_CONFIG.bottomNav: 'always' | 'mobile' | 'never'
package/src/index.html CHANGED
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1" />
6
6
  <title>Docs Toolkit</title>
7
7
  <meta name="description" content="Reusable documentation toolkit for multi-tenant services." />
8
- <link rel="icon" type="image/png" href="./favicon.png" />
9
- <link rel="stylesheet" href="./styles.css" />
8
+ <link rel="icon" type="image/png" href="/favicon.png" />
9
+ <link rel="stylesheet" href="/styles.css" />
10
10
  </head>
11
11
  <body>
12
12
  <a class="skip-link" href="#app">Skip to content</a>
@@ -51,6 +51,6 @@
51
51
  <ul id="commandList" class="cmd-list" role="listbox"></ul>
52
52
  </div>
53
53
  </div>
54
- <script type="module" src="./app.js"></script>
54
+ <script type="module" src="/app.js"></script>
55
55
  </body>
56
56
  </html>
@@ -17,7 +17,7 @@ export function sectionEntry(input, getSectionMetadata) {
17
17
  id: sectionId,
18
18
  title: meta.title,
19
19
  summary: meta.summary,
20
- module: `./sections/${sectionId}.js`
20
+ module: `/sections/${sectionId}.js`
21
21
  };
22
22
  }
23
23
 
package/src/manifest.js CHANGED
@@ -8,7 +8,7 @@ function sectionEntry(input) {
8
8
  id: sectionId,
9
9
  title: meta.title,
10
10
  summary: meta.summary,
11
- module: `./sections/${sectionId}.js`
11
+ module: `/sections/${sectionId}.js`
12
12
  };
13
13
  }
14
14
 
package/src/styles.css CHANGED
@@ -684,6 +684,19 @@ mark.hl {
684
684
  color: var(--ink);
685
685
  }
686
686
 
687
+ .doc-content .doc-meta {
688
+ color: var(--muted);
689
+ font-size: 0.85rem;
690
+ letter-spacing: 0;
691
+ text-transform: uppercase;
692
+ }
693
+
694
+ .doc-content .doc-summary {
695
+ color: var(--muted);
696
+ font-size: 1.05rem;
697
+ line-height: 1.6;
698
+ }
699
+
687
700
  .doc-content ul,
688
701
  .doc-content ol {
689
702
  margin: 0;