@pagenary/publisher 2026.6.14 → 2026.6.15

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 (72) hide show
  1. package/examples/page-effects/config.json +2 -1
  2. package/examples/page-effects/content/disclosure.md +40 -0
  3. package/examples/page-effects/content/index.md +28 -0
  4. package/examples/page-effects/content/parallax-and-sticky.md +13 -0
  5. package/examples/page-effects/content/scroll-snap.md +29 -0
  6. package/examples/page-effects/content/scrollytelling.md +35 -0
  7. package/examples/page-effects/manifest.json +16 -4
  8. package/package.json +1 -1
  9. package/scripts/build-tenants.js +138 -15
  10. package/site/{app.2a8fb673db91.js → app.54e1ad90b733.js} +1 -1
  11. package/site/app.js +1 -1
  12. package/site/assets/images/pipeline.e57f0dbfd05a.svg +33 -0
  13. package/site/assets/images/pipeline.svg +33 -0
  14. package/site/index.html +4 -4
  15. package/site/lib/page-effects.2131b53bea6b.js +1 -0
  16. package/site/lib/page-effects.js +1 -1
  17. package/site/llms.txt +11 -6
  18. package/site/{manifest.a870df3a026b.js → manifest.95224f06782d.js} +49 -44
  19. package/site/manifest.js +49 -44
  20. package/site/pages/accessible-authoring.html +3 -3
  21. package/site/pages/api.html +1 -1
  22. package/site/pages/architecture.html +1 -1
  23. package/site/pages/blog-layout.html +1 -1
  24. package/site/pages/deployment.html +1 -1
  25. package/site/pages/developer-guide.html +1 -1
  26. package/site/pages/extending.html +1 -1
  27. package/site/pages/overview.html +189 -0
  28. package/site/pages/page-effects.html +265 -0
  29. package/site/pages/publishing.html +258 -0
  30. package/site/pages/quickstart.html +1 -1
  31. package/site/pages/search-and-data.html +1 -1
  32. package/site/pages/seo-strategy.html +1 -1
  33. package/site/pages/showcase-gallery.html +184 -0
  34. package/site/pages/showcase-story.html +130 -0
  35. package/site/pages/tenant-config.html +7 -9
  36. package/site/pages/theming-recipes.html +1 -1
  37. package/site/pages/welcome.html +1 -1
  38. package/site/robots.txt +1 -1
  39. package/site/search-index/manifest.json +36 -38
  40. package/site/search-index/metadata.json +706 -355
  41. package/site/search-index/{part-0000.c67bff0d76c7.json → part-0000.64586040c481.json} +707 -354
  42. package/site/search-index/part-0000.json +707 -354
  43. package/site/sections/overview.5a483987fb9d.js +3 -0
  44. package/site/sections/overview.js +3 -0
  45. package/site/sections/page-effects.a38e5a7715d4.js +3 -0
  46. package/site/sections/page-effects.js +3 -0
  47. package/site/sections/publishing.32bf1d55b285.js +3 -0
  48. package/site/sections/publishing.js +3 -0
  49. package/site/sections/showcase-gallery.cd729f94752d.js +3 -0
  50. package/site/sections/showcase-gallery.js +3 -0
  51. package/site/sections/showcase-story.79311d1302c8.js +3 -0
  52. package/site/sections/showcase-story.js +3 -0
  53. package/site/sections/tenant-config.552c0d8c6d2b.js +3 -0
  54. package/site/sections/tenant-config.js +1 -1
  55. package/site/sitemap.xml +21 -9
  56. package/site/{styles.d957d4c1c1af.css → styles.bdb30ba34de5.css} +326 -8
  57. package/site/styles.css +326 -8
  58. package/src/app.js +35 -28
  59. package/src/lib/page-effects.js +410 -23
  60. package/src/styles.css +324 -6
  61. package/examples/page-effects/content/effects-spike.md +0 -212
  62. package/site/lib/page-effects.2e4e21674b13.js +0 -1
  63. package/site/pages/managed-hosting.html +0 -298
  64. package/site/pages/page-effects-spike.html +0 -152
  65. package/site/pages/theme-token-audit.html +0 -158
  66. package/site/sections/managed-hosting.e707893d4520.js +0 -3
  67. package/site/sections/managed-hosting.js +0 -3
  68. package/site/sections/page-effects-spike.d18d9601f432.js +0 -3
  69. package/site/sections/page-effects-spike.js +0 -3
  70. package/site/sections/tenant-config.e99335c66502.js +0 -3
  71. package/site/sections/theme-token-audit.182808e9139e.js +0 -3
  72. package/site/sections/theme-token-audit.js +0 -3
@@ -7,5 +7,6 @@
7
7
  "accentColor": "#e11d48",
8
8
  "surfaceColor": "#ffffff",
9
9
  "inkColor": "#0b1220",
10
- "bottomNav": "always"
10
+ "bottomNav": "always",
11
+ "livingScroll": true
11
12
  }
@@ -0,0 +1,40 @@
1
+ ---
2
+ title: Disclosure (accordion)
3
+ summary: Production accordion built on native <details> — works with zero JS, keyboard-operable, single-open optional.
4
+ hero:
5
+ eyebrow: Primitive
6
+ title: Disclosure
7
+ subtitle: An accordion that's just native <details> — accessible and complete with JavaScript off.
8
+ align: start
9
+ ---
10
+
11
+ # Disclosure (accordion)
12
+
13
+ `.pe-accordion` styles native `<details>/<summary>`, so each panel opens, closes,
14
+ and takes keyboard focus with **zero JavaScript**. Add `data-pe-single` to make it
15
+ single-open — opening one panel closes its siblings — which is a pure enhancement:
16
+ with JS off, every panel still works independently and no content is ever hidden
17
+ behind the script.
18
+
19
+ ```html
20
+ <div class="pe-accordion" data-pe-single>
21
+ <details open>
22
+ <summary>What is a page effect?</summary>
23
+ <p>An opt-in, accessible enhancement — heroes, reveal-on-scroll, living scroll,
24
+ disclosure — that degrades to plain content when JavaScript or motion is off.</p>
25
+ </details>
26
+ <details>
27
+ <summary>Does it work without JavaScript?</summary>
28
+ <p>Yes. The accordion is native <code>&lt;details&gt;</code>. The only thing JS
29
+ adds is the single-open grouping, and that's optional.</p>
30
+ </details>
31
+ <details>
32
+ <summary>Is it keyboard accessible?</summary>
33
+ <p>Native disclosure is operable with Enter/Space and shows a visible focus
34
+ ring. The open/closed state uses a +/− marker, not color alone.</p>
35
+ </details>
36
+ </div>
37
+ ```
38
+
39
+ Drop `data-pe-single` to let multiple panels stay open at once. Style is theme-token
40
+ aware, so it inherits each tenant's palette automatically.
@@ -81,6 +81,34 @@ the viewport, and appear instantly under reduced-motion.
81
81
  </div>
82
82
  ```
83
83
 
84
+ ## Staggered reveal
85
+
86
+ The cards below arrive in a wave — `data-reveal-stagger` on the grid sequences
87
+ each child's entrance as it scrolls into view (JS-off and reduced-motion show all
88
+ at once).
89
+
90
+ ```html
91
+ <div class="pe-card-grid" data-reveal-stagger="90">
92
+ <section class="pe-demo-card"><h3>One</h3><p>Arrives first.</p></section>
93
+ <section class="pe-demo-card"><h3>Two</h3><p>A beat later.</p></section>
94
+ <section class="pe-demo-card"><h3>Three</h3><p>Then this one.</p></section>
95
+ <section class="pe-demo-card"><h3>Four</h3><p>And finally this.</p></section>
96
+ </div>
97
+ ```
98
+
99
+ ## Figure zoom
100
+
101
+ The figure below shows its image normally; click it (or press Enter when focused)
102
+ to enlarge it in a modal. With JavaScript off, the image is just visible at its
103
+ normal size.
104
+
105
+ ```html
106
+ <figure class="pe-figure" data-pe-zoom>
107
+ <img src="images/ridge.svg" alt="Layered ridge illustration" />
108
+ <figcaption>Click to enlarge — opens a native dialog with focus trap and Esc-to-close.</figcaption>
109
+ </figure>
110
+ ```
111
+
84
112
  ## Authoring paths
85
113
 
86
114
  You have four ways to add a hero, from least to most control:
@@ -61,3 +61,16 @@ The CTA band primitive is just as portable:
61
61
  ```
62
62
 
63
63
  Keep scrolling to confirm the sticky hero releases at the end of the page.
64
+
65
+ ## Parallax on any element
66
+
67
+ The card below isn't a hero — it drifts on its own with `data-pe-parallax="0.35"`.
68
+ Any element can; the speed is clamped so it never wanders far, and it's skipped
69
+ entirely under reduced motion.
70
+
71
+ ```html
72
+ <div class="pe-demo-card" data-pe-parallax="0.35" style="max-width: 20rem;">
73
+ <h3>Parallax aside</h3>
74
+ <p>Drifts gently against the scroll — a non-hero element with its own speed.</p>
75
+ </div>
76
+ ```
@@ -0,0 +1,29 @@
1
+ ---
2
+ title: Scroll-snap sections
3
+ summary: A CSS-only .pe-snap region whose panels snap as you scroll — opt-in, no runtime, never traps the keyboard.
4
+ hero:
5
+ eyebrow: Primitive
6
+ title: Scroll-snap sections
7
+ subtitle: Bounded snap panels for landing pages — pure CSS, and the keyboard is never trapped.
8
+ align: start
9
+ ---
10
+
11
+ # Scroll-snap sections
12
+
13
+ `.pe-snap` is a **self-contained, opt-in** scroll region whose `.pe-snap__panel`
14
+ children snap into place as you scroll it. It's pure CSS — no runtime — and uses
15
+ `scroll-snap-type: y proximity` (not `mandatory`) inside a bounded height, so it
16
+ never takes over the page scroll and never traps keyboard users.
17
+
18
+ ```html
19
+ <div class="pe-snap">
20
+ <section class="pe-snap__panel"><h2>Capture</h2><p>Scroll inside this region — each panel snaps to the top.</p></section>
21
+ <section class="pe-snap__panel"><h2>Compose</h2><p>Proximity snapping, so you can still stop between panels.</p></section>
22
+ <section class="pe-snap__panel"><h2>Publish</h2><p>Tab through; the keyboard is never trapped.</p></section>
23
+ </div>
24
+ ```
25
+
26
+ Smooth glide to the snap points is gated under `prefers-reduced-motion:
27
+ no-preference`; reduced-motion readers get instant positioning. The panels are
28
+ ordinary sections, so the content is complete and reachable with or without
29
+ snapping.
@@ -0,0 +1,35 @@
1
+ ---
2
+ title: Scrollytelling
3
+ summary: A sticky stage beside content steps — the stage updates as each step scrolls into view. JS-off shows the steps as plain content.
4
+ hero:
5
+ eyebrow: Primitive
6
+ title: Scrollytelling
7
+ subtitle: A sticky stage that reacts as you read past each step — built from sticky layout + scroll position, no animation engine.
8
+ align: start
9
+ ---
10
+
11
+ # Scrollytelling
12
+
13
+ A `.pe-scrolly` block pairs a **sticky stage** with a column of `[data-pe-step]`
14
+ content steps. As each step scrolls into view, the stage's matching layer
15
+ crossfades in. With JavaScript off, the steps are ordinary readable content and
16
+ the stage shows its layers statically — the swap is a pure enhancement.
17
+
18
+ ```html
19
+ <div class="pe-scrolly">
20
+ <div class="pe-scrolly__stage">
21
+ <div data-pe-step="capture"><h2>Capture</h2></div>
22
+ <div data-pe-step="compose"><h2>Compose</h2></div>
23
+ <div data-pe-step="publish"><h2>Publish</h2></div>
24
+ </div>
25
+ <div class="pe-scrolly__steps">
26
+ <section data-pe-step="capture"><p>Write your content as Markdown, HTML, or a JS module.</p></section>
27
+ <section data-pe-step="compose"><p>Shared templates turn it into a branded, tenant-specific site.</p></section>
28
+ <section data-pe-step="publish"><p>Build emits static files — host them anywhere.</p></section>
29
+ </div>
30
+ </div>
31
+ ```
32
+
33
+ The active step is the last one to scroll past the upper-middle of the viewport —
34
+ the same rect-based detection scroll-spy uses, so no bespoke animation engine is
35
+ needed. The crossfade is gated under `prefers-reduced-motion: no-preference`.
@@ -14,10 +14,22 @@
14
14
  "file": "parallax-and-sticky.md"
15
15
  },
16
16
  {
17
- "id": "effects-spike",
18
- "title": "Effect Spike",
19
- "summary": "Throwaway prototypes for disclosure, scroll-snap panels, and figure zoom candidates.",
20
- "file": "effects-spike.md"
17
+ "id": "disclosure",
18
+ "title": "Disclosure",
19
+ "summary": "Production accordion on native <details> — zero-JS, keyboard-operable, single-open optional.",
20
+ "file": "disclosure.md"
21
+ },
22
+ {
23
+ "id": "scroll-snap",
24
+ "title": "Scroll-snap",
25
+ "summary": "CSS-only .pe-snap region — bounded, opt-in snap panels that never trap the keyboard.",
26
+ "file": "scroll-snap.md"
27
+ },
28
+ {
29
+ "id": "scrollytelling",
30
+ "title": "Scrollytelling",
31
+ "summary": "A sticky stage that reacts as you read past each step — sticky layout + scroll position, no animation engine.",
32
+ "file": "scrollytelling.md"
21
33
  }
22
34
  ]
23
35
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagenary/publisher",
3
- "version": "2026.6.14",
3
+ "version": "2026.6.15",
4
4
  "type": "module",
5
5
  "description": "Multi-tenant static publishing component for Pagenary platform.",
6
6
  "license": "AGPL-3.0-or-later",
@@ -277,6 +277,11 @@ function isGitAvailable() {
277
277
  * Execute a command with timeout and retry
278
278
  */
279
279
  async function execWithRetry(command, options = {}) {
280
+ const commandSegments = Array.isArray(command) ? command : [];
281
+ if (commandSegments.length === 0) {
282
+ throw new Error('execWithRetry expected an argument array');
283
+ }
284
+
280
285
  const {
281
286
  cwd = root,
282
287
  timeout = DEFAULT_GIT_TIMEOUT,
@@ -288,10 +293,13 @@ async function execWithRetry(command, options = {}) {
288
293
  for (let attempt = 1; attempt <= retries; attempt++) {
289
294
  try {
290
295
  return await new Promise((resolve, reject) => {
291
- const proc = spawn('sh', ['-c', command], {
296
+ const proc = spawn(commandSegments[0], commandSegments.slice(1), {
292
297
  cwd,
293
298
  stdio: ['pipe', 'pipe', 'pipe'],
294
- env: { ...env, GIT_TERMINAL_PROMPT: '0' },
299
+ env: {
300
+ ...env,
301
+ GIT_TERMINAL_PROMPT: '0'
302
+ },
295
303
  timeout
296
304
  });
297
305
 
@@ -305,7 +313,10 @@ async function execWithRetry(command, options = {}) {
305
313
  if (code === 0) {
306
314
  resolve({ stdout, stderr });
307
315
  } else {
308
- reject(new Error(`Command failed (exit ${code}): ${stderr || stdout}`));
316
+ const prettyCommand = commandSegments
317
+ .map((segment) => maskAuthSegment(String(segment)))
318
+ .join(' ');
319
+ reject(new Error(`Command failed (exit ${code}): ${stderr || stdout} (command: ${prettyCommand})`));
309
320
  }
310
321
  });
311
322
 
@@ -334,6 +345,34 @@ async function execWithRetry(command, options = {}) {
334
345
  throw lastError;
335
346
  }
336
347
 
348
+ function maskAuthSegment(value) {
349
+ return value.replace(/\/\/[^@]+@/, '//***@');
350
+ }
351
+
352
+ /**
353
+ * Build an authenticated git URL when GIT_CREDENTIALS is available.
354
+ */
355
+ function withGitCredentials(rawUrl) {
356
+ const credentials = process.env.GIT_CREDENTIALS || '';
357
+ const [username, password] = credentials.split(':', 2);
358
+ if (!credentials || !username || password === undefined || !rawUrl) return rawUrl;
359
+
360
+ try {
361
+ const source = new URL(rawUrl);
362
+ if (!['https:', 'http:'].includes(source.protocol)) {
363
+ return rawUrl;
364
+ }
365
+
366
+ if (source.username) {
367
+ return rawUrl;
368
+ }
369
+
370
+ return `${source.protocol}//${encodeURIComponent(username)}:${encodeURIComponent(password)}@${source.host}${source.pathname}${source.search}${source.hash}`;
371
+ } catch {
372
+ return rawUrl;
373
+ }
374
+ }
375
+
337
376
  /**
338
377
  * Generate cache key for git source
339
378
  */
@@ -364,7 +403,10 @@ function isImmutableRef(ref) {
364
403
  */
365
404
  async function getHeadCommit(repoDir) {
366
405
  try {
367
- const { stdout } = await execWithRetry(`git -C "${repoDir}" rev-parse HEAD`, { cwd: root, retries: 1 });
406
+ const { stdout } = await execWithRetry(['git', '-C', repoDir, 'rev-parse', 'HEAD'], {
407
+ cwd: root,
408
+ retries: 1
409
+ });
368
410
  return stdout.trim();
369
411
  } catch {
370
412
  return null;
@@ -385,7 +427,7 @@ async function getChangedFiles(repoDir, oldCommit, newCommit, subPath = '.') {
385
427
  try {
386
428
  // Use --name-status to get file status (A=added, M=modified, D=deleted)
387
429
  const { stdout } = await execWithRetry(
388
- `git -C "${repoDir}" diff --name-status ${oldCommit} ${newCommit}`,
430
+ ['git', '-C', repoDir, 'diff', '--name-status', oldCommit, newCommit],
389
431
  { cwd: root, retries: 1 }
390
432
  );
391
433
 
@@ -461,6 +503,7 @@ async function cloneGitSource(source, cacheDir, options = {}) {
461
503
 
462
504
  // Sanitize URL for logging (hide credentials)
463
505
  const safeUrl = url.replace(/\/\/[^@]+@/, '//***@');
506
+ const authUrl = withGitCredentials(url);
464
507
 
465
508
  console.log(` ↳ git source: ${safeUrl}`);
466
509
  console.log(` ↳ ref: ${ref}, path: ${subPath || '(root)'}`);
@@ -494,8 +537,8 @@ async function cloneGitSource(source, cacheDir, options = {}) {
494
537
  console.log(` ↳ current HEAD: ${oldCommit ? oldCommit.slice(0, 7) : 'unknown'}`);
495
538
 
496
539
  try {
497
- await execWithRetry(`git -C "${cloneDir}" fetch origin ${ref} --depth ${effectiveDepth}`, { cwd: root });
498
- await execWithRetry(`git -C "${cloneDir}" checkout FETCH_HEAD`, { cwd: root });
540
+ await execWithRetry(['git', '-C', cloneDir, 'fetch', 'origin', ref, '--depth', String(effectiveDepth)], { cwd: root });
541
+ await execWithRetry(['git', '-C', cloneDir, 'checkout', 'FETCH_HEAD'], { cwd: root });
499
542
  newCommit = await getHeadCommit(cloneDir);
500
543
  console.log(` ↳ updated HEAD: ${newCommit ? newCommit.slice(0, 7) : 'unknown'}`);
501
544
  } catch (err) {
@@ -513,33 +556,33 @@ async function cloneGitSource(source, cacheDir, options = {}) {
513
556
  wasCloned = true;
514
557
 
515
558
  // Build clone command
516
- let cloneCmd = `git clone --depth ${effectiveDepth}`;
559
+ const cloneArgs = ['git', 'clone', '--depth', String(effectiveDepth)];
517
560
 
518
561
  // Add branch/ref
519
562
  // For commits, we can't use --branch, need to fetch after
520
563
  if (!isImmutableRef(ref) || /^v?\d+\.\d+/.test(ref)) {
521
- cloneCmd += ` --branch ${ref}`;
564
+ cloneArgs.push('--branch', ref);
522
565
  }
523
566
 
524
567
  // Sparse checkout preparation
525
568
  if (effectiveSparse) {
526
- cloneCmd += ' --filter=blob:none --sparse';
569
+ cloneArgs.push('--filter=blob:none', '--sparse');
527
570
  }
528
571
 
529
- cloneCmd += ` "${url}" "${cloneDir}"`;
572
+ cloneArgs.push(authUrl, cloneDir);
530
573
 
531
574
  try {
532
- await execWithRetry(cloneCmd, { cwd: root });
575
+ await execWithRetry(cloneArgs, { cwd: root });
533
576
 
534
577
  // If ref is a commit SHA, checkout after clone
535
578
  if (/^[0-9a-f]{7,40}$/i.test(ref) && !/^v?\d+\.\d+/.test(ref)) {
536
- await execWithRetry(`git -C "${cloneDir}" fetch --depth ${effectiveDepth} origin ${ref}`, { cwd: root });
537
- await execWithRetry(`git -C "${cloneDir}" checkout ${ref}`, { cwd: root });
579
+ await execWithRetry(['git', '-C', cloneDir, 'fetch', '--depth', String(effectiveDepth), 'origin', ref], { cwd: root });
580
+ await execWithRetry(['git', '-C', cloneDir, 'checkout', ref], { cwd: root });
538
581
  }
539
582
 
540
583
  // Set up sparse checkout if needed
541
584
  if (effectiveSparse) {
542
- await execWithRetry(`git -C "${cloneDir}" sparse-checkout set "${subPath}"`, { cwd: root });
585
+ await execWithRetry(['git', '-C', cloneDir, 'sparse-checkout', 'set', subPath], { cwd: root });
543
586
  }
544
587
 
545
588
  newCommit = await getHeadCommit(cloneDir);
@@ -1670,6 +1713,83 @@ async function applyReadingProgressConfig(distDir, config, tenantId) {
1670
1713
  console.log(` ↳ enabled reading progress for ${tenantId}`);
1671
1714
  }
1672
1715
 
1716
+ /**
1717
+ * Living scroll on any layout (docs included). Blog tenants opt in via
1718
+ * `blog.livingScroll` (handled in applyBlogLayout); this is the general path,
1719
+ * driven by a top-level `livingScroll: true` (or `reader.livingScroll`). It sets
1720
+ * the layout-agnostic body flag plus a reading-progress bar. The hidden base
1721
+ * state, reduced-motion fallback, and JS-off completeness all live in
1722
+ * page-effects.js + styles.css — the build only sets the hooks.
1723
+ */
1724
+ async function applyLivingScrollConfig(distDir, config, tenantId) {
1725
+ const reader = (config.reader && typeof config.reader === 'object') ? config.reader : {};
1726
+ if (config.livingScroll !== true && reader.livingScroll !== true) return;
1727
+
1728
+ const indexPath = path.join(distDir, 'index.html');
1729
+ if (!(await pathExists(indexPath))) return;
1730
+
1731
+ let html = await fsp.readFile(indexPath, 'utf8');
1732
+ // Idempotent, and don't fight applyBlogLayout if it already set the blog alias.
1733
+ if (/<body[^>]*data-(?:blog-)?living-scroll(?:[\s=>])/.test(html)) return;
1734
+ const attrs = ['data-living-scroll'];
1735
+ // Living scroll pairs with a progress bar; only add it if not already set
1736
+ // (applyReadingProgressConfig runs first and may have added it).
1737
+ if (!/<body[^>]*data-reading-progress(?:[\s=>])/.test(html)) {
1738
+ attrs.push('data-reading-progress');
1739
+ }
1740
+ html = html.replace(/<body(?=[\s>])/, `<body ${attrs.join(' ')}`);
1741
+ await fsp.writeFile(indexPath, html, 'utf8');
1742
+ console.log(` ↳ enabled living scroll for ${tenantId}`);
1743
+ }
1744
+
1745
+ /**
1746
+ * On-this-page TOC + scroll-spy (#73). Opt-in via a top-level `pageToc`:
1747
+ * pageToc: true → rail placement, default min
1748
+ * pageToc: { placement: "rail"|"top"|"off", minHeadings: N }
1749
+ * Sets the body hooks the pageToc page-effect reads; the effect generates the nav
1750
+ * client-side from the page's headings.
1751
+ */
1752
+ async function applyPageTocConfig(distDir, config, tenantId) {
1753
+ const toc = config.pageToc;
1754
+ if (toc !== true && (!toc || typeof toc !== 'object')) return;
1755
+ const placementRaw = toc && typeof toc === 'object' ? toc.placement : undefined;
1756
+ if (placementRaw === 'off' || (toc && typeof toc === 'object' && toc.enabled === false)) return;
1757
+ const placement = placementRaw === 'top' ? 'top' : 'rail';
1758
+ const min = toc && typeof toc === 'object' && Number.isInteger(toc.minHeadings) && toc.minHeadings > 0
1759
+ ? toc.minHeadings : null;
1760
+
1761
+ const indexPath = path.join(distDir, 'index.html');
1762
+ if (!(await pathExists(indexPath))) return;
1763
+
1764
+ let html = await fsp.readFile(indexPath, 'utf8');
1765
+ if (/<body[^>]*data-page-toc(?:[\s=>])/.test(html)) return;
1766
+ const attrs = [`data-page-toc="${placement}"`];
1767
+ if (min) attrs.push(`data-page-toc-min="${min}"`);
1768
+ html = html.replace(/<body(?=[\s>])/, `<body ${attrs.join(' ')}`);
1769
+ await fsp.writeFile(indexPath, html, 'utf8');
1770
+ console.log(` ↳ enabled on-this-page TOC (${placement}) for ${tenantId}`);
1771
+ }
1772
+
1773
+ /**
1774
+ * Sidebar nav collapse behavior. Configurable via `navCollapse`:
1775
+ * "overlay" (default) — drawer hidden by default; hamburger slides it over the content
1776
+ * "push" — nav visible; collapse slides it out and reflows the content
1777
+ * "instant" — nav visible; collapse instantly hides the column
1778
+ * Writes data-nav-collapse so styles.css can switch modes (drawer modes apply only
1779
+ * to the default left-sidebar layout; positioned-nav demos are unaffected).
1780
+ */
1781
+ async function applyNavCollapseConfig(distDir, config, tenantId) {
1782
+ const raw = typeof config.navCollapse === 'string' ? config.navCollapse.toLowerCase() : '';
1783
+ const mode = raw === 'push' || raw === 'instant' ? raw : 'overlay';
1784
+ const indexPath = path.join(distDir, 'index.html');
1785
+ if (!(await pathExists(indexPath))) return;
1786
+ let html = await fsp.readFile(indexPath, 'utf8');
1787
+ if (/<body[^>]*data-nav-collapse(?:[\s=>])/.test(html)) return;
1788
+ html = html.replace(/<body(?=[\s>])/, `<body data-nav-collapse="${mode}"`);
1789
+ await fsp.writeFile(indexPath, html, 'utf8');
1790
+ console.log(` ↳ nav collapse mode: ${mode} for ${tenantId}`);
1791
+ }
1792
+
1673
1793
  /**
1674
1794
  * Write the synthetic blog-index section module and register it in manifest.js.
1675
1795
  * Mirrors applyDocsMap's injection (append + idempotent).
@@ -4776,6 +4896,9 @@ async function buildTenant(tenant, targetOverride, cacheDir, buildOptions) {
4776
4896
  await applyNavAlignment(distDir, config, tenantId);
4777
4897
  await applyBlogLayout(distDir, config, tenantId);
4778
4898
  await applyReadingProgressConfig(distDir, config, tenantId);
4899
+ await applyLivingScrollConfig(distDir, config, tenantId);
4900
+ await applyPageTocConfig(distDir, config, tenantId);
4901
+ await applyNavCollapseConfig(distDir, config, tenantId);
4779
4902
  await applyDocsMap(distDir, config, tenantId);
4780
4903
  await applyWelcome(distDir, config, tenantId);
4781
4904
  }
@@ -1 +1 @@
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.a870df3a026b.js";import{updateMetaTags as i}from"./seo.90687a1d3d78.js";import{escapeRegExp as r,searchContentPage as c,flattenManifest as l,findPreferredIndex as d,resolveSectionMetadata as m}from"./lib/search.c52bcae8afda.js";import{resolveTarget as p,resolveEntry as u}from"./lib/router.f9d1cfba022d.js";import{composeExportDocument as v,collectExportableSections as f}from"./lib/export.2db2c0bd974c.js";import{renderMermaidBlocks as h}from"./mermaid-init.f25ee3b6ec1e.js";import{initMediaEmbeds as g}from"./media-init.16fde41d8850.js";import{highlightCodeBlocks as y}from"./syntax-highlight.9d51f36b24da.js";import{initPageEffects as b}from"./lib/page-effects.2e4e21674b13.js";const E=document.getElementById("app"),x=document.getElementById("nav"),L=document.getElementById("year"),C=document.getElementById("exportBtn"),T=document.getElementById("commandToggle"),N=document.getElementById("commandPalette"),k=document.getElementById("commandInput"),$=document.getElementById("commandList"),w=document.getElementById("mobileMenuToggle"),A=document.querySelector(".sidebar"),S=document.body.hasAttribute("data-reading-progress"),I=document.body.dataset.readingProgressMode||"bar",M="docs-toolkit-command-query",H=new Map,P=new Map,_=new Map,B=new Set;let q=[],R=0,F=!1,j=(localStorage.getItem(M)||"").trim(),D=!1;function O(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 W(e,t={}){const{scrollToHighlight:a=!1}=t,{targetId:o,parentId:s}=function(e){return p(e,n)}(e);F&&ee(),s&&z(s,!0),D=a||Boolean(j),location.hash.replace("#","")===o?V():location.hash=`#${o}`}function U(){return location.hash.replace("#","")||t}async function V(){const s=U(),r=u(s,n);if(!r)return;const{entry:c,targetId:l,parentId:d}=r;l===s?(d&&z(d,!0),function(e,t=null){P.forEach(e=>{e.setAttribute("aria-current","false")});const n=P.get(e);if(n&&n.setAttribute("aria-current","page"),t){const e=P.get(t);e&&e.setAttribute("aria-current","page")}}(c.id,d),await async function(s){if(!s)return;const r=await import(s.module),c=r.load||r.default;if("function"!=typeof c)return void(E.innerHTML='<article class="section"><p>Section failed to load.</p></article>');const l=await c();E.innerHTML=l.html||"",function(t){if(function(e){const t=function(e){if(S)return!0;const t=e?.progress;return!0===e?.reading_progress||!0===e?.readingProgress||(!0===t||!(!t||"object"!=typeof t||!0!==t.enabled&&!0!==t.bar&&"bar"!==t.mode))}(e);if(document.body.toggleAttribute("data-reading-progress",t),t){const t=e?.progress&&"object"==typeof e.progress?e.progress.mode||(e.progress.label?"label":"bar"):I;document.body.dataset.readingProgressMode=t}else delete document.body.dataset.readingProgressMode}(t),!t)return;const n=(E.querySelector(".doc-content")||E.querySelector("article, section")||E).querySelector("h1");if(!n)return;if(t.hero){const e=document.createElement("figure");e.className="post-hero";const a=document.createElement("img");a.src=t.hero,a.alt="",a.loading="eager",e.appendChild(a),n.before(e)}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.author&&a.push(`By ${t.author}`),t.showReadingTime&&t.reading_time&&a.push(t.reading_label||`${t.reading_time} min read`);let o=n,s=null;if(a.length>0){const e=document.createElement("p");e.className="doc-meta";const t=document.createElement("span");t.className="doc-meta-text",t.textContent=a.join(" · "),e.appendChild(t),n.after(e),o=e,s=e}if(t.showSummary&&t.summary){const e=document.createElement("p");e.className="doc-summary",e.textContent=t.summary,o.after(e),o=e}if(Array.isArray(t.tags)&&t.tags.length){const e=document.createElement("ul");e.className="post-tags";for(const n of t.tags){const t=document.createElement("li");t.textContent=n,e.appendChild(t)}o.after(e),o=e}!async function(t,n,a=null){const o=t.id,s=n.closest(".doc-content")||n.parentElement;let i=null;try{i=await m(e,o)}catch{return}if(U()!==o||!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)}(i))return;const r=document.createElement("div");r.className="doc-fortemi-tools";const c=`docFortemiPanel-${o}`,l=document.createElement("div");l.id=c,l.className="doc-fortemi-panel",l.hidden=!0,l.setAttribute("role","region"),l.setAttribute("aria-label","Page metadata"),function(e,t){e.textContent="",K(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=X(e),"object"==typeof e&&e&&e.definition&&(t.title=e.definition),n.appendChild(t)}),e.appendChild(n)}(e,n):G(e,"concepts","none"),(t.skos_relations||[]).forEach(t=>{const n=X({id:t.source_id||t.source}),a=X({id:t.target_id||t.target});G(e,t.type||"related",`${J(n)} -> ${J(a)}`)})}),K(e,"Source and Provenance",e=>{G(e,"source",t.source?.repo_relative_path||t.source?.path),G(e,"locator",t.source?.locator),G(e,"updated",t.updated_at),G(e,"privacy",t.privacy?.classification),(t.provenance||[]).forEach(t=>{G(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(" · ");G(e,t.activity||"activity",n)})}),K(e,"Related Pages",e=>{const n=t.relationships||[];if(!n.length)return void G(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",r=Array.isArray(e.metadata?.shared_concepts)?e.metadata.shared_concepts.map(J).join(", "):null;n.textContent=`${i}: ${J(o)}${s?` (${s})`:""}`,r&&(n.title=`Shared concepts: ${r}`),n.addEventListener("click",()=>{o&&W(o)}),t.appendChild(n),a.appendChild(t)}),e.appendChild(a)})}(l,i);const d=document.createElement("button");d.type="button",d.className="doc-fortemi-button",d.setAttribute("aria-expanded","false"),d.setAttribute("aria-controls",c),d.title="Show page metadata",d.innerHTML='<span aria-hidden="true">i</span><span class="sr-only">Show page metadata</span>',d.addEventListener("click",()=>{const e=l.hidden;l.hidden=!e,d.setAttribute("aria-expanded",String(e))}),a?(d.classList.add("doc-fortemi-button-inline"),a.appendChild(d),s.appendChild(l)):(r.appendChild(d),s.append(r,l))}(t,o,s)}(s),await h(E),g(E),await y(E),function(e){const s=E.querySelector(".bottom-nav");s&&s.remove();const i="function"==typeof n?n(e):null;if(i&&i.collection)return void function(e){const n=function(){const e=o.postNav;if(!1===e)return null;const t=e&&"object"==typeof e?e:{};return{prev:!1!==t.prev,next:!1!==t.next,index:!1!==t.index,label:"string"==typeof t.label?t.label:null}}();if(!n)return;const{prev:s,next:i}=a(e),r=o.blogIndex||("string"==typeof t?t:null),c=n.index&&r&&r!==e,l=n.prev&&Boolean(s),d=n.next&&Boolean(i);if(!l&&!d&&!c)return;const m=document.createElement("nav");if(m.className="bottom-nav bottom-nav--posts",m.setAttribute("aria-label","Post navigation"),m.appendChild(l?Y(s,"prev"):Z()),c){const e=n.label||(o.blogIndexTitle?`All ${o.blogIndexTitle}`:"Back to index"),t=document.createElement("a");t.href=`#${r}`,t.className="bottom-nav-index",t.textContent=e,t.setAttribute("aria-label",e),t.addEventListener("click",e=>{e.preventDefault(),W(r)}),m.appendChild(t)}else m.appendChild(Z());m.appendChild(d?Y(i,"next"):Z());(E.querySelector("section")||E).appendChild(m)}(e);if("never"===o.bottomNav)return;const r=(o.bottomNavSections||[]).some(t=>e.startsWith(t)),c="mobile"===o.bottomNav&&!r,{prev:l,next:d}=a(e);if(!l&&!d)return;const m=document.createElement("nav");m.className="bottom-nav",c&&m.classList.add("mobile-only"),m.appendChild(l?Y(l,"prev"):Z()),d&&m.appendChild(Y(d,"next")),(E.querySelector("section")||E).appendChild(m)}(s.id),E.scrollTop=0,window.scrollTo(0,0),"function"==typeof l.afterRender&&l.afterRender(E),b(E),i({title:s.title,description:s.summary,siteTitle:o.siteTitle,siteUrl:o.siteUrl,sectionId:s.id,ogImage:s.ogImage||o.ogImage}),H.set(s.id,Date.now());const d=D;D=!1,ue(d),requestAnimationFrame(()=>E.focus())}(c)):location.replace(`#${l}`)}function z(e,t){if(!e)return;const n=_.get(e);t?(B.add(e),n&&n.group.classList.add("expanded")):(B.delete(e),n&&n.group.classList.remove("expanded"))}function J(e){return String(e||"").replace(/[-_]+/g," ").replace(/\s+/g," ").trim()}function X(e){return"string"==typeof e?J(e):e.prefLabel||e.pref_label||e.label||e.id||"concept"}function G(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 K(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 Y(e,t){const n=document.createElement("div");n.className=`bottom-nav-item bottom-nav-${t}`;const a=`<span class="bottom-nav-chevron">${"prev"===t?"‹":"›"}</span>`,o=document.createElement("a");o.href=`#${e.id}`,o.className="bottom-nav-link";const s="prev"===t?"Previous":"Next";return o.title=`${s}: ${e.title}`,o.setAttribute("aria-label",`${s}: ${e.title}`),o.textContent=e.title,o.addEventListener("click",t=>{t.preventDefault(),W(e.id)}),"prev"===t?(n.innerHTML=a,n.appendChild(o)):(n.appendChild(o),n.insertAdjacentHTML("beforeend",a)),n}function Z(){const e=document.createElement("div");return e.className="bottom-nav-spacer",e}function Q(){if(!N||!k)return;F=!0,N.hidden=!1;const e=j;k.value=e,se(e),requestAnimationFrame(()=>{k.focus(),e&&k.select()})}function ee(){N&&k&&(F=!1,N.hidden=!0,k.blur())}!function(){x.innerHTML="",P.clear(),_.clear();let t=B.size>0;e.forEach((e,n)=>{if(e.url){const t=O(e,"nav-leaf");return void x.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(),W(e.id,{scrollToHighlight:Boolean(j)})}),o.querySelector(".nav-expand-toggle").addEventListener("click",t=>{t.stopPropagation();const n=!B.has(e.id);z(e.id,n)}),o.addEventListener("click",t=>{if(t.target===o){const t=!B.has(e.id);z(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=!B.has(e.id);z(e.id,t)}));const s=document.createElement("div");s.className="nav-sublist",e.subsections.forEach(e=>{if(e.url){const t=O(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=!B.has(e.id);z(e.id,t)});const a=document.createElement("div");a.className="nav-sublist nav-sublist-nested",e.subsections.forEach(e=>{if(e.url){const t=O(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=!B.has(e.id);z(e.id,t)});const o=document.createElement("div");o.className="nav-sublist nav-sublist-deep",e.subsections.forEach(e=>{if(e.url){const t=O(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=!B.has(e.id);z(e.id,t)});const a=document.createElement("div");a.className="nav-sublist nav-sublist-ultra",e.subsections.forEach(e=>{if(e.url){const t=O(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",()=>W(e.id,{scrollToHighlight:Boolean(j)})),a.appendChild(t),P.set(e.id,t)}),t.append(n,a),o.appendChild(t),P.set(e.id,n),_.set(e.id,{group:t,button:n,list:a});const s=B.has(e.id)&&!e.collapsed;return void z(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",()=>W(e.id,{scrollToHighlight:Boolean(j)})),o.appendChild(t),P.set(e.id,t)}),t.append(n,o),a.appendChild(t),P.set(e.id,n),_.set(e.id,{group:t,button:n,list:o});const s=B.has(e.id)&&!e.collapsed;return void z(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",()=>W(e.id,{scrollToHighlight:Boolean(j)})),a.appendChild(t),P.set(e.id,t)}),t.append(n,a),s.appendChild(t),P.set(e.id,n),_.set(e.id,{group:t,button:n,list:a});const o=B.has(e.id)&&!e.collapsed;return void z(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",()=>W(e.id,{scrollToHighlight:Boolean(j)})),s.appendChild(t),P.set(e.id,t)}),n.append(o,s),x.appendChild(n),P.set(e.id,o),_.set(e.id,{group:n,button:o,list:s});const i=!e.collapsed&&(B.has(e.id)||!t&&!B.size);z(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",()=>W(e.id,{scrollToHighlight:Boolean(j)})),x.appendChild(t),P.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 r=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches;s(i&&o(i)?i:r&&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{}})}(),j&&(D=!0),window.addEventListener("hashchange",()=>{j&&(D=!0),V()}),L.textContent=(new Date).getFullYear(),V(),T&&N&&k&&$&&(T.addEventListener("click",()=>{F?ee():Q()}),k.addEventListener("input",()=>{const e=k.value;pe(e,!0),se(e)}),k.addEventListener("keydown",e=>{const t=q.length-1;if("ArrowDown"===e.key)e.preventDefault(),R=Math.min(t,R+1),re();else if("ArrowUp"===e.key)e.preventDefault(),R=Math.max(0,R-1),re();else if("Enter"===e.key){e.preventDefault();const t=q[R];t&&(pe(k.value,!0),W(t.id,{scrollToHighlight:!0}),ee())}else"Escape"===e.key&&(e.preventDefault(),ee())}),$.addEventListener("click",e=>{const t=e.target.closest("[data-section]");if(!t)return;const n=t.dataset.section;n&&(pe(k.value,!0),W(n,{scrollToHighlight:!0}),ee())}),N.addEventListener("click",e=>{e.target===N&&ee()}),$.addEventListener("scroll",()=>{oe.loading||oe.complete||$.scrollTop+$.clientHeight>=$.scrollHeight-48&&ie(!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(),F?ee():Q()):"Escape"===e.key&&F&&(e.preventDefault(),ee())}));let te=null,ne=!1;const ae=25;let oe={query:"",offset:0,total:0,complete:!0,loading:!1};async function se(e){$&&(oe={query:e,offset:0,total:0,complete:!1,loading:!1},q=[],!ne&&e.trim()&&(ne=!0,$.innerHTML='<li class="cmd-item cmd-loading">Indexing content...</li>'),clearTimeout(te),te=setTimeout(async()=>{await ie(!0);const e=U();R=d(q,e),re(),ne=!1},e.trim()?150:0))}async function ie(t=!1){if(oe.loading)return;if(!t&&oe.complete)return;const n=oe.query;let a;oe.loading=!0;try{a=await c(e,n,{offset:oe.offset,limit:ae})}catch{return void(oe.loading=!1)}n===oe.query?(q=t?a.items:q.concat(a.items),oe.offset=q.length,oe.total=a.total,oe.complete=a.complete||0===a.items.length,oe.loading=!1,function(){if($){if($.innerHTML="",!q.length){const e=document.createElement("li");return e.className="cmd-item",e.setAttribute("aria-selected","false"),e.textContent="No matches.",void $.appendChild(e)}if(q.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)}$.appendChild(t)}),!oe.complete&&oe.total>q.length){const e=document.createElement("li");e.className="cmd-item cmd-more",e.setAttribute("aria-selected","false"),e.setAttribute("role","presentation"),e.textContent=`Showing ${q.length} of ${oe.total} — scroll for more`,$.appendChild(e)}}}()):oe.loading=!1}function re(){$&&Array.from($.children).forEach((e,t)=>{const n=t===R&&q.length;e.setAttribute("aria-selected",n?"true":"false"),n&&e.scrollIntoView({block:"nearest"})})}const ce=!s||!1!==s.enabled,le=s&&Array.isArray(s.scopes)&&s.scopes.length?s.scopes:["page","site"];C&&(ce?C.addEventListener("click",function(){const t=document.createElement("div");t.className="export-options-overlay";const n=le.map(e=>{const t=de[e];return t?`<button type="button" class="export-option-btn" data-scope="${e}">\n <span class="export-option-title">${t.title}</span>\n <span class="export-option-desc">${t.desc}</span>\n </button>`:""}).join("");t.innerHTML=`\n <div class="export-options-modal">\n <div class="export-options-header">EXPORT OPTIONS</div>\n <div class="export-options-buttons">\n ${n}\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 a=()=>{t.classList.remove("active"),setTimeout(()=>t.remove(),200)};t.querySelector(".export-cancel-btn").addEventListener("click",a),t.addEventListener("click",e=>{e.target===t&&a()}),t.querySelectorAll(".export-option-btn").forEach(t=>{t.addEventListener("click",()=>{const n=t.dataset.scope;a(),async function(t="site"){if(!C)return;const n=C.innerHTML;C.disabled=!0;const a=document.createElement("div");a.className="export-loading-overlay",a.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(a),setTimeout(()=>a.classList.add("active"),10);const o=a.querySelector(".export-loading-fill"),i=a.querySelector(".export-loading-status");try{let n;if("page"===t){const t=U(),a=f(e).find(e=>e.id===t);n=a?[a]:[]}else n=f(e);if(0===n.length)return alert("No content available to export."),a.remove(),void(C.disabled=!1);const r=[],c=n.length;let l=0;for(const e of n){l++;const n=l/c*100;o.style.width=`${n}%`,i.textContent="page"===t?`Exporting: ${e.title}`:`Processing section ${l} 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 a=me((await n()).html||"");r.push({section:e,html:a})}catch(t){console.error("Failed to include section in export",e.id,t)}}i.textContent="Generating document...",await new Promise(e=>setTimeout(e,200));const d=v(r,s);i.textContent="Opening print dialog...",await new Promise(e=>setTimeout(e,100)),function(e){document.getElementById("exportPrintFrame")?.remove();const t=document.createElement("iframe");t.id="exportPrintFrame",t.setAttribute("aria-hidden","true"),t.setAttribute("tabindex","-1"),t.style.cssText="position:fixed;width:0;height:0;border:0;left:-9999px;top:0;visibility:hidden;",document.body.appendChild(t);let n=!1;const a=()=>{n||(n=!0,setTimeout(()=>t.remove(),500))},o=t.contentWindow.document;o.open(),o.write(e),o.close();const s=()=>{const e=t.contentWindow;try{e.addEventListener("afterprint",a,{once:!0}),e.focus(),e.print(),setTimeout(a,6e4)}catch(e){console.error("Export print failed",e),t.remove()}};"complete"===t.contentWindow.document.readyState?setTimeout(s,60):t.addEventListener("load",()=>setTimeout(s,60),{once:!0})}(d),a.classList.remove("active"),setTimeout(()=>a.remove(),300)}catch(e){console.error("Export failed",e),alert("Export failed. Check console for details."),a.remove()}finally{C.disabled=!1,C.innerHTML=n}}(n)})})}):C.remove());const de={page:{title:"Current Page",desc:"Export only this section"},site:{title:"Entire Site",desc:"Export all documentation"}};function me(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 pe(e,t=!1){j=e.trim(),t&&(j?localStorage.setItem(M,j):localStorage.removeItem(M)),ue()}function ue(e=!1){E&&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}}),c=[];let l;for(;l=i.nextNode();){const e=l.nodeValue.toLowerCase();o.some(t=>e.includes(t))&&c.push(l)}const d=new RegExp(`(${a.map(r).join("|")})`,"gi");c.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"})})}(E,j,{scrollToFirst:e})}w&&A&&(w.addEventListener("click",()=>{A.classList.contains("mobile-open")?(A.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),w.setAttribute("aria-expanded","false")):(A.classList.add("mobile-open"),document.body.classList.add("menu-open"),w.setAttribute("aria-expanded","true"))}),x.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"))&&(A.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),w.setAttribute("aria-expanded","false"))}}),document.addEventListener("click",e=>{window.innerWidth<=960&&A.classList.contains("mobile-open")&&!A.contains(e.target)&&!w.contains(e.target)&&(A.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),w.setAttribute("aria-expanded","false"))}));const ve=document.getElementById("brandHome");ve&&ve.addEventListener("click",e=>{e.preventDefault(),W(t)});
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.95224f06782d.js";import{updateMetaTags as i}from"./seo.90687a1d3d78.js";import{escapeRegExp as r,searchContentPage as c,flattenManifest as l,findPreferredIndex as d,resolveSectionMetadata as m}from"./lib/search.c52bcae8afda.js";import{resolveTarget as p,resolveEntry as u}from"./lib/router.f9d1cfba022d.js";import{composeExportDocument as v,collectExportableSections as f}from"./lib/export.2db2c0bd974c.js";import{renderMermaidBlocks as h}from"./mermaid-init.f25ee3b6ec1e.js";import{initMediaEmbeds as g}from"./media-init.16fde41d8850.js";import{highlightCodeBlocks as y}from"./syntax-highlight.9d51f36b24da.js";import{initPageEffects as b}from"./lib/page-effects.2131b53bea6b.js";const E=document.getElementById("app"),x=document.getElementById("nav"),C=document.getElementById("year"),T=document.getElementById("exportBtn"),L=document.getElementById("commandToggle"),N=document.getElementById("commandPalette"),k=document.getElementById("commandInput"),$=document.getElementById("commandList"),w=document.getElementById("mobileMenuToggle"),A=document.querySelector(".sidebar"),S=document.body.hasAttribute("data-reading-progress"),I=document.body.dataset.readingProgressMode||"bar",M="docs-toolkit-command-query",H=new Map,P=new Map,_=new Map,B=new Set;let q=[],R=0,F=!1,j=(localStorage.getItem(M)||"").trim(),D=!1;function W(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 O(e,t={}){const{scrollToHighlight:a=!1}=t,{targetId:o,parentId:s}=function(e){return p(e,n)}(e);F&&ee(),s&&z(s,!0),D=a||Boolean(j),location.hash.replace("#","")===o?V():location.hash=`#${o}`}function U(){return location.hash.replace("#","")||t}async function V(){const s=U(),r=u(s,n);if(!r)return;const{entry:c,targetId:l,parentId:d}=r;l===s?(d&&z(d,!0),function(e,t=null){P.forEach(e=>{e.setAttribute("aria-current","false")});const n=P.get(e);if(n&&n.setAttribute("aria-current","page"),t){const e=P.get(t);e&&e.setAttribute("aria-current","page")}}(c.id,d),await async function(s){if(!s)return;const r=await import(s.module),c=r.load||r.default;if("function"!=typeof c)return void(E.innerHTML='<article class="section"><p>Section failed to load.</p></article>');const l=await c();E.innerHTML=l.html||"",function(t){if(function(e){const t=function(e){if(S)return!0;const t=e?.progress;return!0===e?.reading_progress||!0===e?.readingProgress||(!0===t||!(!t||"object"!=typeof t||!0!==t.enabled&&!0!==t.bar&&"bar"!==t.mode))}(e);if(document.body.toggleAttribute("data-reading-progress",t),t){const t=e?.progress&&"object"==typeof e.progress?e.progress.mode||(e.progress.label?"label":"bar"):I;document.body.dataset.readingProgressMode=t}else delete document.body.dataset.readingProgressMode}(t),!t)return;const n=(E.querySelector(".doc-content")||E.querySelector("article, section")||E).querySelector("h1");if(!n)return;if(t.hero){const e=document.createElement("figure");e.className="post-hero";const a=document.createElement("img");a.src=t.hero,a.alt="",a.loading="eager",e.appendChild(a),n.before(e)}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.author&&a.push(`By ${t.author}`),t.showReadingTime&&t.reading_time&&a.push(t.reading_label||`${t.reading_time} min read`);let o=n,s=null;if(a.length>0){const e=document.createElement("p");e.className="doc-meta";const t=document.createElement("span");t.className="doc-meta-text",t.textContent=a.join(" · "),e.appendChild(t),n.after(e),o=e,s=e}if(t.showSummary&&t.summary){const e=document.createElement("p");e.className="doc-summary",e.textContent=t.summary,o.after(e),o=e}if(Array.isArray(t.tags)&&t.tags.length){const e=document.createElement("ul");e.className="post-tags";for(const n of t.tags){const t=document.createElement("li");t.textContent=n,e.appendChild(t)}o.after(e),o=e}!async function(t,n,a=null){const o=t.id,s=n.closest(".doc-content")||n.parentElement;let i=null;try{i=await m(e,o)}catch{return}if(U()!==o||!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)}(i))return;const r=document.createElement("div");r.className="doc-fortemi-tools";const c=`docFortemiPanel-${o}`,l=document.createElement("div");l.id=c,l.className="doc-fortemi-panel",l.hidden=!0,l.setAttribute("role","region"),l.setAttribute("aria-label","Page metadata"),function(e,t){e.textContent="",K(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=X(e),"object"==typeof e&&e&&e.definition&&(t.title=e.definition),n.appendChild(t)}),e.appendChild(n)}(e,n):G(e,"concepts","none"),(t.skos_relations||[]).forEach(t=>{const n=X({id:t.source_id||t.source}),a=X({id:t.target_id||t.target});G(e,t.type||"related",`${J(n)} -> ${J(a)}`)})}),K(e,"Source and Provenance",e=>{G(e,"source",t.source?.repo_relative_path||t.source?.path),G(e,"locator",t.source?.locator),G(e,"updated",t.updated_at),G(e,"privacy",t.privacy?.classification),(t.provenance||[]).forEach(t=>{G(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(" · ");G(e,t.activity||"activity",n)})}),K(e,"Related Pages",e=>{const n=t.relationships||[];if(!n.length)return void G(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",r=Array.isArray(e.metadata?.shared_concepts)?e.metadata.shared_concepts.map(J).join(", "):null;n.textContent=`${i}: ${J(o)}${s?` (${s})`:""}`,r&&(n.title=`Shared concepts: ${r}`),n.addEventListener("click",()=>{o&&O(o)}),t.appendChild(n),a.appendChild(t)}),e.appendChild(a)})}(l,i);const d=document.createElement("button");d.type="button",d.className="doc-fortemi-button",d.setAttribute("aria-expanded","false"),d.setAttribute("aria-controls",c),d.title="Show page metadata",d.innerHTML='<span aria-hidden="true">i</span><span class="sr-only">Show page metadata</span>',d.addEventListener("click",()=>{const e=l.hidden;l.hidden=!e,d.setAttribute("aria-expanded",String(e))}),a?(d.classList.add("doc-fortemi-button-inline"),a.appendChild(d),s.appendChild(l)):(r.appendChild(d),s.append(r,l))}(t,o,s)}(s),await h(E),g(E),await y(E),function(e){const s=E.querySelector(".bottom-nav");s&&s.remove();const i="function"==typeof n?n(e):null;if(i&&i.collection)return void function(e){const n=function(){const e=o.postNav;if(!1===e)return null;const t=e&&"object"==typeof e?e:{};return{prev:!1!==t.prev,next:!1!==t.next,index:!1!==t.index,label:"string"==typeof t.label?t.label:null}}();if(!n)return;const{prev:s,next:i}=a(e),r=o.blogIndex||("string"==typeof t?t:null),c=n.index&&r&&r!==e,l=n.prev&&Boolean(s),d=n.next&&Boolean(i);if(!l&&!d&&!c)return;const m=document.createElement("nav");if(m.className="bottom-nav bottom-nav--posts",m.setAttribute("aria-label","Post navigation"),m.appendChild(l?Y(s,"prev"):Z()),c){const e=n.label||(o.blogIndexTitle?`All ${o.blogIndexTitle}`:"Back to index"),t=document.createElement("a");t.href=`#${r}`,t.className="bottom-nav-index",t.textContent=e,t.setAttribute("aria-label",e),t.addEventListener("click",e=>{e.preventDefault(),O(r)}),m.appendChild(t)}else m.appendChild(Z());m.appendChild(d?Y(i,"next"):Z());(E.querySelector("section")||E).appendChild(m)}(e);if("never"===o.bottomNav)return;const r=(o.bottomNavSections||[]).some(t=>e.startsWith(t)),c="mobile"===o.bottomNav&&!r,{prev:l,next:d}=a(e);if(!l&&!d)return;const m=document.createElement("nav");m.className="bottom-nav",c&&m.classList.add("mobile-only"),m.appendChild(l?Y(l,"prev"):Z()),d&&m.appendChild(Y(d,"next")),(E.querySelector("section")||E).appendChild(m)}(s.id),E.scrollTop=0,window.scrollTo(0,0),"function"==typeof l.afterRender&&l.afterRender(E),b(E),i({title:s.title,description:s.summary,siteTitle:o.siteTitle,siteUrl:o.siteUrl,sectionId:s.id,ogImage:s.ogImage||o.ogImage}),H.set(s.id,Date.now());const d=D;D=!1,ue(d),requestAnimationFrame(()=>E.focus())}(c)):location.replace(`#${l}`)}function z(e,t){if(!e)return;const n=_.get(e);t?(B.add(e),n&&n.group.classList.add("expanded")):(B.delete(e),n&&n.group.classList.remove("expanded"))}function J(e){return String(e||"").replace(/[-_]+/g," ").replace(/\s+/g," ").trim()}function X(e){return"string"==typeof e?J(e):e.prefLabel||e.pref_label||e.label||e.id||"concept"}function G(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 K(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 Y(e,t){const n=document.createElement("div");n.className=`bottom-nav-item bottom-nav-${t}`;const a=`<span class="bottom-nav-chevron">${"prev"===t?"‹":"›"}</span>`,o=document.createElement("a");o.href=`#${e.id}`,o.className="bottom-nav-link";const s="prev"===t?"Previous":"Next";return o.title=`${s}: ${e.title}`,o.setAttribute("aria-label",`${s}: ${e.title}`),o.textContent=e.title,o.addEventListener("click",t=>{t.preventDefault(),O(e.id)}),"prev"===t?(n.innerHTML=a,n.appendChild(o)):(n.appendChild(o),n.insertAdjacentHTML("beforeend",a)),n}function Z(){const e=document.createElement("div");return e.className="bottom-nav-spacer",e}function Q(){if(!N||!k)return;F=!0,N.hidden=!1;const e=j;k.value=e,se(e),requestAnimationFrame(()=>{k.focus(),e&&k.select()})}function ee(){N&&k&&(F=!1,N.hidden=!0,k.blur())}!function(){x.innerHTML="",P.clear(),_.clear();let t=B.size>0;e.forEach((e,n)=>{if(e.url){const t=W(e,"nav-leaf");return void x.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(),O(e.id,{scrollToHighlight:Boolean(j)})}),o.querySelector(".nav-expand-toggle").addEventListener("click",t=>{t.stopPropagation();const n=!B.has(e.id);z(e.id,n)}),o.addEventListener("click",t=>{if(t.target===o){const t=!B.has(e.id);z(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=!B.has(e.id);z(e.id,t)}));const s=document.createElement("div");s.className="nav-sublist",e.subsections.forEach(e=>{if(e.url){const t=W(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=!B.has(e.id);z(e.id,t)});const a=document.createElement("div");a.className="nav-sublist nav-sublist-nested",e.subsections.forEach(e=>{if(e.url){const t=W(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=!B.has(e.id);z(e.id,t)});const o=document.createElement("div");o.className="nav-sublist nav-sublist-deep",e.subsections.forEach(e=>{if(e.url){const t=W(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=!B.has(e.id);z(e.id,t)});const a=document.createElement("div");a.className="nav-sublist nav-sublist-ultra",e.subsections.forEach(e=>{if(e.url){const t=W(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",()=>O(e.id,{scrollToHighlight:Boolean(j)})),a.appendChild(t),P.set(e.id,t)}),t.append(n,a),o.appendChild(t),P.set(e.id,n),_.set(e.id,{group:t,button:n,list:a});const s=B.has(e.id)&&!e.collapsed;return void z(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",()=>O(e.id,{scrollToHighlight:Boolean(j)})),o.appendChild(t),P.set(e.id,t)}),t.append(n,o),a.appendChild(t),P.set(e.id,n),_.set(e.id,{group:t,button:n,list:o});const s=B.has(e.id)&&!e.collapsed;return void z(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",()=>O(e.id,{scrollToHighlight:Boolean(j)})),a.appendChild(t),P.set(e.id,t)}),t.append(n,a),s.appendChild(t),P.set(e.id,n),_.set(e.id,{group:t,button:n,list:a});const o=B.has(e.id)&&!e.collapsed;return void z(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",()=>O(e.id,{scrollToHighlight:Boolean(j)})),s.appendChild(t),P.set(e.id,t)}),n.append(o,s),x.appendChild(n),P.set(e.id,o),_.set(e.id,{group:n,button:o,list:s});const i=!e.collapsed&&(B.has(e.id)||!t&&!B.size);z(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",()=>O(e.id,{scrollToHighlight:Boolean(j)})),x.appendChild(t),P.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 r=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches;s(i&&o(i)?i:r&&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{}})}(),j&&(D=!0),window.addEventListener("hashchange",()=>{j&&(D=!0),V()}),C.textContent=(new Date).getFullYear(),V(),L&&N&&k&&$&&(L.addEventListener("click",()=>{F?ee():Q()}),k.addEventListener("input",()=>{const e=k.value;pe(e,!0),se(e)}),k.addEventListener("keydown",e=>{const t=q.length-1;if("ArrowDown"===e.key)e.preventDefault(),R=Math.min(t,R+1),re();else if("ArrowUp"===e.key)e.preventDefault(),R=Math.max(0,R-1),re();else if("Enter"===e.key){e.preventDefault();const t=q[R];t&&(pe(k.value,!0),O(t.id,{scrollToHighlight:!0}),ee())}else"Escape"===e.key&&(e.preventDefault(),ee())}),$.addEventListener("click",e=>{const t=e.target.closest("[data-section]");if(!t)return;const n=t.dataset.section;n&&(pe(k.value,!0),O(n,{scrollToHighlight:!0}),ee())}),N.addEventListener("click",e=>{e.target===N&&ee()}),$.addEventListener("scroll",()=>{oe.loading||oe.complete||$.scrollTop+$.clientHeight>=$.scrollHeight-48&&ie(!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(),F?ee():Q()):"Escape"===e.key&&F&&(e.preventDefault(),ee())}));let te=null,ne=!1;const ae=25;let oe={query:"",offset:0,total:0,complete:!0,loading:!1};async function se(e){$&&(oe={query:e,offset:0,total:0,complete:!1,loading:!1},q=[],!ne&&e.trim()&&(ne=!0,$.innerHTML='<li class="cmd-item cmd-loading">Indexing content...</li>'),clearTimeout(te),te=setTimeout(async()=>{await ie(!0);const e=U();R=d(q,e),re(),ne=!1},e.trim()?150:0))}async function ie(t=!1){if(oe.loading)return;if(!t&&oe.complete)return;const n=oe.query;let a;oe.loading=!0;try{a=await c(e,n,{offset:oe.offset,limit:ae})}catch{return void(oe.loading=!1)}n===oe.query?(q=t?a.items:q.concat(a.items),oe.offset=q.length,oe.total=a.total,oe.complete=a.complete||0===a.items.length,oe.loading=!1,function(){if($){if($.innerHTML="",!q.length){const e=document.createElement("li");return e.className="cmd-item",e.setAttribute("aria-selected","false"),e.textContent="No matches.",void $.appendChild(e)}if(q.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)}$.appendChild(t)}),!oe.complete&&oe.total>q.length){const e=document.createElement("li");e.className="cmd-item cmd-more",e.setAttribute("aria-selected","false"),e.setAttribute("role","presentation"),e.textContent=`Showing ${q.length} of ${oe.total} — scroll for more`,$.appendChild(e)}}}()):oe.loading=!1}function re(){$&&Array.from($.children).forEach((e,t)=>{const n=t===R&&q.length;e.setAttribute("aria-selected",n?"true":"false"),n&&e.scrollIntoView({block:"nearest"})})}const ce=!s||!1!==s.enabled,le=s&&Array.isArray(s.scopes)&&s.scopes.length?s.scopes:["page","site"];T&&(ce?T.addEventListener("click",function(){const t=document.createElement("div");t.className="export-options-overlay";const n=le.map(e=>{const t=de[e];return t?`<button type="button" class="export-option-btn" data-scope="${e}">\n <span class="export-option-title">${t.title}</span>\n <span class="export-option-desc">${t.desc}</span>\n </button>`:""}).join("");t.innerHTML=`\n <div class="export-options-modal">\n <div class="export-options-header">EXPORT OPTIONS</div>\n <div class="export-options-buttons">\n ${n}\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 a=()=>{t.classList.remove("active"),setTimeout(()=>t.remove(),200)};t.querySelector(".export-cancel-btn").addEventListener("click",a),t.addEventListener("click",e=>{e.target===t&&a()}),t.querySelectorAll(".export-option-btn").forEach(t=>{t.addEventListener("click",()=>{const n=t.dataset.scope;a(),async function(t="site"){if(!T)return;const n=T.innerHTML;T.disabled=!0;const a=document.createElement("div");a.className="export-loading-overlay",a.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(a),setTimeout(()=>a.classList.add("active"),10);const o=a.querySelector(".export-loading-fill"),i=a.querySelector(".export-loading-status");try{let n;if("page"===t){const t=U(),a=f(e).find(e=>e.id===t);n=a?[a]:[]}else n=f(e);if(0===n.length)return alert("No content available to export."),a.remove(),void(T.disabled=!1);const r=[],c=n.length;let l=0;for(const e of n){l++;const n=l/c*100;o.style.width=`${n}%`,i.textContent="page"===t?`Exporting: ${e.title}`:`Processing section ${l} 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 a=me((await n()).html||"");r.push({section:e,html:a})}catch(t){console.error("Failed to include section in export",e.id,t)}}i.textContent="Generating document...",await new Promise(e=>setTimeout(e,200));const d=v(r,s);i.textContent="Opening print dialog...",await new Promise(e=>setTimeout(e,100)),function(e){document.getElementById("exportPrintFrame")?.remove();const t=document.createElement("iframe");t.id="exportPrintFrame",t.setAttribute("aria-hidden","true"),t.setAttribute("tabindex","-1"),t.style.cssText="position:fixed;width:0;height:0;border:0;left:-9999px;top:0;visibility:hidden;",document.body.appendChild(t);let n=!1;const a=()=>{n||(n=!0,setTimeout(()=>t.remove(),500))},o=t.contentWindow.document;o.open(),o.write(e),o.close();const s=()=>{const e=t.contentWindow;try{e.addEventListener("afterprint",a,{once:!0}),e.focus(),e.print(),setTimeout(a,6e4)}catch(e){console.error("Export print failed",e),t.remove()}};"complete"===t.contentWindow.document.readyState?setTimeout(s,60):t.addEventListener("load",()=>setTimeout(s,60),{once:!0})}(d),a.classList.remove("active"),setTimeout(()=>a.remove(),300)}catch(e){console.error("Export failed",e),alert("Export failed. Check console for details."),a.remove()}finally{T.disabled=!1,T.innerHTML=n}}(n)})})}):T.remove());const de={page:{title:"Current Page",desc:"Export only this section"},site:{title:"Entire Site",desc:"Export all documentation"}};function me(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 pe(e,t=!1){j=e.trim(),t&&(j?localStorage.setItem(M,j):localStorage.removeItem(M)),ue()}function ue(e=!1){E&&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}}),c=[];let l;for(;l=i.nextNode();){const e=l.nodeValue.toLowerCase();o.some(t=>e.includes(t))&&c.push(l)}const d=new RegExp(`(${a.map(r).join("|")})`,"gi");c.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"})})}(E,j,{scrollToFirst:e})}if(w&&A){const e=()=>document.body.dataset.navCollapse||"overlay",t=()=>window.innerWidth<=960||"overlay"===e(),n=()=>{A.classList.remove("mobile-open"),document.body.classList.remove("menu-open"),w.setAttribute("aria-expanded","false")};window.innerWidth>960&&"overlay"!==e()&&w.setAttribute("aria-expanded","true"),w.addEventListener("click",()=>{if(window.innerWidth>960&&"overlay"!==e()){const e=document.body.classList.toggle("nav-collapsed");return void w.setAttribute("aria-expanded",String(!e))}const t=A.classList.toggle("mobile-open");document.body.classList.toggle("menu-open",t),w.setAttribute("aria-expanded",String(t))}),x.addEventListener("click",e=>{if(!t())return;const a=e.target.closest(".nav-item, .nav-leaf, .nav-parent");a&&(a.classList.contains("nav-item")||a.classList.contains("nav-leaf"))&&n()}),document.addEventListener("click",e=>{t()&&A.classList.contains("mobile-open")&&!A.contains(e.target)&&!w.contains(e.target)&&n()})}const ve=document.getElementById("brandHome");ve&&ve.addEventListener("click",e=>{e.preventDefault(),O(t)});