@dogsbay/docs-layout 0.2.0-beta.74 → 0.2.0-beta.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dogsbay/docs-layout",
3
- "version": "0.2.0-beta.74",
3
+ "version": "0.2.0-beta.75",
4
4
  "description": "Standard documentation layout components for Dogsbay",
5
5
  "type": "module",
6
6
  "exports": {
@@ -29,8 +29,8 @@
29
29
  "./json-ld": "./src/json-ld.ts"
30
30
  },
31
31
  "dependencies": {
32
- "@dogsbay/ui": "0.2.0-beta.74",
33
- "@dogsbay/primitives": "0.2.0-beta.74"
32
+ "@dogsbay/ui": "0.2.0-beta.75",
33
+ "@dogsbay/primitives": "0.2.0-beta.75"
34
34
  },
35
35
  "devDependencies": {
36
36
  "vitest": "^3.0.0"
@@ -376,7 +376,16 @@ export function buildFacetTree(
376
376
 
377
377
  const slashEncoded = entries.some((e) => e.value.includes("/"));
378
378
  if (slashEncoded) {
379
- return buildSlashTree(facetName, entries, options.display);
379
+ // When nav is supplied, derive document-order. Otherwise fall
380
+ // back to count-desc + alpha. The map keys are full cumulative
381
+ // paths (matching Pagefind entry values 1:1).
382
+ const navOrder = options.nav
383
+ ? buildNavOrderMap(
384
+ options.nav,
385
+ new Set(entries.map((e) => e.value)),
386
+ )
387
+ : undefined;
388
+ return buildSlashTree(facetName, entries, options.display, navOrder);
380
389
  }
381
390
  return buildSegmentTree(facetName, entries, options.nav, options.display);
382
391
  }
@@ -387,11 +396,20 @@ export function buildFacetTree(
387
396
  * nodes. Synthetic intermediate nodes (a path component used by a
388
397
  * descendant but absent from `entries`) get `hasValue: false` and
389
398
  * `count: 0` so they render but don't contribute to selection.
399
+ *
400
+ * Sort order at every depth:
401
+ * 1. `navOrder` index if present (document order from nav.json).
402
+ * Without this, deep API-reference subtrees with high page
403
+ * counts would dominate the top of the tree even though they
404
+ * sit at the BOTTOM of the rendered nav.
405
+ * 2. Count desc (more pages → higher).
406
+ * 3. Alpha (final tiebreak).
390
407
  */
391
408
  function buildSlashTree(
392
409
  facetName: string,
393
410
  entries: FacetEntry[],
394
411
  display?: TaxonomyDisplayMap,
412
+ navOrder?: Map<string, number>,
395
413
  ): FacetTreeNode[] {
396
414
  // Trie keyed by full slash-path. Children are tracked on each
397
415
  // node's own `children` array (built unsorted in pass 1, sorted
@@ -428,26 +446,86 @@ function buildSlashTree(
428
446
  }
429
447
  }
430
448
 
431
- // Pass 2: sort children at every depth. Mirrors sortFacetEntries
432
- // (count desc, alpha tiebreaker).
449
+ // Pass 2: sort children at every depth.
450
+ const cmp = (a: FacetTreeNode, b: FacetTreeNode): number => {
451
+ if (navOrder) {
452
+ const oa = navOrder.get(a.value);
453
+ const ob = navOrder.get(b.value);
454
+ if (oa !== undefined && ob !== undefined && oa !== ob) return oa - ob;
455
+ // Nodes IN the nav sort before nodes not in nav
456
+ if (oa !== undefined && ob === undefined) return -1;
457
+ if (oa === undefined && ob !== undefined) return 1;
458
+ }
459
+ if (a.count !== b.count) return b.count - a.count;
460
+ return a.value.localeCompare(b.value);
461
+ };
433
462
  const sortChildren = (node: FacetTreeNode): void => {
434
463
  if (node.children.length === 0) return;
435
- node.children.sort((a, b) => {
436
- if (a.count !== b.count) return b.count - a.count;
437
- return a.value.localeCompare(b.value);
438
- });
464
+ node.children.sort(cmp);
439
465
  for (const c of node.children) sortChildren(c);
440
466
  };
441
467
 
442
468
  const rootList = rootValues.map((v) => byPath.get(v)!);
443
- rootList.sort((a, b) => {
444
- if (a.count !== b.count) return b.count - a.count;
445
- return a.value.localeCompare(b.value);
446
- });
469
+ rootList.sort(cmp);
447
470
  for (const r of rootList) sortChildren(r);
448
471
  return rootList;
449
472
  }
450
473
 
474
+ /**
475
+ * Walk `nav` and assign each cumulative slash-path (matching a
476
+ * Pagefind facet value in `knownValues`) a sequential index in
477
+ * first-encounter order. Used by `buildSlashTree` to sort children
478
+ * in document order rather than count-desc.
479
+ *
480
+ * Auto-detects and strips a leading basePath: for each nav leaf, we
481
+ * try each starting offset of the href's parent segments. The first
482
+ * offset whose initial cumulative path is in `knownValues` is the
483
+ * "real" content root; the dropped prefix is the basePath. This way
484
+ * the helper works on any site without needing the basePath threaded
485
+ * through explicitly.
486
+ *
487
+ * Hrefs whose parents don't match anything in `knownValues` at any
488
+ * offset are skipped (e.g. top-level pages with no auto-derived
489
+ * category, or pages outside the indexed corpus).
490
+ *
491
+ * Exported for use by `buildFacetTree`. Pure — no I/O.
492
+ */
493
+ export function buildNavOrderMap(
494
+ nav: NavLike[],
495
+ knownValues: Set<string>,
496
+ ): Map<string, number> {
497
+ const order = new Map<string, number>();
498
+ let n = 0;
499
+ const walk = (items: NavLike[]): void => {
500
+ for (const item of items) {
501
+ if (item.href) {
502
+ const parents = hrefToCategorySegments(item.href);
503
+ // Try each starting offset; first one whose initial cumulative
504
+ // path is in knownValues wins (auto-detects basePath).
505
+ for (let start = 0; start < parents.length; start++) {
506
+ const first = parents[start];
507
+ if (!knownValues.has(first)) continue;
508
+ // Record all cumulative paths starting from `start` that
509
+ // exist in knownValues. The check at line below filters out
510
+ // synthetic intermediates the corpus didn't tag (e.g. a
511
+ // user-supplied frontmatter category that skipped a level).
512
+ let cumulative = "";
513
+ for (let i = start; i < parents.length; i++) {
514
+ cumulative = cumulative ? `${cumulative}/${parents[i]}` : parents[i];
515
+ if (knownValues.has(cumulative) && !order.has(cumulative)) {
516
+ order.set(cumulative, n++);
517
+ }
518
+ }
519
+ break;
520
+ }
521
+ }
522
+ if (item.children && item.children.length > 0) walk(item.children);
523
+ }
524
+ };
525
+ walk(nav);
526
+ return order;
527
+ }
528
+
451
529
  /**
452
530
  * Segment-encoded tree builder. Walks `nav` to discover the
453
531
  * canonical document hierarchy, then attaches Pagefind counts to