@commentray/render 0.1.0 → 0.1.2

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 (59) hide show
  1. package/dist/block-stretch-layout.d.ts.map +1 -1
  2. package/dist/block-stretch-layout.js +2 -1
  3. package/dist/block-stretch-layout.js.map +1 -1
  4. package/dist/browse-page-slug.d.ts +6 -1
  5. package/dist/browse-page-slug.d.ts.map +1 -1
  6. package/dist/browse-page-slug.js +6 -1
  7. package/dist/browse-page-slug.js.map +1 -1
  8. package/dist/browse-pair-html-test-fixtures.d.ts +10 -0
  9. package/dist/browse-pair-html-test-fixtures.d.ts.map +1 -0
  10. package/dist/browse-pair-html-test-fixtures.js +19 -0
  11. package/dist/browse-pair-html-test-fixtures.js.map +1 -0
  12. package/dist/build-commentray-nav-search.d.ts +4 -4
  13. package/dist/build-commentray-nav-search.js +1 -1
  14. package/dist/code-browser-block-rays.d.ts +29 -0
  15. package/dist/code-browser-block-rays.d.ts.map +1 -1
  16. package/dist/code-browser-block-rays.js +120 -0
  17. package/dist/code-browser-block-rays.js.map +1 -1
  18. package/dist/code-browser-client.bundle.js +11 -11
  19. package/dist/code-browser-client.js +80 -27
  20. package/dist/code-browser-client.js.map +1 -1
  21. package/dist/code-browser-pair-nav.d.ts +3 -3
  22. package/dist/code-browser-pair-nav.d.ts.map +1 -1
  23. package/dist/code-browser-pair-nav.js +25 -13
  24. package/dist/code-browser-pair-nav.js.map +1 -1
  25. package/dist/code-browser.d.ts +18 -4
  26. package/dist/code-browser.d.ts.map +1 -1
  27. package/dist/code-browser.js +66 -269
  28. package/dist/code-browser.js.map +1 -1
  29. package/dist/commentray-anchor-viewport-probe.d.ts +9 -0
  30. package/dist/commentray-anchor-viewport-probe.d.ts.map +1 -0
  31. package/dist/commentray-anchor-viewport-probe.js +13 -0
  32. package/dist/commentray-anchor-viewport-probe.js.map +1 -0
  33. package/dist/commentray-preview-html.d.ts +13 -0
  34. package/dist/commentray-preview-html.d.ts.map +1 -0
  35. package/dist/commentray-preview-html.js +12 -0
  36. package/dist/commentray-preview-html.js.map +1 -0
  37. package/dist/companion-markdown-preview-entry.d.ts +7 -0
  38. package/dist/companion-markdown-preview-entry.d.ts.map +1 -0
  39. package/dist/companion-markdown-preview-entry.js +6 -0
  40. package/dist/companion-markdown-preview-entry.js.map +1 -0
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +2 -0
  44. package/dist/index.js.map +1 -1
  45. package/dist/inject-md-line-anchors.d.ts +18 -0
  46. package/dist/inject-md-line-anchors.d.ts.map +1 -0
  47. package/dist/inject-md-line-anchors.js +250 -0
  48. package/dist/inject-md-line-anchors.js.map +1 -0
  49. package/dist/inline-favicon.js +15 -15
  50. package/dist/markdown-pipeline.d.ts +5 -0
  51. package/dist/markdown-pipeline.d.ts.map +1 -1
  52. package/dist/markdown-pipeline.js +40 -0
  53. package/dist/markdown-pipeline.js.map +1 -1
  54. package/package.json +6 -2
  55. package/dist/side-by-side-layout.css +0 -58
  56. package/dist/side-by-side-layout.embedded.d.ts +0 -3
  57. package/dist/side-by-side-layout.embedded.d.ts.map +0 -1
  58. package/dist/side-by-side-layout.embedded.js +0 -3
  59. package/dist/side-by-side-layout.embedded.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import { FuzzySearcher, PrefixSearcher, Query, SearcherFactory, SubstringSearcher, } from "@m31coding/fuzzy-search";
2
- import { activeBlockIdForViewport, clampViewportYToGutterLocal, codeLineDomIndex0, gutterRayBezierPaths, sortBlockLinksBySource, } from "./code-browser-block-rays.js";
2
+ import { activeBlockIdForCommentrayLine0, activeBlockIdForViewport, clampViewportYToGutterLocal, codeLineDomIndex0, dedupeBlockScrollLinksById, gutterRayBezierPaths, maxRenderableCommentaryContentBottomViewport, nextBlockLinkInCommentrayOrder, sortBlockLinksBySource, } from "./code-browser-block-rays.js";
3
3
  import { mirroredScrollTop, pickCommentrayLineForSourceScroll, pickSourceLine0ForCommentrayScroll, } from "./code-browser-scroll-sync.js";
4
+ import { maxCommentrayAnchorLine0AtOrAboveViewportY } from "./commentray-anchor-viewport-probe.js";
4
5
  import { decodeBase64Utf8 } from "./code-browser-encoding.js";
5
6
  import { readEmbeddedRawB64Strings } from "./code-browser-embedded-payload.js";
6
7
  import { escapeHtmlHighlightingSearchTokens, filterPairsByDocumentedTreeQuery, findOrderedTokenSpans, lineAtIndex, offsetToLineIndex, pathRowsFromDocumentedPairs, tokenizeQuery, uniqueSourceFilePreviewRows, } from "./code-browser-search.js";
@@ -700,11 +701,22 @@ function parseScrollBlockLinksFromShell(b64) {
700
701
  typeof o.commentrayLine === "number" &&
701
702
  typeof o.sourceStart === "number" &&
702
703
  typeof o.sourceEnd === "number") {
704
+ const mvRaw = o.markerViewportHalfOpen1Based;
705
+ const mv = typeof mvRaw === "object" &&
706
+ mvRaw !== null &&
707
+ typeof mvRaw.lo === "number" &&
708
+ typeof mvRaw.hiExclusive === "number"
709
+ ? {
710
+ lo: mvRaw.lo,
711
+ hiExclusive: mvRaw.hiExclusive,
712
+ }
713
+ : { lo: o.sourceStart, hiExclusive: o.sourceEnd + 1 };
703
714
  out.push({
704
715
  id: o.id,
705
716
  commentrayLine: o.commentrayLine,
706
717
  sourceStart: o.sourceStart,
707
718
  sourceEnd: o.sourceEnd,
719
+ markerViewportHalfOpen1Based: mv,
708
720
  });
709
721
  }
710
722
  }
@@ -730,19 +742,15 @@ function readCommentrayLine0FromAnchor(el) {
730
742
  return null;
731
743
  return Number(lineAttr);
732
744
  }
733
- /** Greatest marker line whose anchor sits at or above viewport Y `y`. */
734
745
  function bestCommentrayAnchorLine0AtOrAboveY(anchors, y) {
735
- let best = 0;
746
+ const readings = [];
736
747
  for (const a of anchors) {
737
748
  const line0 = readCommentrayLine0FromAnchor(a);
738
749
  if (line0 === null)
739
750
  continue;
740
- if (a.getBoundingClientRect().top <= y + 1 + 1e-3)
741
- best = line0;
742
- else
743
- break;
751
+ readings.push({ line0, top: a.getBoundingClientRect().top });
744
752
  }
745
- return best;
753
+ return maxCommentrayAnchorLine0AtOrAboveViewportY(readings, y);
746
754
  }
747
755
  function lastCommentrayAnchorLine0(anchors) {
748
756
  const last = anchors[anchors.length - 1];
@@ -815,7 +823,8 @@ function probeCommentrayLine0FromDoc(docPane) {
815
823
  if (paneScrollNearEnd(docPane))
816
824
  return lastCommentrayAnchorLine0(anchors);
817
825
  const dr = docPane.getBoundingClientRect();
818
- const y = dr.top + docPane.clientTop + 2;
826
+ /** Same band as the root-scroll branch: a few px below the pane top so block anchors sit inside `top <= y` while their prose is what the reader sees first. */
827
+ const y = dr.top + docPane.clientTop + Math.max(2, Math.min(40, docPane.clientHeight * 0.15));
819
828
  return bestCommentrayAnchorLine0AtOrAboveY(anchors, y);
820
829
  }
821
830
  function pageBreakPullEnabled() {
@@ -1015,16 +1024,28 @@ function centerYInViewport(el) {
1015
1024
  function codeLineHighlightCenterYViewport(lineEl) {
1016
1025
  return centerYInViewport(lineEl);
1017
1026
  }
1018
- function commentaryBandEndYViewport(docScrollEl, next, docTop) {
1027
+ function commentaryBandEndYViewport(docScrollEl, next, docTop, clipThroughPageBreakGaps) {
1019
1028
  if (next) {
1020
1029
  const nextEl = document.getElementById(`commentray-block-${next.id}`);
1021
- return nextEl ? nextEl.getBoundingClientRect().top - 3 : centerYInViewport(docTop);
1030
+ if (!nextEl)
1031
+ return centerYInViewport(docTop);
1032
+ const nextTop = nextEl.getBoundingClientRect().top - 3;
1033
+ if (!clipThroughPageBreakGaps)
1034
+ return nextTop;
1035
+ const docBandTop = docTop.getBoundingClientRect().top + 4;
1036
+ const contentBottom = maxRenderableCommentaryContentBottomViewport(docScrollEl, docTop, nextEl);
1037
+ return Math.min(nextTop, Math.max(docBandTop, contentBottom));
1022
1038
  }
1023
1039
  const dr = docScrollEl.getBoundingClientRect();
1024
1040
  let bottom = dr.bottom - 4;
1025
1041
  const lastKid = docScrollEl.children[docScrollEl.children.length - 1];
1026
1042
  if (lastKid)
1027
1043
  bottom = Math.min(bottom, lastKid.getBoundingClientRect().bottom - 4);
1044
+ if (clipThroughPageBreakGaps) {
1045
+ const docBandTop = docTop.getBoundingClientRect().top + 4;
1046
+ const contentBottom = maxRenderableCommentaryContentBottomViewport(docScrollEl, docTop, null);
1047
+ bottom = Math.min(bottom, Math.max(docBandTop, contentBottom));
1048
+ }
1028
1049
  return bottom;
1029
1050
  }
1030
1051
  function sourceAnchorIndexFromId(id, prefix) {
@@ -1087,7 +1108,7 @@ function subscribeBlockRayRedraw(gutter, codePane, docScrollEl, scheduleDraw) {
1087
1108
  ro.observe(shell);
1088
1109
  }
1089
1110
  function drawBlockRaysIntoSvg(svg, gutter, docScrollEl, getLinks, probeTopSourceLine1Based, lineIdPrefix) {
1090
- const links = getLinks();
1111
+ const links = dedupeBlockScrollLinksById(getLinks());
1091
1112
  const sorted = sortBlockLinksBySource(links);
1092
1113
  const gutterRect = gutter.getBoundingClientRect();
1093
1114
  const w = gutterRect.width;
@@ -1096,7 +1117,11 @@ function drawBlockRaysIntoSvg(svg, gutter, docScrollEl, getLinks, probeTopSource
1096
1117
  svg.replaceChildren();
1097
1118
  return;
1098
1119
  }
1099
- const activeId = activeBlockIdForViewport(links, probeTopSourceLine1Based());
1120
+ /** Doc-aligned active block matches visible commentary; code-only probe can lag in page gaps. */
1121
+ const activeId = docScrollEl.querySelector(".commentray-block-anchor") !== null
1122
+ ? activeBlockIdForCommentrayLine0(links, probeCommentrayLine0FromDoc(docScrollEl))
1123
+ : activeBlockIdForViewport(links, probeTopSourceLine1Based());
1124
+ const clipGutterRaysThroughPageBreakGaps = pageBreakPullEnabled();
1100
1125
  svg.setAttribute("viewBox", `0 0 ${String(w)} ${String(h)}`);
1101
1126
  svg.setAttribute("preserveAspectRatio", "none");
1102
1127
  const parts = [];
@@ -1108,7 +1133,7 @@ function drawBlockRaysIntoSvg(svg, gutter, docScrollEl, getLinks, probeTopSource
1108
1133
  const link = sorted[i];
1109
1134
  if (!link)
1110
1135
  continue;
1111
- const next = sorted[i + 1];
1136
+ const next = nextBlockLinkInCommentrayOrder(links, link);
1112
1137
  const i0 = codeLineDomIndex0(link.sourceStart);
1113
1138
  const i1 = codeLineDomIndex0(link.sourceEnd);
1114
1139
  const codeTop = document.getElementById(`${lineIdPrefix}${String(i0)}`) ??
@@ -1118,7 +1143,7 @@ function drawBlockRaysIntoSvg(svg, gutter, docScrollEl, getLinks, probeTopSource
1118
1143
  const docTop = document.getElementById(`commentray-block-${link.id}`);
1119
1144
  if (!codeTop || !codeBot || !docTop)
1120
1145
  continue;
1121
- const docEndYViewport = commentaryBandEndYViewport(docScrollEl, next, docTop);
1146
+ const docEndYViewport = commentaryBandEndYViewport(docScrollEl, next, docTop, clipGutterRaysThroughPageBreakGaps);
1122
1147
  const yCodeTop = codeLineHighlightCenterYViewport(codeTop);
1123
1148
  const yCodeBot = codeLineHighlightCenterYViewport(codeBot);
1124
1149
  const yDocTop = docTop.getBoundingClientRect().top + 2;
@@ -1152,8 +1177,8 @@ function drawBlockRaysIntoSvg(svg, gutter, docScrollEl, getLinks, probeTopSource
1152
1177
  }
1153
1178
  /**
1154
1179
  * Splines in the gutter between each block’s source range and its commentary band (dual pane,
1155
- * index-backed blocks). Emphasizes the block aligned with the current source viewport; clamps
1156
- * off-screen endpoints so readers see which way to scroll.
1180
+ * index-backed blocks). Emphasizes the block aligned with the **doc** viewport when block anchors
1181
+ * exist; otherwise the source viewport. Clamps off-screen endpoints so readers see which way to scroll.
1157
1182
  *
1158
1183
  * @returns Request a redraw after DOM changes that do not resize the panes (e.g. multi-angle body swap).
1159
1184
  */
@@ -1493,12 +1518,13 @@ function wireDocumentedFilesTree() {
1493
1518
  if (!(hub instanceof HTMLDetailsElement) || !(treeHost instanceof HTMLElement)) {
1494
1519
  return;
1495
1520
  }
1521
+ const detailsHub = hub;
1496
1522
  const treeMount = treeHost;
1497
- const jsonUrl = hub.getAttribute("data-nav-json-url")?.trim() ?? "";
1523
+ const jsonUrl = detailsHub.getAttribute("data-nav-json-url")?.trim() ?? "";
1498
1524
  const embeddedB64 = shell?.getAttribute("data-documented-pairs-b64")?.trim() ?? "";
1499
1525
  if (jsonUrl.length === 0 && embeddedB64.length === 0)
1500
1526
  return;
1501
- const placeDocHubFlyout = wireDocumentedFilesTreeMobileFlyout(hub);
1527
+ const placeDocHubFlyout = wireDocumentedFilesTreeMobileFlyout(detailsHub);
1502
1528
  const ensureLoaded = loadDocumentedPairs(jsonUrl, embeddedB64);
1503
1529
  let cachedPairs = null;
1504
1530
  function applyFilterAndRender() {
@@ -1521,21 +1547,31 @@ function wireDocumentedFilesTree() {
1521
1547
  '<p class="nav-rail__doc-hub-hint" role="alert">Could not load the file list.</p>';
1522
1548
  }
1523
1549
  }
1524
- hub.addEventListener("toggle", () => {
1550
+ detailsHub.addEventListener("toggle", () => {
1525
1551
  placeDocHubFlyout();
1526
- if (hub.open) {
1552
+ if (detailsHub.open) {
1527
1553
  globalThis.requestAnimationFrame(() => {
1528
1554
  placeDocHubFlyout();
1529
1555
  focusDocumentedFilesFilterInput();
1530
1556
  });
1531
1557
  }
1532
- if (!hub.open)
1558
+ if (!detailsHub.open)
1533
1559
  return;
1534
1560
  void hydrateTree();
1535
1561
  });
1562
+ function onDocumentedFilesHubEscape(ev) {
1563
+ if (!detailsHub.open || ev.key !== "Escape")
1564
+ return;
1565
+ ev.preventDefault();
1566
+ detailsHub.open = false;
1567
+ const sum = detailsHub.querySelector("summary");
1568
+ if (sum instanceof HTMLElement)
1569
+ sum.focus({ preventScroll: true });
1570
+ }
1571
+ document.addEventListener("keydown", onDocumentedFilesHubEscape, true);
1536
1572
  if (filterInput instanceof HTMLInputElement) {
1537
1573
  filterInput.addEventListener("input", () => {
1538
- if (!hub.open || cachedPairs === null)
1574
+ if (!detailsHub.open || cachedPairs === null)
1539
1575
  return;
1540
1576
  applyFilterAndRender();
1541
1577
  });
@@ -2333,7 +2369,7 @@ function humaneBrowseAliasPathForSource(sourcePath) {
2333
2369
  return sourcePath
2334
2370
  .split("/")
2335
2371
  .filter((seg) => seg.length > 0)
2336
- .map((seg) => encodeURIComponent(seg))
2372
+ .map((seg) => seg.startsWith(".") ? `%2E${encodeURIComponent(seg.slice(1))}` : encodeURIComponent(seg))
2337
2373
  .join("/");
2338
2374
  }
2339
2375
  function companionStemFromCommentrayPath(commentrayPath) {
@@ -2418,12 +2454,29 @@ function maybeBackfillAddressBarWithHumanePairLink() {
2418
2454
  normalizeDocumentationHomeHrefForCurrentPath();
2419
2455
  if (!shellEligibleForHumaneBackfill(shell, pathname))
2420
2456
  return;
2421
- const nextPath = nextHumaneBrowsePathForShell(shell, pathname);
2422
- if (nextPath === null)
2423
- return;
2424
2457
  const beforeHref = globalThis.location.href;
2425
2458
  absolutizeNavJsonUrls(shell, beforeHref);
2426
2459
  normalizePairBrowseHrefForCurrentPath(shell, pathname);
2460
+ /** Prefer the same `staticBrowseUrl` the static build put on `#shell` (slug or `…/index.html` shims). */
2461
+ const canonicalBrowsePathname = (() => {
2462
+ const raw = shell.getAttribute("data-commentray-pair-browse-href")?.trim() ?? "";
2463
+ if (raw.length === 0)
2464
+ return null;
2465
+ try {
2466
+ const u = new URL(raw, globalThis.location.href);
2467
+ if (u.origin !== globalThis.location.origin)
2468
+ return null;
2469
+ if (!u.pathname.includes("/browse/"))
2470
+ return null;
2471
+ return u.pathname;
2472
+ }
2473
+ catch {
2474
+ return null;
2475
+ }
2476
+ })();
2477
+ const nextPath = canonicalBrowsePathname ?? nextHumaneBrowsePathForShell(shell, pathname);
2478
+ if (nextPath === null)
2479
+ return;
2427
2480
  globalThis.history.replaceState(null, "", `${nextPath}${globalThis.location.search}${globalThis.location.hash}`);
2428
2481
  }
2429
2482
  function permalinkHashSuffixFromUi() {