@ecency/render-helper 2.5.24 → 2.5.25

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.
@@ -12,6 +12,16 @@ interface Entry {
12
12
  interface RenderOptions {
13
13
  /** When true, video embeds (3Speak, YouTube, etc.) render as iframes directly without a play button overlay. */
14
14
  embedVideosDirectly?: boolean;
15
+ /**
16
+ * When true, auto-linkified `@user` and `#tag` chips render as inert `<span>`
17
+ * instead of `<a>`. Only affects the non-app render path, which is the only
18
+ * one that emits these chips. For consumers that have no profile or tag
19
+ * routes to send a reader to (a single-author self-hosted blog), an `<a>`
20
+ * pointing at `/@user` or `/trending/tag` is a dead link that is still
21
+ * focusable, announced as a link, and crawlable. Classes are unchanged so
22
+ * existing chip styling still applies.
23
+ */
24
+ inertAuthorAndTagChips?: boolean;
15
25
  }
16
26
 
17
27
  /**
@@ -1763,6 +1763,9 @@ function linkify(content, forApp, renderOptions) {
1763
1763
  const tag2 = tag.trim().substring(1);
1764
1764
  const tagLower = tag2.toLowerCase();
1765
1765
  if (!forApp) {
1766
+ if (renderOptions?.inertAuthorAndTagChips) {
1767
+ return `${preceding}<span class="er-tag er-tag-link">${tag.trim()}</span>`;
1768
+ }
1766
1769
  return `${preceding}<a class="er-tag er-tag-link" href="/trending/${tagLower}">${tag.trim()}</a>`;
1767
1770
  }
1768
1771
  return `${preceding}<a class="markdown-tag-link" data-tag="${tagLower}">${tag.trim()}</a>`;
@@ -1776,7 +1779,8 @@ function linkify(content, forApp, renderOptions) {
1776
1779
  if (userLower.indexOf("/") === -1 && isValidUsername(user)) {
1777
1780
  if (!forApp) {
1778
1781
  const avatarSrc = `${getProxyBase()}/u/${userLower}/avatar/small`;
1779
- const html = `${preceedings}<a class="er-author er-author-link" href="/@${userLower}"><img class="er-author-link-image" src="${avatarSrc}" alt="${userLower}"/>@${userLower}</a>`;
1782
+ const inner = `<img class="er-author-link-image" src="${avatarSrc}" alt="${userLower}"/>@${userLower}`;
1783
+ const html = renderOptions?.inertAuthorAndTagChips ? `${preceedings}<span class="er-author er-author-link">${inner}</span>` : `${preceedings}<a class="er-author er-author-link" href="/@${userLower}">${inner}</a>`;
1780
1784
  const placeholder = `\u200C${authorPlaceholders.length}\u200C`;
1781
1785
  authorPlaceholders.push({ placeholder, html });
1782
1786
  return placeholder;
@@ -1841,6 +1845,19 @@ function hasAncestor(node, tagNames) {
1841
1845
  }
1842
1846
  return false;
1843
1847
  }
1848
+ var CHIP_CLASSES = ["er-author-link", "er-tag-link"];
1849
+ function hasChipAncestor(node) {
1850
+ let current = node.parentNode;
1851
+ while (current) {
1852
+ const el = current;
1853
+ const className = typeof el.getAttribute === "function" ? el.getAttribute("class") : null;
1854
+ if (className && className.split(/\s+/).some((token) => CHIP_CLASSES.includes(token))) {
1855
+ return true;
1856
+ }
1857
+ current = current.parentNode;
1858
+ }
1859
+ return false;
1860
+ }
1844
1861
  function text(node, forApp, renderOptions) {
1845
1862
  if (!node || !node.parentNode) {
1846
1863
  return;
@@ -1848,8 +1865,11 @@ function text(node, forApp, renderOptions) {
1848
1865
  if (hasAncestor(node, ["a", "code", "pre"])) {
1849
1866
  return;
1850
1867
  }
1868
+ if (renderOptions?.inertAuthorAndTagChips && hasChipAncestor(node)) {
1869
+ return;
1870
+ }
1851
1871
  const nodeValue = node.nodeValue || "";
1852
- const linkified = linkify(nodeValue, forApp);
1872
+ const linkified = linkify(nodeValue, forApp, renderOptions);
1853
1873
  if (linkified !== nodeValue) {
1854
1874
  const doc = DOMParser.parseFromString(
1855
1875
  `<span class="wr">${linkified}</span>`,
@@ -1937,7 +1957,7 @@ function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, p
1937
1957
  iframe(child, parentDomain, forApp, renderOptions);
1938
1958
  }
1939
1959
  if (child.nodeName === "#text") {
1940
- text(child, forApp);
1960
+ text(child, forApp, renderOptions);
1941
1961
  }
1942
1962
  if (child.nodeName.toLowerCase() === "img") {
1943
1963
  img(child, state, forApp);
@@ -2141,7 +2161,7 @@ function markdown2Html(obj, forApp = true, _webp = false, parentDomain = "ecency
2141
2161
  logIfSlow(performance.now() - t02, `body_len=${obj.length}`);
2142
2162
  return res2;
2143
2163
  }
2144
- const key = `${makeEntryCacheKey(obj)}-md-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}${renderOptions?.embedVideosDirectly ? "-embed" : ""}`;
2164
+ const key = `${makeEntryCacheKey(obj)}-md-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}${renderOptions?.embedVideosDirectly ? "-embed" : ""}${renderOptions?.inertAuthorAndTagChips ? "-inert" : ""}`;
2145
2165
  const item = cacheGet(key);
2146
2166
  if (item) {
2147
2167
  return item;