@pagenary/publisher 2026.6.12 → 2026.6.13

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.
Files changed (38) hide show
  1. package/README.md +3 -0
  2. package/package.json +2 -2
  3. package/scripts/build-tenants.js +42 -6
  4. package/scripts/lib/search-index-generator.js +16 -2
  5. package/site/app.js +1 -1
  6. package/site/index.html +1 -1
  7. package/site/lib/docs-map.js +1 -1
  8. package/site/lib/fortemi-corpus.js +1 -1
  9. package/site/lib/search.js +1 -1
  10. package/site/pages/api.html +9 -3
  11. package/site/pages/architecture.html +1 -1
  12. package/site/pages/deployment.html +1 -1
  13. package/site/pages/developer-guide.html +1 -1
  14. package/site/pages/extending.html +1 -1
  15. package/site/pages/quickstart.html +1 -1
  16. package/site/pages/seo-strategy.html +1 -1
  17. package/site/pages/tenant-config.html +4 -4
  18. package/site/pages/theming-recipes.html +4 -4
  19. package/site/pages/welcome.html +1 -1
  20. package/site/robots.txt +1 -1
  21. package/site/search-index/manifest.json +37 -2
  22. package/site/search-index/metadata.json +1541 -0
  23. package/site/search-index/part-0000.json +1182 -32
  24. package/site/sections/api.js +1 -1
  25. package/site/sections/tenant-config.js +1 -1
  26. package/site/sections/theming-recipes.js +1 -1
  27. package/site/sitemap.xml +11 -11
  28. package/site/styles.css +286 -3
  29. package/site/vendor/fortemi-aiwg-index.d.ts +41 -1
  30. package/site/vendor/fortemi-aiwg-index.js +1 -1
  31. package/src/app.js +182 -2
  32. package/src/lib/docs-map.js +293 -14
  33. package/src/lib/fortemi-corpus.js +142 -4
  34. package/src/lib/search.js +73 -0
  35. package/src/styles.css +286 -3
  36. package/src/vendor/fortemi-aiwg-index.d.ts +41 -1
  37. package/src/vendor/fortemi-aiwg-index.js +61 -6
  38. package/tenants.schema.json +26 -0
package/README.md CHANGED
@@ -92,6 +92,9 @@ inspect, build, or run the AIWG project from `~/dev/aiwg`.
92
92
  The engine is *vendored* (`@fortemi/core`) — see `docs/VENDORING.md` for the
93
93
  process, `docs/ARCHITECTURE.md`, and
94
94
  `.aiwg/architecture/adr/ADR-015-fortemi-core-search-adapter.md`.
95
+ - **Docs Map** — opt-in static graph view over the same Fortemi corpus, with
96
+ weighted relationships, zoom/pan controls, neighbor highlighting, and node
97
+ metadata popups.
95
98
  - **Manifest-Driven Nav** — declarative navigation structure
96
99
  - **Keyboard Navigation** — arrow keys, Enter to select
97
100
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagenary/publisher",
3
- "version": "2026.6.12",
3
+ "version": "2026.6.13",
4
4
  "type": "module",
5
5
  "description": "Multi-tenant static publishing component for Pagenary platform.",
6
6
  "license": "AGPL-3.0-or-later",
@@ -67,7 +67,7 @@
67
67
  "terser": "^5.48.0"
68
68
  },
69
69
  "devDependencies": {
70
- "@fortemi/core": "2026.6.7",
70
+ "@fortemi/core": "2026.6.8",
71
71
  "jest": "^30.4.2"
72
72
  }
73
73
  }
@@ -1267,6 +1267,7 @@ async function applyThemePicker(distDir, config, tenantId) {
1267
1267
  async function applyDocsMap(distDir, config, tenantId) {
1268
1268
  const docsMap = config.docsMap;
1269
1269
  if (!docsMap || !docsMap.enabled) return;
1270
+ const renderer = normalizeDocsMapRenderer(docsMap.renderer);
1270
1271
 
1271
1272
  const manifestPath = path.join(distDir, 'manifest.js');
1272
1273
  if (!(await pathExists(manifestPath))) return;
@@ -1282,21 +1283,23 @@ async function applyDocsMap(distDir, config, tenantId) {
1282
1283
  await fsp.writeFile(
1283
1284
  path.join(distDir, 'docs-map-data.js'),
1284
1285
  `export const DOCS_MAP_GRAPH = ${JSON.stringify(graphArtifact.graph)};\n` +
1285
- `export const DOCS_MAP_LABELS = ${JSON.stringify(graphArtifact.labels)};\n`,
1286
+ `export const DOCS_MAP_LABELS = ${JSON.stringify(graphArtifact.labels)};\n` +
1287
+ `export const DOCS_MAP_METADATA = ${JSON.stringify(graphArtifact.metadata)};\n`,
1286
1288
  'utf8'
1287
1289
  );
1288
1290
  await fsp.writeFile(
1289
1291
  path.join(sectionsDir, 'docs-map.js'),
1290
- "import { DOCS_MAP_GRAPH, DOCS_MAP_LABELS } from '../docs-map-data.js';\n" +
1292
+ "import * as DOCS_MAP_DATA from '../docs-map-data.js';\n" +
1291
1293
  "import { loadDocsMap } from '../lib/docs-map.js';\n" +
1292
- 'export const load = () => loadDocsMap(DOCS_MAP_GRAPH, DOCS_MAP_LABELS);\n',
1294
+ `export const load = () => loadDocsMap(DOCS_MAP_DATA.DOCS_MAP_GRAPH, DOCS_MAP_DATA.DOCS_MAP_LABELS, { renderer: ${JSON.stringify(renderer)}, metadata: DOCS_MAP_DATA.DOCS_MAP_METADATA });\n`,
1293
1295
  'utf8'
1294
1296
  );
1295
1297
  } else {
1296
1298
  // No content to analyze — fall back to the runtime (MANIFEST-derived) graph.
1297
1299
  await fsp.writeFile(
1298
1300
  path.join(sectionsDir, 'docs-map.js'),
1299
- "export { load } from '../lib/docs-map.js';\n",
1301
+ "import { loadManifestDocsMap } from '../lib/docs-map.js';\n" +
1302
+ `export const load = () => loadManifestDocsMap({ renderer: ${JSON.stringify(renderer)} });\n`,
1300
1303
  'utf8'
1301
1304
  );
1302
1305
  }
@@ -1321,7 +1324,12 @@ async function applyDocsMap(distDir, config, tenantId) {
1321
1324
  js = updated;
1322
1325
  }
1323
1326
  await fsp.writeFile(manifestPath, js, 'utf8');
1324
- console.log(` ↳ wired docs-map view for ${tenantId}`);
1327
+ console.log(` ↳ wired docs-map view for ${tenantId} (${renderer} renderer)`);
1328
+ }
1329
+
1330
+ function normalizeDocsMapRenderer(value) {
1331
+ const renderer = String(value || 'svg').trim().toLowerCase();
1332
+ return renderer === 'cytoscape' ? 'cytoscape' : 'svg';
1325
1333
  }
1326
1334
 
1327
1335
  /**
@@ -1395,12 +1403,40 @@ async function buildDocsMapGraph(distDir, tenantId) {
1395
1403
  const graph = aiwgFortemiIndexToCommunityGraph(index, { communityFacet: 'group' });
1396
1404
  if (!graph.nodes || graph.nodes.length < 2) return null;
1397
1405
 
1406
+ const recordIds = new Set(index.items.map((item) => item.id));
1407
+ const nodeMetadata = index.items.map((item) => [item.id, {
1408
+ title: item.title,
1409
+ section_id: String(item.id).replace(/^docs:page:/, ''),
1410
+ concepts: item.concepts || [],
1411
+ skos_concepts: item.skos_concepts || [],
1412
+ source: item.source || null,
1413
+ privacy: item.privacy || null,
1414
+ updated_at: item.updated_at || null
1415
+ }]);
1416
+ const edgeMetadata = [];
1417
+ for (const item of index.items) {
1418
+ for (const relationship of item.relationships || []) {
1419
+ if (!recordIds.has(relationship.target_id)) continue;
1420
+ const key = `${item.id}\0${relationship.target_id}\0${relationship.type}`;
1421
+ edgeMetadata.push([key, {
1422
+ label: relationship.label || relationship.type,
1423
+ confidence: relationship.confidence ?? null,
1424
+ privacy: relationship.privacy || null,
1425
+ shared_concepts: Array.isArray(relationship.metadata?.shared_concepts)
1426
+ ? relationship.metadata.shared_concepts
1427
+ : [],
1428
+ source_path: relationship.source_path || null
1429
+ }]);
1430
+ }
1431
+ }
1432
+
1398
1433
  const labels = sections.map((s) => [s.id, s.title]);
1434
+ const metadata = { nodes: nodeMetadata, edges: edgeMetadata };
1399
1435
  console.log(
1400
1436
  ` ↳ docs-map graph for ${tenantId}: ${graph.nodes.length} nodes, ` +
1401
1437
  `${graph.edges.length} edges, ${graph.communities.length} communities`
1402
1438
  );
1403
- return { graph, labels };
1439
+ return { graph, labels, metadata };
1404
1440
  }
1405
1441
 
1406
1442
  const NAV_POSITIONS = new Set(['left', 'right', 'top', 'bottom', 'hybrid']);
@@ -20,6 +20,7 @@ import path from 'path';
20
20
  import { pathToFileURL } from 'node:url';
21
21
  import {
22
22
  buildFortemiIndexExport,
23
+ buildFortemiMetadataExport,
23
24
  chunkFortemiIndex,
24
25
  stripHtml,
25
26
  DEFAULT_PART_SIZE
@@ -66,7 +67,10 @@ async function extractSectionText(section, distDir) {
66
67
  }
67
68
 
68
69
  /**
69
- * Generate and write the chunked Fortemi index for one tenant bundle.
70
+ * Generate and write the chunked Fortemi index for one tenant bundle. The
71
+ * static search artifact is also the page-metadata source, so it opts into
72
+ * content concepts and concept-derived relationships for richer graph/metadata
73
+ * UX while preserving the lower-level corpus builder's bare default.
70
74
  * @param {string} distDir - Tenant output directory (contains manifest.js, sections/)
71
75
  * @param {Array} processedManifest - The nested manifest array written to manifest.js
72
76
  * @param {object} [options]
@@ -88,8 +92,13 @@ export async function generateSearchIndex(distDir, processedManifest, options =
88
92
  entries.push({ section, text });
89
93
  }
90
94
 
91
- const { index, buildHash } = buildFortemiIndexExport(entries, { repo: tenantId });
95
+ const { index, buildHash } = buildFortemiIndexExport(entries, {
96
+ repo: tenantId,
97
+ extractConcepts: true,
98
+ relateByConcept: true
99
+ });
92
100
  const { manifest, parts } = chunkFortemiIndex(index, { partSize });
101
+ const metadata = buildFortemiMetadataExport(index);
93
102
 
94
103
  // Build-time validation gate (#25): assert the emitted artifact against the
95
104
  // vendored @fortemi/core contract so an invalid index fails the build clearly,
@@ -114,6 +123,11 @@ export async function generateSearchIndex(distDir, processedManifest, options =
114
123
  `${JSON.stringify(manifest, null, 2)}\n`,
115
124
  'utf8'
116
125
  );
126
+ await fsp.writeFile(
127
+ path.join(outDir, 'metadata.json'),
128
+ `${JSON.stringify(metadata, null, 2)}\n`,
129
+ 'utf8'
130
+ );
117
131
  await Promise.all(parts.map((part, i) =>
118
132
  fsp.writeFile(
119
133
  path.join(outDir, manifest.parts[i].href),
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,searchContentPage 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 g=document.getElementById("app"),y=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,S=new Map,I=new Map,H=new Set;let M=[],A=0,q=!1,P=(localStorage.getItem(k)||"").trim(),R=!1;function B(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 D(e,t={}){const{scrollToHighlight:a=!1}=t,{targetId:s,parentId:o}=function(e){return m(e,n)}(e);q&&W(),o&&j(o,!0),R=a||Boolean(P),location.hash.replace("#","")===s?O():location.hash=`#${s}`}function F(){return location.hash.replace("#","")||t}async function O(){const e=F(),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){S.forEach(e=>{e.setAttribute("aria-current","false")});const n=S.get(e);if(n&&n.setAttribute("aria-current","page"),t){const e=S.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(g.innerHTML='<article class="section"><p>Section failed to load.</p></article>');const o=await n();g.innerHTML=o.html||"",function(e){if(!e||!e.showDate&&!e.showReadingTime&&!e.showSummary)return;const t=(g.querySelector(".doc-content")||g.querySelector("article, section")||g).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(g),await f(g),function(e){const t=g.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(),D(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(),D(l.id)}),e.appendChild(t),e.innerHTML+='<span class="bottom-nav-chevron">›</span>',c.appendChild(e)}(g.querySelector("section")||g).appendChild(c)}(e.id),g.scrollTop=0,window.scrollTo(0,0),"function"==typeof o.afterRender&&o.afterRender(g),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=R;R=!1,Q(l),requestAnimationFrame(()=>g.focus())}(o)):location.replace(`#${l}`)}function j(e,t){if(!e)return;const n=I.get(e);t?(H.add(e),n&&n.group.classList.add("expanded")):(H.delete(e),n&&n.group.classList.remove("expanded"))}function _(){if(!T||!x)return;q=!0,T.hidden=!1;const e=P;x.value=e,X(e),requestAnimationFrame(()=>{x.focus(),e&&x.select()})}function W(){T&&x&&(q=!1,T.hidden=!0,x.blur())}!function(){y.innerHTML="",S.clear(),I.clear();let t=H.size>0;e.forEach((e,n)=>{if(e.url){const t=B(e,"nav-leaf");return void y.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(),D(e.id,{scrollToHighlight:Boolean(P)})}),s.querySelector(".nav-expand-toggle").addEventListener("click",t=>{t.stopPropagation();const n=!H.has(e.id);j(e.id,n)}),s.addEventListener("click",t=>{if(t.target===s){const t=!H.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=!H.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=B(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=!H.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=B(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=!H.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=B(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=!H.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=B(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",()=>D(e.id,{scrollToHighlight:Boolean(P)})),a.appendChild(t),S.set(e.id,t)}),t.append(n,a),s.appendChild(t),S.set(e.id,n),I.set(e.id,{group:t,button:n,list:a});const o=H.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",()=>D(e.id,{scrollToHighlight:Boolean(P)})),s.appendChild(t),S.set(e.id,t)}),t.append(n,s),a.appendChild(t),S.set(e.id,n),I.set(e.id,{group:t,button:n,list:s});const o=H.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",()=>D(e.id,{scrollToHighlight:Boolean(P)})),a.appendChild(t),S.set(e.id,t)}),t.append(n,a),o.appendChild(t),S.set(e.id,n),I.set(e.id,{group:t,button:n,list:a});const s=H.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",()=>D(e.id,{scrollToHighlight:Boolean(P)})),o.appendChild(t),S.set(e.id,t)}),n.append(s,o),y.appendChild(n),S.set(e.id,s),I.set(e.id,{group:n,button:s,list:o});const i=!e.collapsed&&(H.has(e.id)||!t&&!H.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",()=>D(e.id,{scrollToHighlight:Boolean(P)})),y.appendChild(t),S.set(e.id,t)}})}(),function(){const e=document.getElementById("themePicker"),t=document.getElementById("themeStylesheet");if(!e||!t)return;let n;try{n=JSON.parse(e.dataset.themes||"[]")}catch{return}if(!Array.isArray(n)||0===n.length)return;const a="pagenary:theme",s=e=>{const t=n.find(t=>t.name===e);return t?t.file:null};function o(n){const a=s(n);a&&(t.setAttribute("href",a),e.value=n)}let i=null;try{i=localStorage.getItem(a)}catch{i=null}const l=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches;o(i&&s(i)?i:l&&s("dark")?"dark":e.dataset.default&&s(e.dataset.default)?e.dataset.default:n[0].name),e.addEventListener("change",()=>{o(e.value);try{localStorage.setItem(a,e.value)}catch{}})}(),P&&(R=!0),window.addEventListener("hashchange",()=>{P&&(R=!0),O()}),b.textContent=(new Date).getFullYear(),O(),L&&T&&x&&w&&(L.addEventListener("click",()=>{q?W():_()}),x.addEventListener("input",()=>{const e=x.value;Z(e,!0),X(e)}),x.addEventListener("keydown",e=>{const t=M.length-1;if("ArrowDown"===e.key)e.preventDefault(),A=Math.min(t,A+1),K();else if("ArrowUp"===e.key)e.preventDefault(),A=Math.max(0,A-1),K();else if("Enter"===e.key){e.preventDefault();const t=M[A];t&&(Z(x.value,!0),D(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&&(Z(x.value,!0),D(n,{scrollToHighlight:!0}),W())}),T.addEventListener("click",e=>{e.target===T&&W()}),w.addEventListener("scroll",()=>{J.loading||J.complete||w.scrollTop+w.clientHeight>=w.scrollHeight-48&&G(!1)}),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(),q?W():_()):"Escape"===e.key&&q&&(e.preventDefault(),W())}));let U=null,V=!1;const z=25;let J={query:"",offset:0,total:0,complete:!0,loading:!1};async function X(e){w&&(J={query:e,offset:0,total:0,complete:!1,loading:!1},M=[],!V&&e.trim()&&(V=!0,w.innerHTML='<li class="cmd-item cmd-loading">Indexing content...</li>'),clearTimeout(U),U=setTimeout(async()=>{await G(!0);const e=F();A=d(M,e),K(),V=!1},e.trim()?150:0))}async function G(t=!1){if(J.loading)return;if(!t&&J.complete)return;const n=J.query;let a;J.loading=!0;try{a=await c(e,n,{offset:J.offset,limit:z})}catch{return void(J.loading=!1)}n===J.query?(M=t?a.items:M.concat(a.items),J.offset=M.length,J.total=a.total,J.complete=a.complete||0===a.items.length,J.loading=!1,function(){if(w){if(w.innerHTML="",!M.length){const e=document.createElement("li");return e.className="cmd-item",e.setAttribute("aria-selected","false"),e.textContent="No matches.",void w.appendChild(e)}if(M.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");if(a.className="cmd-item-summary",a.textContent=e.summary||"",t.append(n,a),e.searchSnippet&&e.searchSnippet!==e.summary){const n=document.createElement("span");n.className="cmd-item-snippet",n.textContent=e.searchSnippet,t.appendChild(n)}if("number"==typeof e.searchRank&&e.searchRank>0){const n=document.createElement("span");n.className="cmd-item-score",n.textContent=`Rank ${e.searchRank}`,t.appendChild(n)}w.appendChild(t)}),!J.complete&&J.total>M.length){const e=document.createElement("li");e.className="cmd-item cmd-more",e.setAttribute("aria-selected","false"),e.setAttribute("role","presentation"),e.textContent=`Showing ${M.length} of ${J.total} — scroll for more`,w.appendChild(e)}}}()):J.loading=!1}function K(){w&&Array.from(w.children).forEach((e,t)=>{const n=t===A&&M.length;e.setAttribute("aria-selected",n?"true":"false"),n&&e.scrollIntoView({block:"nearest"})})}function Y(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 Z(e,t=!1){P=e.trim(),t&&(P?localStorage.setItem(k,P):localStorage.removeItem(k)),Q()}function Q(e=!1){g&&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"})})}(g,P,{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=[],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=Y((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"))}),y.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"))}));
1
+ import{MANIFEST as e,DEFAULT_SECTION as t,findSection as n,getAdjacentSections as a,SITE_CONFIG as o,EXPORT_CONFIG as s}from"./manifest.js";import{updateMetaTags as i}from"./seo.js";import{escapeRegExp as c,searchContentPage as r,flattenManifest as l,findPreferredIndex as d,resolveSectionMetadata as m}from"./lib/search.js";import{resolveTarget as p,resolveEntry as u}from"./lib/router.js";import{composeExportDocument as v,collectExportableSections as h}from"./lib/export.js";import{renderMermaidBlocks as f}from"./mermaid-init.js";import{highlightCodeBlocks as g}from"./syntax-highlight.js";const y=document.getElementById("app"),b=document.getElementById("nav"),E=document.getElementById("year"),x=document.getElementById("exportBtn"),L=document.getElementById("commandToggle"),T=document.getElementById("commandPalette"),C=document.getElementById("commandInput"),N=document.getElementById("commandList"),k=document.getElementById("mobileMenuToggle"),w=document.querySelector(".sidebar"),$="docs-toolkit-command-query",S=new Map,A=new Map,I=new Map,M=new Set;let H=[],_=0,P=!1,q=(localStorage.getItem($)||"").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:o,parentId:s}=function(e){return p(e,n)}(e);P&&X(),s&&O(s,!0),B=a||Boolean(q),location.hash.replace("#","")===o?j():location.hash=`#${o}`}function D(){return location.hash.replace("#","")||t}async function j(){const t=D(),s=u(t,n);if(!s)return;const{entry:c,targetId:r,parentId:l}=s;r===t?(l&&O(l,!0),function(e,t=null){A.forEach(e=>{e.setAttribute("aria-current","false")});const n=A.get(e);if(n&&n.setAttribute("aria-current","page"),t){const e=A.get(t);e&&e.setAttribute("aria-current","page")}}(c.id,l),await async function(t){if(!t)return;const n=await import(t.module),s=n.load||n.default;if("function"!=typeof s)return void(y.innerHTML='<article class="section"><p>Section failed to load.</p></article>');const c=await s();y.innerHTML=c.html||"",function(t){if(!t)return;const n=(y.querySelector(".doc-content")||y.querySelector("article, section")||y).querySelector("h1");if(!n)return;const a=[];t.showDate&&t.date&&a.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)}(t.date)),t.showReadingTime&&t.reading_time&&a.push(`${t.reading_time} min read`);let o=n;if(a.length>0){const e=document.createElement("p");e.className="doc-meta",e.textContent=a.join(" · "),n.after(e),o=e}if(t.showSummary&&t.summary){const e=document.createElement("p");e.className="doc-summary",e.textContent=t.summary,o.after(e),o=e}!async function(t,n){const a=t.id,o=n.closest(".doc-content")||n.parentElement;let s=null;try{s=await m(e,a)}catch{return}if(D()!==a||!function(e){return!!e&&Boolean(e.concepts&&e.concepts.length||e.skos_concepts&&e.skos_concepts.length||e.relationships&&e.relationships.length||e.provenance&&e.provenance.length||e.provenance_events&&e.provenance_events.length||e.source||e.privacy)}(s))return;const i=document.createElement("div");i.className="doc-fortemi-tools";const c=`docFortemiPanel-${a}`,r=document.createElement("div");r.id=c,r.className="doc-fortemi-panel",r.hidden=!0,r.setAttribute("role","region"),r.setAttribute("aria-label","Page metadata"),function(e,t){e.textContent="",z(e,"Concepts",e=>{const n=t.skos_concepts&&t.skos_concepts.length?t.skos_concepts:t.concepts||[];n.length?function(e,t){const n=document.createElement("div");n.className="doc-fortemi-chips",t.forEach(e=>{const t=document.createElement("span");t.className="doc-fortemi-chip",t.textContent=U(e),"object"==typeof e&&e&&e.definition&&(t.title=e.definition),n.appendChild(t)}),e.appendChild(n)}(e,n):V(e,"concepts","none"),(t.skos_relations||[]).forEach(t=>{const n=U({id:t.source_id||t.source}),a=U({id:t.target_id||t.target});V(e,t.type||"related",`${W(n)} -> ${W(a)}`)})}),z(e,"Source and Provenance",e=>{V(e,"source",t.source?.repo_relative_path||t.source?.path),V(e,"locator",t.source?.locator),V(e,"updated",t.updated_at),V(e,"privacy",t.privacy?.classification),(t.provenance||[]).forEach(t=>{V(e,t.field||"field",`${t.source||"source"} (${t.confidence||"unknown"})`)}),(t.provenance_events||[]).forEach(t=>{const n=[t.agent||"unknown agent",t.source,t.started_at||t.ended_at].filter(Boolean).join(" · ");V(e,t.activity||"activity",n)})}),z(e,"Related Pages",e=>{const n=t.relationships||[];if(!n.length)return void V(e,"related","none");const a=document.createElement("ul");a.className="doc-fortemi-links",n.forEach(e=>{const t=document.createElement("li"),n=document.createElement("button");n.type="button",n.className="doc-fortemi-link";const o=function(e){return String(e?.target_id||e?.target||"").replace(/^docs:page:/,"")}(e),s=function(e){const t=Number(e);return Number.isFinite(t)?`${Math.round(100*Math.max(0,Math.min(1,t)))}%`:null}(e.confidence),i=e.label||e.type||"related",c=Array.isArray(e.metadata?.shared_concepts)?e.metadata.shared_concepts.map(W).join(", "):null;n.textContent=`${i}: ${W(o)}${s?` (${s})`:""}`,c&&(n.title=`Shared concepts: ${c}`),n.addEventListener("click",()=>{o&&F(o)}),t.appendChild(n),a.appendChild(t)}),e.appendChild(a)})}(r,s);const l=document.createElement("button");l.type="button",l.className="doc-fortemi-button",l.setAttribute("aria-expanded","false"),l.setAttribute("aria-controls",c),l.title="Show page metadata",l.innerHTML='<span aria-hidden="true">i</span><span class="sr-only">Show page metadata</span>',l.addEventListener("click",()=>{const e=r.hidden;r.hidden=!e,l.setAttribute("aria-expanded",String(e))}),i.appendChild(l),o.append(i,r)}(t,o)}(t),await f(y),await g(y),function(e){const t=y.querySelector(".bottom-nav");if(t&&t.remove(),"never"===o.bottomNav)return;const n=(o.bottomNavSections||[]).some(t=>e.startsWith(t)),s="mobile"===o.bottomNav&&!n,{prev:i,next:c}=a(e);if(!i&&!c)return;const r=document.createElement("nav");if(r.className="bottom-nav",s&&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(),F(i.id)}),e.appendChild(t),r.appendChild(e)}else{const e=document.createElement("div");e.className="bottom-nav-spacer",r.appendChild(e)}if(c){const e=document.createElement("div");e.className="bottom-nav-item bottom-nav-next";const t=document.createElement("a");t.href=`#${c.id}`,t.className="bottom-nav-link",t.title=`Next: ${c.title}`,t.textContent=c.title,t.addEventListener("click",e=>{e.preventDefault(),F(c.id)}),e.appendChild(t),e.innerHTML+='<span class="bottom-nav-chevron">›</span>',r.appendChild(e)}(y.querySelector("section")||y).appendChild(r)}(t.id),y.scrollTop=0,window.scrollTo(0,0),"function"==typeof c.afterRender&&c.afterRender(y),i({title:t.title,description:t.summary,siteTitle:o.siteTitle,siteUrl:o.siteUrl,sectionId:t.id,ogImage:t.ogImage||o.ogImage}),S.set(t.id,Date.now());const r=B;B=!1,oe(r),requestAnimationFrame(()=>y.focus())}(c)):location.replace(`#${r}`)}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 W(e){return String(e||"").replace(/[-_]+/g," ").replace(/\s+/g," ").trim()}function U(e){return"string"==typeof e?W(e):e.prefLabel||e.pref_label||e.label||e.id||"concept"}function V(e,t,n){if(null==n||""===n)return;const a=document.createElement("div");a.className="doc-fortemi-row";const o=document.createElement("span");o.className="doc-fortemi-key",o.textContent=t;const s=document.createElement("span");s.textContent=String(n),a.append(o,s),e.appendChild(a)}function z(e,t,n){const a=document.createElement("section");a.className="doc-fortemi-section";const o=document.createElement("h2");o.textContent=t,a.appendChild(o),n(a),e.appendChild(a)}function J(){if(!T||!C)return;P=!0,T.hidden=!1;const e=q;C.value=e,Q(e),requestAnimationFrame(()=>{C.focus(),e&&C.select()})}function X(){T&&C&&(P=!1,T.hidden=!0,C.blur())}!function(){b.innerHTML="",A.clear(),I.clear();let t=M.size>0;e.forEach((e,n)=>{if(e.url){const t=R(e,"nav-leaf");return void b.appendChild(t)}if(e.subsections&&e.subsections.length){const n=document.createElement("div");n.className="nav-group";const a=Boolean(e.module),o=document.createElement("button");o.type="button",o.className="nav-parent"+(a?" nav-parent-with-content":""),o.dataset.section=e.id,o.title=e.summary,a?(o.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 `,o.querySelector(".nav-title-link").addEventListener("click",t=>{t.stopPropagation(),F(e.id,{scrollToHighlight:Boolean(q)})}),o.querySelector(".nav-expand-toggle").addEventListener("click",t=>{t.stopPropagation();const n=!M.has(e.id);O(e.id,n)}),o.addEventListener("click",t=>{if(t.target===o){const t=!M.has(e.id);O(e.id,t)}})):(o.innerHTML=`\n <span class="nav-title">${e.title}</span>\n ${e.summary?`<span class="nav-summary">${e.summary}</span>`:""}\n `,o.addEventListener("click",()=>{const t=!M.has(e.id);O(e.id,t)}));const s=document.createElement("div");s.className="nav-sublist",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-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=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);O(e.id,t)});const o=document.createElement("div");o.className="nav-sublist nav-sublist-deep",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-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=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),A.set(e.id,t)}),t.append(n,a),o.appendChild(t),A.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 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)})),o.appendChild(t),A.set(e.id,t)}),t.append(n,o),a.appendChild(t),A.set(e.id,n),I.set(e.id,{group:t,button:n,list:o});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 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),A.set(e.id,t)}),t.append(n,a),s.appendChild(t),A.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"+(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)})),s.appendChild(t),A.set(e.id,t)}),n.append(o,s),b.appendChild(n),A.set(e.id,o),I.set(e.id,{group:n,button:o,list:s});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",()=>F(e.id,{scrollToHighlight:Boolean(q)})),b.appendChild(t),A.set(e.id,t)}})}(),function(){const e=document.getElementById("themePicker"),t=document.getElementById("themeStylesheet");if(!e||!t)return;let n;try{n=JSON.parse(e.dataset.themes||"[]")}catch{return}if(!Array.isArray(n)||0===n.length)return;const a="pagenary:theme",o=e=>{const t=n.find(t=>t.name===e);return t?t.file:null};function s(n){const a=o(n);a&&(t.setAttribute("href",a),e.value=n)}let i=null;try{i=localStorage.getItem(a)}catch{i=null}const c=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches;s(i&&o(i)?i:c&&o("dark")?"dark":e.dataset.default&&o(e.dataset.default)?e.dataset.default:n[0].name),e.addEventListener("change",()=>{s(e.value);try{localStorage.setItem(a,e.value)}catch{}})}(),q&&(B=!0),window.addEventListener("hashchange",()=>{q&&(B=!0),j()}),E.textContent=(new Date).getFullYear(),j(),L&&T&&C&&N&&(L.addEventListener("click",()=>{P?X():J()}),C.addEventListener("input",()=>{const e=C.value;ae(e,!0),Q(e)}),C.addEventListener("keydown",e=>{const t=H.length-1;if("ArrowDown"===e.key)e.preventDefault(),_=Math.min(t,_+1),te();else if("ArrowUp"===e.key)e.preventDefault(),_=Math.max(0,_-1),te();else if("Enter"===e.key){e.preventDefault();const t=H[_];t&&(ae(C.value,!0),F(t.id,{scrollToHighlight:!0}),X())}else"Escape"===e.key&&(e.preventDefault(),X())}),N.addEventListener("click",e=>{const t=e.target.closest("[data-section]");if(!t)return;const n=t.dataset.section;n&&(ae(C.value,!0),F(n,{scrollToHighlight:!0}),X())}),T.addEventListener("click",e=>{e.target===T&&X()}),N.addEventListener("scroll",()=>{Z.loading||Z.complete||N.scrollTop+N.clientHeight>=N.scrollHeight-48&&ee(!1)}),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?X():J()):"Escape"===e.key&&P&&(e.preventDefault(),X())}));let G=null,K=!1;const Y=25;let Z={query:"",offset:0,total:0,complete:!0,loading:!1};async function Q(e){N&&(Z={query:e,offset:0,total:0,complete:!1,loading:!1},H=[],!K&&e.trim()&&(K=!0,N.innerHTML='<li class="cmd-item cmd-loading">Indexing content...</li>'),clearTimeout(G),G=setTimeout(async()=>{await ee(!0);const e=D();_=d(H,e),te(),K=!1},e.trim()?150:0))}async function ee(t=!1){if(Z.loading)return;if(!t&&Z.complete)return;const n=Z.query;let a;Z.loading=!0;try{a=await r(e,n,{offset:Z.offset,limit:Y})}catch{return void(Z.loading=!1)}n===Z.query?(H=t?a.items:H.concat(a.items),Z.offset=H.length,Z.total=a.total,Z.complete=a.complete||0===a.items.length,Z.loading=!1,function(){if(N){if(N.innerHTML="",!H.length){const e=document.createElement("li");return e.className="cmd-item",e.setAttribute("aria-selected","false"),e.textContent="No matches.",void N.appendChild(e)}if(H.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");if(a.className="cmd-item-summary",a.textContent=e.summary||"",t.append(n,a),e.searchSnippet&&e.searchSnippet!==e.summary){const n=document.createElement("span");n.className="cmd-item-snippet",n.textContent=e.searchSnippet,t.appendChild(n)}if("number"==typeof e.searchRank&&e.searchRank>0){const n=document.createElement("span");n.className="cmd-item-score",n.textContent=`Rank ${e.searchRank}`,t.appendChild(n)}N.appendChild(t)}),!Z.complete&&Z.total>H.length){const e=document.createElement("li");e.className="cmd-item cmd-more",e.setAttribute("aria-selected","false"),e.setAttribute("role","presentation"),e.textContent=`Showing ${H.length} of ${Z.total} — scroll for more`,N.appendChild(e)}}}()):Z.loading=!1}function te(){N&&Array.from(N.children).forEach((e,t)=>{const n=t===_&&H.length;e.setAttribute("aria-selected",n?"true":"false"),n&&e.scrollIntoView({block:"nearest"})})}function ne(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 ae(e,t=!1){q=e.trim(),t&&(q?localStorage.setItem($,q):localStorage.removeItem($)),oe()}function oe(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 o=a.map(e=>e.toLowerCase()),s=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&&s.has(t.tagName)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}}),r=[];let l;for(;l=i.nextNode();){const e=l.nodeValue.toLowerCase();o.some(t=>e.includes(t))&&r.push(l)}const d=new RegExp(`(${a.map(c).join("|")})`,"gi");r.forEach(e=>{const t=e.nodeValue,n=[];let a=0;t.replace(d,(e,o,s)=>{s>a&&n.push(document.createTextNode(t.slice(a,s)));const i=document.createElement("mark");return i.className="hl",i.textContent=e,n.push(i),a=s+e.length,e}),a<t.length&&n.push(document.createTextNode(t.slice(a)));const o=document.createDocumentFragment();n.forEach(e=>o.appendChild(e)),e.parentNode.replaceChild(o,e)}),n&&requestAnimationFrame(()=>{const t=e.querySelector("mark.hl");t&&t.scrollIntoView({behavior:"smooth",block:"center"})})}(y,q,{scrollToFirst:e})}x&&x.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(!x)return;const n=x.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(),x.disabled=!0;const o=document.createElement("div");o.className="export-loading-overlay",o.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(o),setTimeout(()=>o.classList.add("active"),10);const i=o.querySelector(".export-loading-fill"),c=o.querySelector(".export-loading-status");try{let n;if("page"===t){const t=D(),a=h(e).find(e=>e.id===t);n=a?[a]:[]}else n=h(e);if(0===n.length)return alert("No content available to export."),o.remove(),void(x.disabled=!1);const a=[],r=n.length;let l=0;for(const e of n){l++;const n=l/r*100;i.style.width=`${n}%`,c.textContent="page"===t?`Exporting: ${e.title}`:`Processing section ${l} 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 o=ne((await n()).html||"");a.push({section:e,html:o})}catch(t){console.error("Failed to include section in export",e.id,t)}}c.textContent="Generating document...",await new Promise(e=>setTimeout(e,200));const d=v(a,s);c.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 o.remove();m.document.open(),m.document.write(d),m.document.close(),m.focus(),o.classList.remove("active"),setTimeout(()=>o.remove(),300)}catch(e){console.error("Export failed",e),alert("Export failed. Check console for details."),o.remove()}finally{x.disabled=!1,x.innerHTML=n}}(a)})})}),k&&w&&(k.addEventListener("click",()=>{w.classList.contains("mobile-open")?(w.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),k.setAttribute("aria-expanded","false")):(w.classList.add("mobile-open"),document.body.classList.add("menu-open"),k.setAttribute("aria-expanded","true"))}),b.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"))&&(w.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),k.setAttribute("aria-expanded","false"))}}),document.addEventListener("click",e=>{window.innerWidth<=960&&w.classList.contains("mobile-open")&&!w.contains(e.target)&&!k.contains(e.target)&&(w.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),k.setAttribute("aria-expanded","false"))}));
package/site/index.html CHANGED
@@ -21,7 +21,7 @@
21
21
  </script>
22
22
  <link rel="icon" type="image/png" href="./favicon.png" />
23
23
  <link rel="stylesheet" href="./styles.css" />
24
- <meta name="x-build" content="2026-06-18T17:44:12.862Z" />
24
+ <meta name="x-build" content="2026-06-21T02:21:08.456Z" />
25
25
  </head>
26
26
  <body>
27
27
  <a class="skip-link" href="#app">Skip to content</a>
@@ -1 +1 @@
1
- import{buildCommunityGraph as t,flattenManifest as e}from"./search.js";import{MANIFEST as n}from"../manifest.js";const o="http://www.w3.org/2000/svg",s=1e3;function i(t){return String(t).replace(/^docs:page:/,"")}function a(t){return[t.source??t.from??(Array.isArray(t)?t[0]:null),t.target??t.to??(Array.isArray(t)?t[1]:null)]}export function renderDocsMap(t,e,n={}){if(!t)return;t.textContent="";const r=e&&e.nodes||[];if(r.length<2){const e=document.createElement("p");return e.className="docs-map-empty",e.textContent="Not enough pages to map yet — add a few more sections to see how they relate.",void t.appendChild(e)}const c=n.labelFor||(t=>i(t)),d=n.onNavigate||(()=>{}),{positions:l,anchors:u,communityCount:m}=function(t){const e=new Map,n=t.communities&&t.communities.length?t.communities:[{id:"all",nodes:t.nodes.map(t=>t.id)}],o=.34*Math.min(s,700),i=.12*Math.min(s,700),a=[];return n.forEach((t,s)=>{const r=1===n.length?{x:500,y:350}:{x:500+o*Math.cos(2*Math.PI*s/n.length-Math.PI/2),y:350+o*Math.sin(2*Math.PI*s/n.length-Math.PI/2)};a.push({id:t.id,x:r.x,y:r.y,index:s});const c=t.nodes||[];c.forEach((t,n)=>{if(1===c.length)e.set(t,{x:r.x,y:r.y,community:s});else{const o=2*Math.PI*n/c.length;e.set(t,{x:r.x+i*Math.cos(o),y:r.y+i*Math.sin(o),community:s})}})}),t.nodes.forEach((t,n)=>{e.has(t.id)||e.set(t.id,{x:500+n%5*12-24,y:350,community:-1})}),{positions:e,anchors:a,communityCount:n.length}}(e),p=t=>t<0?0:Math.round(360*t/Math.max(1,m)),h=document.createElementNS(o,"svg");h.setAttribute("class","docs-map-svg"),h.setAttribute("viewBox","0 0 1000 700"),h.setAttribute("role","img"),h.setAttribute("aria-label",`Relationship map of ${r.length} documentation pages`);for(const t of e.edges||[]){const[e,n]=a(t),s=l.get(e),i=l.get(n);if(!s||!i)continue;const r=document.createElementNS(o,"line");r.setAttribute("x1",s.x),r.setAttribute("y1",s.y),r.setAttribute("x2",i.x),r.setAttribute("y2",i.y),r.setAttribute("class","docs-map-edge"),h.appendChild(r)}u.forEach(t=>{if("all"===t.id)return;const e=document.createElementNS(o,"text");e.setAttribute("x",t.x),e.setAttribute("y",t.y-.12*Math.min(s,700)-10),e.setAttribute("text-anchor","middle"),e.setAttribute("class","docs-map-community"),e.setAttribute("fill",`hsl(${p(t.index)} 60% 55%)`),e.textContent=String(t.id).replace(/^[a-z]+:/,"").replace(/[-_]/g," "),h.appendChild(e)});for(const t of r){const e=l.get(t.id);if(!e)continue;const n=i(t.id),s=c(t.id),a=document.createElementNS(o,"g");a.setAttribute("class","docs-map-node"),a.setAttribute("tabindex","0"),a.setAttribute("role","link"),a.setAttribute("aria-label",s);const r=document.createElementNS(o,"circle");r.setAttribute("cx",e.x),r.setAttribute("cy",e.y),r.setAttribute("r",8),r.setAttribute("fill",`hsl(${p(e.community)} 60% 55%)`),a.appendChild(r);const u=document.createElementNS(o,"text");u.setAttribute("x",e.x+12),u.setAttribute("y",e.y+4),u.setAttribute("class","docs-map-label"),u.textContent=s,a.appendChild(u);const m=()=>d(n);a.addEventListener("click",m),a.addEventListener("keydown",t=>{"Enter"!==t.key&&" "!==t.key||(t.preventDefault(),m())}),h.appendChild(a)}t.appendChild(h)}const r=['<section class="section doc">',' <div class="doc-content">'," <header>",' <p class="eyebrow">Overview</p>'," <h1>Docs Map</h1>",' <p class="lead">How these pages cluster and relate. Click a node to jump to it.</p>'," </header>",' <div id="docsMapRoot" class="docs-map"></div>'," </div>","</section>"].join("\n");export function loadDocsMap(t,e){const n=e instanceof Map?e:new Map(Array.isArray(e)?e:Object.entries(e||{}));return{html:r,afterRender(e){const o=e.querySelector("#docsMapRoot");o&&renderDocsMap(o,t||{nodes:[],edges:[],communities:[]},{labelFor:t=>n.get(i(t))||i(t),onNavigate:t=>{location.hash=`#${t}`}})}}}export async function load(){return{html:r,afterRender(o){const s=o.querySelector("#docsMapRoot");if(!s)return;let a;try{a=t(n)}catch{a={nodes:[],edges:[],communities:[]}}const r=function(t){const n=new Map;for(const o of e(t))o&&o.id&&n.set(o.id,o.title||o.id);return n}(n);renderDocsMap(s,a,{labelFor:t=>r.get(i(t))||i(t),onNavigate:t=>{location.hash=`#${t}`}})}}}
1
+ import{buildCommunityGraph as e,flattenManifest as t}from"./search.js";import{MANIFEST as n}from"../manifest.js";const a="http://www.w3.org/2000/svg",s=1e3;function o(e){return String(e).replace(/^docs:page:/,"")}function r(e){return[e.source??e.from??(Array.isArray(e)?e[0]:null),e.target??e.to??(Array.isArray(e)?e[1]:null)]}function c(e){return e instanceof Map?e:new Map(Array.isArray(e)?e:Object.entries(e||{}))}function i(e){return String(e||"").replace(/^concept:/,"").replace(/^docs:page:/,"").replace(/[-_]+/g," ").replace(/\s+/g," ").trim()}function d(e,t,n){return`${e}\0${t}\0${n||"related"}`}function l(e){const t=Number(e);return Number.isFinite(t)?`${Math.round(100*Math.max(0,Math.min(1,t)))}%`:null}function p(e){return e.filter(Boolean).join("\n")}function u(e,t,n){const a=document.createElement("button");return a.type="button",a.className="docs-map-control",a.textContent=e,a.title=t,a.setAttribute("aria-label",t),a.addEventListener("click",n),a}export function renderDocsMap(e,t,n={}){if(!e)return;e.textContent="";const m=t&&t.nodes||[];if(m.length<2){const t=document.createElement("p");return t.className="docs-map-empty",t.textContent="Not enough pages to map yet — add a few more sections to see how they relate.",void e.appendChild(t)}const h=n.labelFor||(e=>o(e)),g=n.onNavigate||(()=>{}),f=c(n.metadata?.nodes),y=c(n.metadata?.edges),{positions:b,anchors:x,communityCount:v}=function(e){const t=new Map,n=e.communities&&e.communities.length?e.communities:[{id:"all",nodes:e.nodes.map(e=>e.id)}],a=.34*Math.min(s,700),o=.12*Math.min(s,700),r=[];return n.forEach((e,s)=>{const c=1===n.length?{x:500,y:350}:{x:500+a*Math.cos(2*Math.PI*s/n.length-Math.PI/2),y:350+a*Math.sin(2*Math.PI*s/n.length-Math.PI/2)};r.push({id:e.id,x:c.x,y:c.y,index:s});const i=e.nodes||[];i.forEach((e,n)=>{if(1===i.length)t.set(e,{x:c.x,y:c.y,community:s});else{const a=2*Math.PI*n/i.length;t.set(e,{x:c.x+o*Math.cos(a),y:c.y+o*Math.sin(a),community:s})}})}),e.nodes.forEach((e,n)=>{t.has(e.id)||t.set(e.id,{x:500+n%5*12-24,y:350,community:-1})}),{positions:t,anchors:r,communityCount:n.length}}(t),A=e=>e<0?0:Math.round(360*e/Math.max(1,v)),M=new Map;for(const e of t.edges||[]){const[t,n]=r(e);t&&n&&(M.has(t)||M.set(t,new Set),M.has(n)||M.set(n,new Set),M.get(t).add(n),M.get(n).add(t))}let E=1;const C={x:0,y:0};let w=!1,N=null;const S=document.createElement("div");S.className="docs-map-controls";const L=document.createElementNS(a,"svg");L.setAttribute("class","docs-map-svg"),L.setAttribute("viewBox","0 0 1000 700"),L.setAttribute("role","img"),L.setAttribute("aria-label",`Relationship map of ${m.length} documentation pages`);const k=document.createElementNS(a,"g");k.setAttribute("class","docs-map-viewport"),L.appendChild(k);const $=()=>{k.setAttribute("transform",`translate(${C.x} ${C.y}) scale(${E})`)},D=e=>{E=Math.max(.55,Math.min(2.8,e)),$()},I=(e,t)=>{C.x+=e,C.y+=t,$()};S.append(u("+ Zoom","Zoom in",()=>D(1.2*E)),u("- Zoom","Zoom out",()=>D(E/1.2)),u("←","Pan left",()=>I(48,0)),u("↑","Pan up",()=>I(0,48)),u("↓","Pan down",()=>I(0,-48)),u("→","Pan right",()=>I(-48,0)),u("Reset","Reset view",()=>{E=1,C.x=0,C.y=0,$()}));const P=function(e){const t=document.createElement("aside");t.className="docs-map-popup",t.hidden=!0,t.dataset.pinned="false",t.setAttribute("aria-live","polite");const n=document.createElement("button");return n.type="button",n.className="docs-map-popup-close",n.textContent="x",n.setAttribute("aria-label","Close graph details"),n.addEventListener("click",()=>{t.hidden=!0,t.dataset.pinned="false"}),t.appendChild(n),t.renderDetails=(n,a=!1)=>{t.dataset.pinned=String(a),t.hidden=!1,t.querySelectorAll(":scope > :not(.docs-map-popup-close)").forEach(e=>e.remove());const s=document.createElement("h2");if(s.textContent=n.title,t.appendChild(s),n.source){const e=document.createElement("p");e.className="docs-map-popup-source",e.textContent=n.source,t.appendChild(e)}if(n.concepts.length){const e=document.createElement("div");e.className="docs-map-popup-chips",n.concepts.slice(0,8).forEach(t=>{const n=document.createElement("span");n.textContent=t,e.appendChild(n)}),t.appendChild(e)}const o=document.createElement("button");o.type="button",o.className="docs-map-popup-open",o.textContent="Open page",o.addEventListener("click",()=>e(n.sectionId)),t.appendChild(o)},t}(g);L.addEventListener("wheel",e=>{e.preventDefault(),D(E*(e.deltaY<0?1.12:.88))},{passive:!1}),L.addEventListener("pointerdown",e=>{(function(e){let t=e;for(;t&&1===t.nodeType;){if(t.classList&&t.classList.contains("docs-map-node"))return t;t=t.parentNode}return null})(e.target)||(w=!0,N={x:e.clientX,y:e.clientY,panX:C.x,panY:C.y},L.setPointerCapture(e.pointerId))}),L.addEventListener("pointermove",e=>{w&&N&&(C.x=N.panX+(e.clientX-N.x),C.y=N.panY+(e.clientY-N.y),$())}),L.addEventListener("pointerup",e=>{w=!1,N=null,L.hasPointerCapture(e.pointerId)&&L.releasePointerCapture(e.pointerId)}),L.addEventListener("pointercancel",()=>{w=!1,N=null});for(const e of t.edges||[]){const[t,n]=r(e),s=b.get(t),o=b.get(n);if(!s||!o)continue;const c=y.get(d(t,n,e.kind)),u=l(c?.confidence),m=Array.isArray(c?.shared_concepts)&&c.shared_concepts.length?c.shared_concepts.map(i).join(", "):null,h=document.createElementNS(a,"line");h.setAttribute("x1",s.x),h.setAttribute("y1",s.y),h.setAttribute("x2",o.x),h.setAttribute("y2",o.y),h.setAttribute("class","docs-map-edge"),h.setAttribute("data-kind",e.kind||"related"),h.dataset.source=t,h.dataset.target=n,h.setAttribute("stroke-width",String(Math.max(1,Math.min(5,1+.6*Number(e.weight||1))))),h.setAttribute("opacity",String(Math.max(.28,Math.min(.82,.34+.08*Number(e.weight||1)))));const g=document.createElementNS(a,"title");g.textContent=p([c?.label||e.kind||"related",u?`Confidence: ${u}`:null,m?`Shared concepts: ${m}`:null]),h.appendChild(g),k.appendChild(h)}x.forEach(e=>{if("all"===e.id)return;const t=document.createElementNS(a,"text");t.setAttribute("x",e.x),t.setAttribute("y",e.y-.12*Math.min(s,700)-10),t.setAttribute("text-anchor","middle"),t.setAttribute("class","docs-map-community"),t.setAttribute("fill",`hsl(${A(e.index)} 60% 55%)`),t.textContent=String(e.id).replace(/^[a-z]+:/,"").replace(/[-_]/g," "),k.appendChild(t)});for(const e of m){const t=b.get(e.id);if(!t)continue;const n=o(e.id),s=h(e.id),r=f.get(e.id),c=Array.isArray(r?.skos_concepts)&&r.skos_concepts.length?r.skos_concepts.map(e=>e.prefLabel||e.id):(r?.concepts||[]).map(i),d={sectionId:n,title:s,concepts:c,source:r?.source?.repo_relative_path||r?.source?.path||""},l=document.createElementNS(a,"g");l.setAttribute("class","docs-map-node"),l.dataset.nodeId=e.id,l.setAttribute("tabindex","0"),l.setAttribute("role","button"),l.setAttribute("aria-label",s);const u=document.createElementNS(a,"title");u.textContent=p([s,c.length?`Concepts: ${c.slice(0,8).join(", ")}`:null,r?.source?.repo_relative_path||r?.source?.path]),l.appendChild(u);const m=document.createElementNS(a,"circle");m.setAttribute("cx",t.x),m.setAttribute("cy",t.y),m.setAttribute("r",8),m.setAttribute("fill",`hsl(${A(t.community)} 60% 55%)`),l.appendChild(m);const g=document.createElementNS(a,"text");g.setAttribute("x",t.x+12),g.setAttribute("y",t.y+4),g.setAttribute("class","docs-map-label"),g.textContent=s,l.appendChild(g);const y=()=>{const t=M.get(e.id)||new Set;k.querySelectorAll(".docs-map-node").forEach(n=>{const a=n.dataset.nodeId;n.classList.toggle("is-active",a===e.id),n.classList.toggle("is-neighbor",t.has(a)),n.classList.toggle("is-dimmed",a!==e.id&&!t.has(a))}),k.querySelectorAll(".docs-map-edge").forEach(t=>{const n=t.dataset.source===e.id||t.dataset.target===e.id;t.classList.toggle("is-active",n),t.classList.toggle("is-dimmed",!n)})},x=()=>{k.querySelectorAll(".is-active, .is-neighbor, .is-dimmed").forEach(e=>{e.classList.remove("is-active","is-neighbor","is-dimmed")})};l.addEventListener("mouseenter",()=>{y(),"true"!==P.dataset.pinned&&P.renderDetails(d,!1)}),l.addEventListener("mouseleave",()=>{"true"!==P.dataset.pinned&&x(),"true"!==P.dataset.pinned&&(P.hidden=!0)}),l.addEventListener("click",e=>{e.preventDefault(),y(),P.renderDetails(d,!0)}),l.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),P.renderDetails(d,!0))}),k.appendChild(l)}$(),e.append(S,L,P)}const m=['<section class="section doc">',' <div class="doc-content">'," <header>",' <p class="eyebrow">Overview</p>'," <h1>Docs Map</h1>",' <p class="lead">How these pages cluster and relate.</p>'," </header>",' <div id="docsMapRoot" class="docs-map"></div>'," </div>","</section>"].join("\n");function h(e,t,n={}){const a=(s=n.renderer,"cytoscape"===String(s||"svg").trim().toLowerCase()?"cytoscape":"svg");var s;"svg"!==a?(e.dataset.docsMapRenderer=a,e.dataset.docsMapFallback="svg"):(e.dataset.docsMapRenderer="svg",delete e.dataset.docsMapFallback),renderDocsMap(e,t,n)}export function loadDocsMap(e,t,n={}){const a=t instanceof Map?t:new Map(Array.isArray(t)?t:Object.entries(t||{}));return{html:m,afterRender(t){const s=t.querySelector("#docsMapRoot");s&&h(s,e||{nodes:[],edges:[],communities:[]},{renderer:n.renderer,metadata:n.metadata,labelFor:e=>a.get(o(e))||o(e),onNavigate:e=>{location.hash=`#${e}`}})}}}export async function loadManifestDocsMap(a={}){return{html:m,afterRender(s){const r=s.querySelector("#docsMapRoot");if(!r)return;let c;try{c=e(n)}catch{c={nodes:[],edges:[],communities:[]}}const i=function(e){const n=new Map;for(const a of t(e))a&&a.id&&n.set(a.id,a.title||a.id);return n}(n);h(r,c,{renderer:a.renderer,labelFor:e=>i.get(o(e))||o(e),onNavigate:e=>{location.hash=`#${e}`}})}}}export async function load(){return loadManifestDocsMap()}
@@ -1 +1 @@
1
- export const FORTEMI_INDEX_SCHEMA="aiwg.fortemi.index.export.v1";export const FORTEMI_RECORD_SCHEMA="aiwg.fortemi.index.record.v1";export const FORTEMI_CHUNK_MANIFEST_SCHEMA="aiwg.fortemi.index.chunk-manifest.v1";export const FORTEMI_CHUNK_PART_SCHEMA="aiwg.fortemi.index.chunk.v1";export const DEFAULT_PART_SIZE=100;export function stableHash(e){let t=0xcbf29ce484222325n;for(let o=0;o<e.length;o+=1)t^=BigInt(255&e.charCodeAt(o)),t=1099511628211n*t&18446744073709551615n;return t.toString(16).padStart(16,"0")}export function stripHtml(e){return e?String(e).replace(/<(script|style)[^>]*>[\s\S]*?<\/\1>/gi," ").replace(/<[^>]+>/g," ").replace(/&nbsp;/gi," ").replace(/&amp;/gi,"&").replace(/&lt;/gi,"<").replace(/&gt;/gi,">").replace(/&quot;/gi,'"').replace(/&#39;/gi,"'").replace(/&mdash;/gi,"—").replace(/&hellip;/gi,"…").replace(/\s+/g," ").trim():""}export function normalizeFacetValue(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"general"}const e=new Set(["the","and","for","are","but","not","you","all","any","can","her","was","one","our","out","has","have","his","how","its","may","new","now","old","see","two","who","did","get","use","used","uses","using","with","this","that","they","them","then","than","from","into","your","yours","each","once","more","most","some","such","only","over","under","when","what","which","while","where","will","would","should","could","also","about","after","before","these","those","their","there","here","been","being","were","because","between","both","same","every","either","back","down","make","made","need","needs","keep","read","reads","call","calls","set","sets","follow","follows","three","plus","step","steps","shape","shapes","related","example","examples","docs","page","pages","http","https","www","com","json","returns","return","inspect","describe","described"]);export function extractConcepts(t,o={}){const r=Number.isFinite(o.max)?o.max:6,a=Number.isFinite(o.minLength)?o.minLength:4;if(!t)return[];const n=new Map,i=String(t).toLowerCase().match(/[a-z][a-z0-9-]+/g)||[];for(const t of i){const o=t.replace(/^-+|-+$/g,"");o.length<a||e.has(o)||n.set(o,(n.get(o)||0)+1)}const s=Array.from(n.entries()).sort((e,t)=>t[1]-e[1]||e[0].localeCompare(t[0])).slice(0,Math.max(0,r)).map(([e])=>normalizeFacetValue(e)).filter(Boolean);return Array.from(new Set(s)).sort()}export function addConceptRelationships(e,t,o={}){const r=Number.isFinite(o.maxRelations)?o.maxRelations:4,a=Number.isFinite(o.minShared)?o.minShared:1,n=t.map(e=>new Set(e||[]));for(let t=0;t<e.length;t+=1){const o=n[t];if(!o||0===o.size)continue;const i=[];for(let r=0;r<e.length;r+=1){if(r===t)continue;let s=0;for(const e of o)n[r].has(e)&&(s+=1);s>=a&&i.push({id:e[r].id,shared:s})}i.sort((e,t)=>t.shared-e.shared||e.id.localeCompare(t.id));const s=new Set(e[t].relationships.map(e=>e.target_id));for(const o of i.slice(0,Math.max(0,r)))s.has(o.id)||(e[t].relationships.push({target_id:o.id,type:"related"}),s.add(o.id))}}export function recordToSectionId(e){if(!e)return null;const t=e.source?.locator||"",o=/^#\/?(.+)$/.exec(t);if(o)return o[1];const r=e.facets?.section?.[0];return r||e.id?.replace(/^docs:page:/,"")||null}export function sectionToFortemiRecord(e,t,o){const r=String(e.group||e.title||"Documentation").split(">").map(e=>e.trim()).filter(Boolean),a=e.module||"",n=a?a.replace(/^\.?\//,"").replace(/^sections\//,"sections/"):`${e.id}.md`,i=Array.from(new Set([...r.map(normalizeFacetValue),e.type?normalizeFacetValue(e.type):null].filter(Boolean))),s=t&&t.trim()?t.trim():`${e.title||""} ${e.summary||""}`.trim();return{schema_version:FORTEMI_RECORD_SCHEMA,id:`docs:page:${e.id}`,type:"docs.page",source:{path:n,repo_relative_path:n,locator:`#/${e.id}`},title:e.title||e.id,text:s,facets:{section:[e.id],group:r.length?r.map(normalizeFacetValue):["documentation"]},tags:e.type?[normalizeFacetValue(e.type)]:[],concepts:i,relationships:[],provenance:[{field:"text",source:n,path:"$.text",confidence:"source",privacy:"public"}],privacy:{classification:"public",pii:!1},updated_at:o}}export function buildFortemiIndexExport(e,t={}){const o=t.repo||"pagenary",r=new Map;for(const{section:t,text:o}of e)t&&t.id&&(r.has(t.id)||r.set(t.id,{section:t,text:o||""}));const a=Array.from(r.values()).sort((e,t)=>`docs:page:${e.section.id}`.localeCompare(`docs:page:${t.section.id}`)),n=stableHash(`${o}${a.map(({section:e,text:t})=>`${e.id}\0${e.title||""}\0${t||""}`).join("")}`),i=Number(BigInt(`0x${n.slice(0,8)}`)),s=new Date(1e3*i).toISOString(),c=a.map(({section:e,text:t})=>sectionToFortemiRecord(e,t,e.date||s));if(t.extractConcepts||t.relateByConcept){const e=a.map(({text:e})=>extractConcepts(e,t.conceptOptions));t.extractConcepts&&c.forEach((t,o)=>{t.concepts=Array.from(new Set([...t.concepts,...e[o]])).sort()}),t.relateByConcept&&addConceptRelationships(c,e,t.relationOptions)}return{index:{schema_version:FORTEMI_INDEX_SCHEMA,generated_at:s,source:{repo:o,privacy:"public",build_hash:n},items:c},buildHash:n,generatedAt:s}}export function computeFacetCounts(e){const t={},o=(e,o)=>{null!=o&&(t[e]||={},t[e][o]=(t[e][o]||0)+1)};for(const t of e)o("type",t.type),o("privacy",t.privacy?.classification),(t.tags||[]).forEach(e=>o("tag",e)),(t.concepts||[]).forEach(e=>o("concept",e)),Object.entries(t.facets||{}).forEach(([e,t])=>(t||[]).forEach(t=>o(e,t)));return t}export function chunkFortemiIndex(e,t={}){const o=Math.max(1,t.partSize||100),r=t.partHref||(e=>`part-${String(e).padStart(4,"0")}.json`),a=e.items||[],n=[],i=[];let s=0,c=0;do{const e=a.slice(s,s+o),t=r(c);n.push({schema_version:FORTEMI_CHUNK_PART_SCHEMA,manifest_schema_version:FORTEMI_CHUNK_MANIFEST_SCHEMA,offset:s,items:e}),i.push({href:t,offset:s,count:e.length}),s+=e.length||o,c+=1}while(s<a.length);return{manifest:{schema_version:FORTEMI_CHUNK_MANIFEST_SCHEMA,generated_at:e.generated_at,source:e.source,total:a.length,part_size:o,facets:computeFacetCounts(a),parts:i},parts:n}}
1
+ export const FORTEMI_INDEX_SCHEMA="aiwg.fortemi.index.export.v1";export const FORTEMI_RECORD_SCHEMA="aiwg.fortemi.index.record.v1";export const FORTEMI_CHUNK_MANIFEST_SCHEMA="aiwg.fortemi.index.chunk-manifest.v1";export const FORTEMI_CHUNK_PART_SCHEMA="aiwg.fortemi.index.chunk.v1";export const FORTEMI_METADATA_SCHEMA="pagenary.fortemi.metadata.v1";export const DEFAULT_PART_SIZE=100;export function stableHash(e){let t=0xcbf29ce484222325n;for(let o=0;o<e.length;o+=1)t^=BigInt(255&e.charCodeAt(o)),t=1099511628211n*t&18446744073709551615n;return t.toString(16).padStart(16,"0")}export function stripHtml(e){return e?String(e).replace(/<(script|style)[^>]*>[\s\S]*?<\/\1>/gi," ").replace(/<[^>]+>/g," ").replace(/&nbsp;/gi," ").replace(/&amp;/gi,"&").replace(/&lt;/gi,"<").replace(/&gt;/gi,">").replace(/&quot;/gi,'"').replace(/&#39;/gi,"'").replace(/&mdash;/gi,"—").replace(/&hellip;/gi,"…").replace(/\s+/g," ").trim():""}export function normalizeFacetValue(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"general"}const e=new Set(["the","and","for","are","but","not","you","all","any","can","her","was","one","our","out","has","have","his","how","its","may","new","now","old","see","two","who","did","get","use","used","uses","using","with","this","that","they","them","then","than","from","into","your","yours","each","once","more","most","some","such","only","over","under","when","what","which","while","where","will","would","should","could","also","about","after","before","these","those","their","there","here","been","being","were","because","between","both","same","every","either","back","down","make","made","need","needs","keep","read","reads","call","calls","set","sets","follow","follows","three","plus","step","steps","shape","shapes","related","example","examples","docs","page","pages","http","https","www","com","json","returns","return","inspect","describe","described"]);export function extractConcepts(t,o={}){const n=Number.isFinite(o.max)?o.max:6,r=Number.isFinite(o.minLength)?o.minLength:4;if(!t)return[];const a=new Map,s=String(t).toLowerCase().match(/[a-z][a-z0-9-]+/g)||[];for(const t of s){const o=t.replace(/^-+|-+$/g,"");o.length<r||e.has(o)||a.set(o,(a.get(o)||0)+1)}const c=Array.from(a.entries()).sort((e,t)=>t[1]-e[1]||e[0].localeCompare(t[0])).slice(0,Math.max(0,n)).map(([e])=>normalizeFacetValue(e)).filter(Boolean);return Array.from(new Set(c)).sort()}export function addConceptRelationships(e,t,o={}){const n=Number.isFinite(o.maxRelations)?o.maxRelations:4,r=Number.isFinite(o.minShared)?o.minShared:1,a=t.map(e=>new Set(e||[]));for(let t=0;t<e.length;t+=1){const o=a[t];if(!o||0===o.size)continue;const s=[];for(let n=0;n<e.length;n+=1){if(n===t)continue;const c=[];for(const e of o)a[n].has(e)&&c.push(e);const i=c.length;i>=r&&s.push({id:e[n].id,shared:i,sharedConcepts:c.sort()})}s.sort((e,t)=>t.shared-e.shared||e.id.localeCompare(t.id));const c=new Set(e[t].relationships.map(e=>e.target_id));for(const r of s.slice(0,Math.max(0,n)))c.has(r.id)||(e[t].relationships.push({target_id:r.id,type:"related",label:`Shares ${r.shared} concept${1===r.shared?"":"s"}`,confidence:Math.min(1,r.shared/Math.max(1,o.size)),privacy:"public",metadata:{shared_concepts:r.sharedConcepts}}),c.add(r.id))}}export function recordToSectionId(e){if(!e)return null;const t=e.source?.locator||"",o=/^#\/?(.+)$/.exec(t);if(o)return o[1];const n=e.facets?.section?.[0];return n||e.id?.replace(/^docs:page:/,"")||null}function t(e,t){return null==e?t:JSON.parse(JSON.stringify(e))}function o(e,o){for(const n of o)if(e&&null!=e[n])return t(e[n],null);return null}function n(e){return String(e||"").split(/[-_]+/).filter(Boolean).map(e=>e?e[0].toUpperCase()+e.slice(1):e).join(" ")}function r(e){return{id:`concept:${e}`,prefLabel:n(e),notation:e,uri:`urn:pagenary:concept:${e}`}}export function fortemiRecordToPageMetadata(e){const n=recordToSectionId(e);if(!e||!n)return null;const r={section_id:n,record_id:e.id,type:e.type,title:e.title||n,source:t(e.source,null),facets:t(e.facets||{},{}),tags:t(e.tags||[],[]),concepts:t(e.concepts||[],[]),relationships:t(e.relationships||[],[]),provenance:t(e.provenance||[],[]),privacy:t(e.privacy||null,null),updated_at:e.updated_at||null},a=o(e,["skos_concepts","skosConcepts","concept_details","conceptDetails"]);a&&(r.skos_concepts=a);const s=o(e,["skos_relations","skosRelations","concept_relations","conceptRelations"]);s&&(r.skos_relations=s);const c=o(e,["provenance_events","provenanceEvents","prov","prov_events","provEvents"]);return c&&(r.provenance_events=c),r}export function buildFortemiMetadataExport(e){const o=(e?.items||[]).map(e=>fortemiRecordToPageMetadata(e)).filter(Boolean).sort((e,t)=>e.section_id.localeCompare(t.section_id));return{schema_version:FORTEMI_METADATA_SCHEMA,generated_at:e?.generated_at||null,source:t(e?.source||null,null),pages:o}}export function sectionToFortemiRecord(e,t,o){const n=String(e.group||e.title||"Documentation").split(">").map(e=>e.trim()).filter(Boolean),r=e.module||"",a=r?r.replace(/^\.?\//,"").replace(/^sections\//,"sections/"):`${e.id}.md`,s=Array.from(new Set([...n.map(normalizeFacetValue),e.type?normalizeFacetValue(e.type):null].filter(Boolean))),c=t&&t.trim()?t.trim():`${e.title||""} ${e.summary||""}`.trim();return{schema_version:FORTEMI_RECORD_SCHEMA,id:`docs:page:${e.id}`,type:"docs.page",source:{path:a,repo_relative_path:a,locator:`#/${e.id}`},title:e.title||e.id,text:c,facets:{section:[e.id],group:n.length?n.map(normalizeFacetValue):["documentation"]},tags:e.type?[normalizeFacetValue(e.type)]:[],concepts:s,relationships:[],provenance:[{field:"text",source:a,path:"$.text",confidence:"source",privacy:"public"}],privacy:{classification:"public",pii:!1},updated_at:o}}export function buildFortemiIndexExport(e,t={}){const o=t.repo||"pagenary",n=new Map;for(const{section:t,text:o}of e)t&&t.id&&(n.has(t.id)||n.set(t.id,{section:t,text:o||""}));const a=Array.from(n.values()).sort((e,t)=>`docs:page:${e.section.id}`.localeCompare(`docs:page:${t.section.id}`)),s=stableHash(`${o}${a.map(({section:e,text:t})=>`${e.id}\0${e.title||""}\0${t||""}`).join("")}`),c=Number(BigInt(`0x${s.slice(0,8)}`)),i=new Date(1e3*c).toISOString(),p=a.map(({section:e,text:t})=>sectionToFortemiRecord(e,t,e.date||i));if(t.extractConcepts||t.relateByConcept){const e=a.map(({text:e})=>extractConcepts(e,t.conceptOptions));t.extractConcepts&&p.forEach((t,o)=>{t.concepts=Array.from(new Set([...t.concepts,...e[o]])).sort()}),t.relateByConcept&&addConceptRelationships(p,e,t.relationOptions)}return function(e){for(const t of e)t.skos_concepts=Array.from(new Set(t.concepts||[])).sort().map(r),t.skos_relations=[],t.provenance_events=[{activity:"built",agent:"pagenary",source:t.source?.repo_relative_path||t.source?.path,path:"$",confidence:"source",privacy:t.privacy?.classification||"public",attributes:{record_id:t.id,section_id:recordToSectionId(t)}}]}(p),{index:{schema_version:FORTEMI_INDEX_SCHEMA,generated_at:i,source:{repo:o,privacy:"public",build_hash:s},items:p},buildHash:s,generatedAt:i}}export function computeFacetCounts(e){const t={},o=(e,o)=>{null!=o&&(t[e]||={},t[e][o]=(t[e][o]||0)+1)};for(const t of e)o("type",t.type),o("privacy",t.privacy?.classification),(t.tags||[]).forEach(e=>o("tag",e)),(t.concepts||[]).forEach(e=>o("concept",e)),Object.entries(t.facets||{}).forEach(([e,t])=>(t||[]).forEach(t=>o(e,t)));return t}export function chunkFortemiIndex(e,t={}){const o=Math.max(1,t.partSize||100),n=t.partHref||(e=>`part-${String(e).padStart(4,"0")}.json`),r=e.items||[],a=[],s=[];let c=0,i=0;do{const e=r.slice(c,c+o),t=n(i);a.push({schema_version:FORTEMI_CHUNK_PART_SCHEMA,manifest_schema_version:FORTEMI_CHUNK_MANIFEST_SCHEMA,offset:c,items:e}),s.push({href:t,offset:c,count:e.length}),c+=e.length||o,i+=1}while(c<r.length);return{manifest:{schema_version:FORTEMI_CHUNK_MANIFEST_SCHEMA,generated_at:e.generated_at,source:e.source,total:r.length,part_size:o,facets:computeFacetCounts(r),parts:s},parts:a}}
@@ -1 +1 @@
1
- import{queryAiwgFortemiIndex as t,getAiwgFortemiFacets as e,createAiwgIndexController as n,createAiwgFetchChunkLoader as r,aiwgFortemiIndexToCommunityGraph as i,validateAiwgFortemiChunkManifest as o,validateAiwgFortemiIndexExport as a}from"../vendor/fortemi-aiwg-index.js";import{buildFortemiIndexExport as s,recordToSectionId as l}from"./fortemi-corpus.js";export{t as queryAiwgFortemiIndex,e as getAiwgFortemiFacets,n as createAiwgIndexController,r as createAiwgFetchChunkLoader,i as aiwgFortemiIndexToCommunityGraph};let u=null,c=null,f=!1,m=null,p=null,d=null,h=null;export function escapeRegExp(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}export function flattenManifest(t,e=""){const n=[];for(const r of t){const t=e?`${e} > ${r.title}`:r.title,i=Array.isArray(r.subsections)&&r.subsections.length>0;if(!r.module&&i||n.push({...r,group:e||r.title}),i){const e=flattenManifest(r.subsections,t);n.push(...e)}}return n}function y(t,e){const n=l(t.item);return n?{...e.get(n)||{id:n,title:t.item.title,summary:""},searchRank:t.rank,searchSnippet:t.snippet,searchMatches:t.matches||[]}:null}export function filterSections(t,e){const n=flattenManifest(t),r=e.trim().toLowerCase();return r?n.filter(t=>`${t.title||""} ${t.summary||""} ${t.group||""}`.toLowerCase().includes(r)):n}export async function searchContentPage(e,i,a={}){const l=Math.max(0,a.offset||0),g=Math.max(1,a.limit||25),x=function(t){if(d&&h===t)return d;d=new Map;for(const e of flattenManifest(t))e&&e.id&&!d.has(e.id)&&d.set(e.id,e);return h=t,d}(e),w={types:["docs.page"],rank:!0,snippets:!0,includeMatches:!0,snippetLength:140,limit:g,offset:l},M=await async function(){if(u)return u;if(f)return null;if(c)return c;c=(async()=>{const t=function(){if("undefined"==typeof document||"undefined"==typeof URL)return null;try{return new URL("search-index/",document.baseURI).toString()}catch{return null}}();if(!t||"function"!=typeof fetch)return null;try{const e=await fetch(new URL("manifest.json",t).toString());if(!e.ok)return null;const i=await e.json();if(!o(i).valid)return null;const a=n();return a.loadChunkedIndex(i,r(t),{maxCachedParts:4}),u=a,a}catch{return null}})();const t=await c;return t||(f=!0),c=null,t}();if(M)try{const t=await M.queryChunked(i,{...w,onProgress:a.onProgress}),e=(t.rankedItems||[]).map(t=>y(t,x)).filter(Boolean);return{items:e,total:t.total,offset:l,limit:g,complete:l+e.length>=t.total,source:"static"}}catch{}const $=await async function(t){return m||p||(p=(async()=>{const e=flattenManifest(t),n=await Promise.all(e.map(async t=>{let e="";try{if(t.module){const n=t.module.replace("./","../"),r=await import(n);r.load&&(e=function(t){if("undefined"==typeof document)return"";const e=document.createElement("div");return e.innerHTML=t,e.textContent||e.innerText||""}((await r.load()).html||""))}}catch{}return{section:t,text:`${t.title||""} ${t.summary||""} ${t.group||""} ${e}`.trim()}})),{index:r}=s(n,{repo:"pagenary"}),i=new Map(e.map(t=>[t.id,t]));return m={index:r,byId:i},m})(),p)}(e);if(!i.trim()){const t=Array.from($.byId.values()),e=t.slice(l,l+g);return{items:e,total:t.length,offset:l,limit:g,complete:l+e.length>=t.length,source:"legacy"}}const C=t($.index,i,w),I=(C.rankedItems||[]).map(t=>y(t,$.byId)).filter(Boolean);return{items:I,total:C.total,offset:l,limit:g,complete:l+I.length>=C.total,source:"legacy"}}export async function searchContent(t,e,n={}){return e.trim()?(await searchContentPage(t,e,{limit:25,...n})).items:(await searchContentPage(t,"",{offset:0,limit:Number.MAX_SAFE_INTEGER,...n})).items}export function buildCommunityGraph(t,e={}){const n=flattenManifest(t).map(t=>({section:t,text:`${t.title||""} ${t.summary||""}`.trim()})),{index:r}=s(n,{repo:"pagenary"});return a(r).valid?i(r,e):{nodes:[],edges:[],communities:[]}}export function parseSearchTerms(t){return t.split(/\s+/).map(t=>t.trim()).filter(Boolean)}export function findPreferredIndex(t,e){const n=t.findIndex(t=>t.id===e);return n>=0?n:0}export function resetSearchState(){u=null,c=null,f=!1,m=null,p=null,d=null,h=null}
1
+ import{queryAiwgFortemiIndex as t,getAiwgFortemiFacets as e,createAiwgIndexController as n,createAiwgFetchChunkLoader as r,aiwgFortemiIndexToCommunityGraph as i,validateAiwgFortemiChunkManifest as a,validateAiwgFortemiIndexExport as o}from"../vendor/fortemi-aiwg-index.js";import{buildFortemiMetadataExport as s,buildFortemiIndexExport as l,recordToSectionId as u}from"./fortemi-corpus.js";export{t as queryAiwgFortemiIndex,e as getAiwgFortemiFacets,n as createAiwgIndexController,r as createAiwgFetchChunkLoader,i as aiwgFortemiIndexToCommunityGraph};let c=null,f=null,m=!1,p=null,d=null,y=null,h=null,g=!1,x=null,w=null;export function escapeRegExp(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}export function flattenManifest(t,e=""){const n=[];for(const r of t){const t=e?`${e} > ${r.title}`:r.title,i=Array.isArray(r.subsections)&&r.subsections.length>0;if(!r.module&&i||n.push({...r,group:e||r.title}),i){const e=flattenManifest(r.subsections,t);n.push(...e)}}return n}function M(){if("undefined"==typeof document||"undefined"==typeof URL)return null;try{return new URL("search-index/",document.baseURI).toString()}catch{return null}}async function $(){if(y)return y;if(g)return null;if(h)return h;h=(async()=>{const t=M();if(!t||"function"!=typeof fetch)return null;try{const e=await fetch(new URL("metadata.json",t).toString());if(!e.ok)return null;const n=await e.json();return"pagenary.fortemi.metadata.v1"===n?.schema_version&&Array.isArray(n.pages)?(y=n,n):null}catch{return null}})();const t=await h;return t||(g=!0),h=null,t}async function C(t){return p||d||(d=(async()=>{const e=flattenManifest(t),n=await Promise.all(e.map(async t=>{let e="";try{if(t.module){const n=t.module.replace("./","../"),r=await import(n);r.load&&(e=function(t){if("undefined"==typeof document)return"";const e=document.createElement("div");return e.innerHTML=t,e.textContent||e.innerText||""}((await r.load()).html||""))}}catch{}return{section:t,text:`${t.title||""} ${t.summary||""} ${t.group||""} ${e}`.trim()}})),{index:r}=l(n,{repo:"pagenary"}),i=new Map(e.map(t=>[t.id,t]));return p={index:r,byId:i},p})(),d)}function S(t,e){const n=u(t.item);return n?{...e.get(n)||{id:n,title:t.item.title,summary:""},searchRank:t.rank,searchSnippet:t.snippet,searchMatches:t.matches||[]}:null}export function filterSections(t,e){const n=flattenManifest(t),r=e.trim().toLowerCase();return r?n.filter(t=>`${t.title||""} ${t.summary||""} ${t.group||""}`.toLowerCase().includes(r)):n}export async function searchContentPage(e,i,o={}){const s=Math.max(0,o.offset||0),l=Math.max(1,o.limit||25),u=function(t){if(x&&w===t)return x;x=new Map;for(const e of flattenManifest(t))e&&e.id&&!x.has(e.id)&&x.set(e.id,e);return w=t,x}(e),p={types:["docs.page"],rank:!0,snippets:!0,includeMatches:!0,snippetLength:140,limit:l,offset:s},d=await async function(){if(c)return c;if(m)return null;if(f)return f;f=(async()=>{const t=M();if(!t||"function"!=typeof fetch)return null;try{const e=await fetch(new URL("manifest.json",t).toString());if(!e.ok)return null;const i=await e.json();if(!a(i).valid)return null;const o=n();return o.loadChunkedIndex(i,r(t),{maxCachedParts:4}),c=o,o}catch{return null}})();const t=await f;return t||(m=!0),f=null,t}();if(d)try{const t=await d.queryChunked(i,{...p,onProgress:o.onProgress}),e=(t.rankedItems||[]).map(t=>S(t,u)).filter(Boolean);return{items:e,total:t.total,offset:s,limit:l,complete:s+e.length>=t.total,source:"static"}}catch{}const y=await C(e);if(!i.trim()){const t=Array.from(y.byId.values()),e=t.slice(s,s+l);return{items:e,total:t.length,offset:s,limit:l,complete:s+e.length>=t.length,source:"legacy"}}const h=t(y.index,i,p),g=(h.rankedItems||[]).map(t=>S(t,y.byId)).filter(Boolean);return{items:g,total:h.total,offset:s,limit:l,complete:s+g.length>=h.total,source:"legacy"}}export async function searchContent(t,e,n={}){return e.trim()?(await searchContentPage(t,e,{limit:25,...n})).items:(await searchContentPage(t,"",{offset:0,limit:Number.MAX_SAFE_INTEGER,...n})).items}export function buildCommunityGraph(t,e={}){const n=flattenManifest(t).map(t=>({section:t,text:`${t.title||""} ${t.summary||""}`.trim()})),{index:r}=l(n,{repo:"pagenary"});return o(r).valid?i(r,e):{nodes:[],edges:[],communities:[]}}export async function resolveSectionMetadata(t,e){if(!e)return null;const n=await $();if(n){const t=n.pages.find(t=>t.section_id===e);if(t)return t}const r=await C(t);return s(r.index).pages.find(t=>t.section_id===e)||null}export async function resolveSectionMetadataMap(t){const e=await $()||s((await C(t)).index);return new Map((e.pages||[]).map(t=>[t.section_id,t]))}export function parseSearchTerms(t){return t.split(/\s+/).map(t=>t.trim()).filter(Boolean)}export function findPreferredIndex(t,e){const n=t.findIndex(t=>t.id===e);return n>=0?n:0}export function resetSearchState(){c=null,f=null,m=!1,p=null,d=null,y=null,h=null,g=!1,x=null,w=null}
@@ -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-06-18",
30
+ "dateModified": "2026-06-21",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/api.html"
@@ -165,7 +165,7 @@ await loadSection(section);</code></pre>
165
165
  <hr>
166
166
  <h2 id="library-modules">Library Modules</h2>
167
167
  <h3 id="libsearchjs-fortemi-backed-search">lib/search.js - Fortemi-backed search</h3>
168
- <p>Search runs on the <strong>real, vendored `@fortemi/core` static-index engine</strong> (`src/vendor/fortemi-aiwg-index.js`). At build time, `scripts/build-tenants.js` emits a deterministic <strong>chunked</strong> index per tenant under `dist/&lt;tenant&gt;/search-index/` (`manifest.json` + `part-NNNN.json`, the `aiwg.fortemi.index.*.v1` contract). At runtime the adapter loads that index through an index controller + fetch chunk-loader: parts are fetched lazily and cached (<strong>precache</strong>), results are ranked with snippets, and pages are returned by offset for <strong>infinite scroll</strong>. If the static index is missing or invalid, the adapter falls back to an in-browser index built from section modules — same ranking engine, same result shape. See `.aiwg/architecture/adr/ADR-015-fortemi-core-search-adapter.md`.</p>
168
+ <p>Search runs on the <strong>real, vendored `@fortemi/core` static-index engine</strong> (`src/vendor/fortemi-aiwg-index.js`). At build time, `scripts/build-tenants.js` emits a deterministic <strong>chunked</strong> index per tenant under `dist/&lt;tenant&gt;/search-index/` (`manifest.json` + `part-NNNN.json`, the `aiwg.fortemi.index.*.v1` contract) plus compact `metadata.json` (`pagenary.fortemi.metadata.v1`) for page-addressable Fortemi metadata without duplicating full document text. At runtime the adapter loads that index through an index controller + fetch chunk-loader: parts are fetched lazily and cached (<strong>precache</strong>), results are ranked with snippets, and pages are returned by offset for <strong>infinite scroll</strong>. If the static index is missing or invalid, the adapter falls back to an in-browser index built from section modules — same ranking engine, same result shape. See `.aiwg/architecture/adr/ADR-015-fortemi-core-search-adapter.md`.</p>
169
169
  <p>Build-time and fallback share the deterministic corpus builder in `lib/fortemi-corpus.js`.</p>
170
170
  <h4 id="functions-3">Functions</h4>
171
171
  <p><strong>`escapeRegExp(value: string): string`</strong></p>
@@ -189,12 +189,18 @@ await loadSection(section);</code></pre>
189
189
  <p><strong>`buildCommunityGraph(manifest, options?): { nodes, edges, communities }`</strong></p>
190
190
  <p>Project the corpus into a Fortemi community graph (relationships/facets) — the &quot;graph&quot; capability, no full-text required.</p>
191
191
  <pre><code class="language-javascript">const graph = buildCommunityGraph(MANIFEST);</code></pre>
192
+ <p><strong>`resolveSectionMetadata(manifest, sectionId): Promise&lt;object|null&gt;`</strong></p>
193
+ <p>Resolve compact Fortemi metadata for one page. The static artifact is preferred; the browser fallback derives the same shape from the in-browser Fortemi index.</p>
194
+ <pre><code class="language-javascript">const metadata = await resolveSectionMetadata(MANIFEST, &#39;developers&#39;);
195
+ // metadata.source, facets, concepts, relationships, provenance, privacy</code></pre>
196
+ <p><strong>`resolveSectionMetadataMap(manifest): Promise&lt;Map&lt;string, object&gt;&gt;`</strong></p>
197
+ <p>Resolve all compact Fortemi metadata keyed by section id for page tools, graph node details, or tenant-specific integrations.</p>
192
198
  <p><strong>`findPreferredIndex(entries: Section[], currentId: string): number`</strong></p>
193
199
  <p>Find index of current section in filtered results.</p>
194
200
  <p>Re-exported from the vendored engine for advanced use: `queryAiwgFortemiIndex`, `getAiwgFortemiFacets`, `createAiwgIndexController`, `createAiwgFetchChunkLoader`, `aiwgFortemiIndexToCommunityGraph`.</p>
195
201
  <hr>
196
202
  <h3 id="libfortemi-corpusjs-deterministic-corpus-builder">lib/fortemi-corpus.js - Deterministic corpus builder</h3>
197
- <p>Pure, DOM-free, `Date.now()`-free helpers shared by the build-time generator and the runtime fallback: `buildFortemiIndexExport(entries, { repo })` (records sorted by id, deduped, content-hashed `generated_at` + `source.build_hash`), `chunkFortemiIndex(index, { partSize })`, `sectionToFortemiRecord`, `stripHtml`, `recordToSectionId`, `stableHash`.</p>
203
+ <p>Pure, DOM-free, `Date.now()`-free helpers shared by the build-time generator and the runtime fallback: `buildFortemiIndexExport(entries, { repo })` (records sorted by id, deduped, content-hashed `generated_at` + `source.build_hash`), `chunkFortemiIndex(index, { partSize })`, `sectionToFortemiRecord`, `stripHtml`, `recordToSectionId`, `fortemiRecordToPageMetadata`, `buildFortemiMetadataExport`, `stableHash`.</p>
198
204
  <hr>
199
205
  <h3 id="librouterjs-hash-routing">lib/router.js - Hash Routing</h3>
200
206
  <p>URL hash parsing and resolution.</p>
@@ -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-06-18",
30
+ "dateModified": "2026-06-21",
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-06-18",
30
+ "dateModified": "2026-06-21",
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-06-18",
30
+ "dateModified": "2026-06-21",
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-06-18",
30
+ "dateModified": "2026-06-21",
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-06-18",
30
+ "dateModified": "2026-06-21",
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-06-18",
30
+ "dateModified": "2026-06-21",
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-06-18",
30
+ "dateModified": "2026-06-21",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/tenant-config.html"
@@ -175,9 +175,9 @@
175
175
  <p>The build emits one full stylesheet per selectable theme (`theme-&lt;name&gt;.css`) and swaps the active `&lt;link&gt;` at runtime, so switching is instant and pixel-correct (including code blocks and tables). When disabled, no control, script behavior, or extra stylesheets are emitted.</p>
176
176
  <h4 id="docs-map-relationship-view">Docs map (relationship view)</h4>
177
177
  <p>Opt-in `docsMap` adds a standalone <strong>Docs Map</strong> page — a framework-free SVG view that clusters your pages by concept (from the same index that powers search) so readers can see how the docs relate. It appears in the nav and at `#docs-map`.</p>
178
- <pre><code class="language-json">{ &quot;docsMap&quot;: { &quot;enabled&quot;: true } }</code></pre>
179
- <table><thead><tr><th style="text-align: left">Property</th><th style="text-align: left">Type</th><th style="text-align: left">Default</th><th style="text-align: left">Description</th></tr></thead><tbody><tr><td style="text-align: left">`docsMap.enabled`</td><td style="text-align: left">boolean</td><td style="text-align: left">`false`</td><td style="text-align: left">Add the Docs Map page + nav entry</td></tr><tr><td style="text-align: left">`docsMap.title`</td><td style="text-align: left">string</td><td style="text-align: left">`&quot;Docs Map&quot;`</td><td style="text-align: left">Nav/heading label</td></tr></tbody></table>
180
- <p>The graph is computed <strong>at build time</strong> from your actual page content. Each page&#39;s body is run through a concept-extraction procedure (the same Fortemi model that powers search): pages cluster into communities by nav group, and pages that share salient concepts are linked with `related` edges. The richer and more cross-referenced your docs, the denser the graph. The build embeds the result as `docs-map-data.js` and the page renders it client-side (no server, no React). Tenants with too little content fall back to a lightweight manifest-derived graph, and small or empty corpora render a friendly placeholder. When disabled, nothing is emitted.</p>
178
+ <pre><code class="language-json">{ &quot;docsMap&quot;: { &quot;enabled&quot;: true, &quot;renderer&quot;: &quot;svg&quot; } }</code></pre>
179
+ <table><thead><tr><th style="text-align: left">Property</th><th style="text-align: left">Type</th><th style="text-align: left">Default</th><th style="text-align: left">Description</th></tr></thead><tbody><tr><td style="text-align: left">`docsMap.enabled`</td><td style="text-align: left">boolean</td><td style="text-align: left">`false`</td><td style="text-align: left">Add the Docs Map page + nav entry</td></tr><tr><td style="text-align: left">`docsMap.title`</td><td style="text-align: left">string</td><td style="text-align: left">`&quot;Docs Map&quot;`</td><td style="text-align: left">Nav/heading label</td></tr><tr><td style="text-align: left">`docsMap.renderer`</td><td style="text-align: left">`&quot;svg&quot;` or `&quot;cytoscape&quot;`</td><td style="text-align: left">`&quot;svg&quot;`</td><td style="text-align: left">Graph renderer. `svg` is the current renderer and fallback; `cytoscape` reserves the opt-in richer JS renderer path.</td></tr></tbody></table>
180
+ <p>The graph is computed <strong>at build time</strong> from your actual page content using the vendored Fortemi graph adapter. Each page&#39;s body is run through the Fortemi concept procedure: pages cluster into communities by nav group, and pages that share salient concepts are linked with weighted `related` edges. The build embeds the Fortemi graph plus compact node/relationship metadata as `docs-map-data.js`; the SVG renderer uses that metadata for subtle hover titles, edge weight, confidence, shared-concept details, zoom/pan controls, neighbor highlighting, and pinned node popups. The default renderer is the framework-free SVG view; optional renderers must fall back to SVG if they cannot initialize. Tenants with too little content fall back to a lightweight manifest-derived graph, and small or empty corpora render a friendly placeholder. When disabled, nothing is emitted.</p>
181
181
  <blockquote>
182
182
  <p>See `examples/docs-map-corpus/` (the <strong>docs-map</strong> recipe) for a fully</p>
183
183
  <p>cross-linked sample corpus — 14 interconnected pages that produce a graph of</p>