@curvenote/theme-ui 1.0.1 → 2.0.0

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 (166) hide show
  1. package/dist/components/layout/app-layout.d.ts +1 -1
  2. package/dist/components/layout/app-layout.d.ts.map +1 -1
  3. package/dist/components/layout/app-layout.js +6 -5
  4. package/dist/components/layout/app-sidebar.d.ts.map +1 -1
  5. package/dist/components/layout/app-sidebar.js +10 -6
  6. package/dist/components/layout/article-layout.d.ts +1 -1
  7. package/dist/components/layout/article-layout.d.ts.map +1 -1
  8. package/dist/components/layout/article-layout.js +6 -7
  9. package/dist/components/layout/header.js +1 -1
  10. package/dist/components/layout/index.d.ts +1 -0
  11. package/dist/components/layout/index.d.ts.map +1 -1
  12. package/dist/components/layout/index.js +1 -0
  13. package/dist/components/layout/loading-bar.d.ts +8 -1
  14. package/dist/components/layout/loading-bar.d.ts.map +1 -1
  15. package/dist/components/layout/loading-bar.js +12 -4
  16. package/dist/components/layout/site-header.d.ts +6 -1
  17. package/dist/components/layout/site-header.d.ts.map +1 -1
  18. package/dist/components/layout/site-header.js +7 -4
  19. package/dist/components/layout/types.d.ts +6 -0
  20. package/dist/components/layout/types.d.ts.map +1 -1
  21. package/dist/components/providers/analytics-boundary.d.ts +18 -0
  22. package/dist/components/providers/analytics-boundary.d.ts.map +1 -0
  23. package/dist/components/providers/analytics-boundary.js +53 -0
  24. package/dist/components/providers/analytics-dev-log.d.ts +11 -0
  25. package/dist/components/providers/analytics-dev-log.d.ts.map +1 -0
  26. package/dist/components/providers/analytics-dev-log.js +46 -0
  27. package/dist/components/providers/analytics-dev-tray.d.ts +2 -0
  28. package/dist/components/providers/analytics-dev-tray.d.ts.map +1 -0
  29. package/dist/components/providers/analytics-dev-tray.js +56 -0
  30. package/dist/components/providers/article-versions-provider.d.ts +19 -0
  31. package/dist/components/providers/article-versions-provider.d.ts.map +1 -0
  32. package/dist/components/providers/article-versions-provider.js +13 -0
  33. package/dist/components/providers/curvenote-providers.d.ts +12 -13
  34. package/dist/components/providers/curvenote-providers.d.ts.map +1 -1
  35. package/dist/components/providers/curvenote-providers.js +20 -27
  36. package/dist/components/providers/design.d.ts +3 -33
  37. package/dist/components/providers/design.d.ts.map +1 -1
  38. package/dist/components/providers/design.js +64 -21
  39. package/dist/components/providers/index.d.ts +4 -2
  40. package/dist/components/providers/index.d.ts.map +1 -1
  41. package/dist/components/providers/index.js +3 -1
  42. package/dist/components/providers/lightbox-provider.d.ts +8 -0
  43. package/dist/components/providers/lightbox-provider.d.ts.map +1 -1
  44. package/dist/components/providers/lightbox-provider.js +28 -5
  45. package/dist/components/providers/overlay-depth.d.ts +16 -0
  46. package/dist/components/providers/overlay-depth.d.ts.map +1 -0
  47. package/dist/components/providers/overlay-depth.js +16 -0
  48. package/dist/components/providers/ping-boundary.d.ts +3 -1
  49. package/dist/components/providers/ping-boundary.d.ts.map +1 -1
  50. package/dist/components/providers/ping-boundary.js +7 -4
  51. package/dist/components/providers/platform-providers.d.ts +0 -7
  52. package/dist/components/providers/platform-providers.d.ts.map +1 -1
  53. package/dist/components/providers/platform-providers.js +0 -14
  54. package/dist/components/ui/abbreviations.d.ts +3 -1
  55. package/dist/components/ui/abbreviations.d.ts.map +1 -1
  56. package/dist/components/ui/abbreviations.js +13 -6
  57. package/dist/components/ui/accessibility-skip-to.d.ts +13 -3
  58. package/dist/components/ui/accessibility-skip-to.d.ts.map +1 -1
  59. package/dist/components/ui/accessibility-skip-to.js +4 -1
  60. package/dist/components/ui/arrow.d.ts +6 -0
  61. package/dist/components/ui/arrow.d.ts.map +1 -0
  62. package/dist/components/ui/arrow.js +15 -0
  63. package/dist/components/ui/article-actions-downloads.d.ts.map +1 -1
  64. package/dist/components/ui/article-actions-downloads.js +74 -7
  65. package/dist/components/ui/article-actions-dropdown.d.ts +2 -1
  66. package/dist/components/ui/article-actions-dropdown.d.ts.map +1 -1
  67. package/dist/components/ui/article-actions-dropdown.js +102 -21
  68. package/dist/components/ui/article-actions-share.d.ts +18 -0
  69. package/dist/components/ui/article-actions-share.d.ts.map +1 -1
  70. package/dist/components/ui/article-actions-share.js +38 -2
  71. package/dist/components/ui/article-actions.d.ts.map +1 -1
  72. package/dist/components/ui/article-actions.js +16 -3
  73. package/dist/components/ui/article-bar.d.ts +3 -1
  74. package/dist/components/ui/article-bar.d.ts.map +1 -1
  75. package/dist/components/ui/article-bar.js +19 -5
  76. package/dist/components/ui/article-card.js +1 -1
  77. package/dist/components/ui/author-popover.d.ts +2 -1
  78. package/dist/components/ui/author-popover.d.ts.map +1 -1
  79. package/dist/components/ui/author-popover.js +42 -7
  80. package/dist/components/ui/backmatter.d.ts.map +1 -1
  81. package/dist/components/ui/backmatter.js +23 -5
  82. package/dist/components/ui/banner.d.ts.map +1 -1
  83. package/dist/components/ui/banner.js +1 -1
  84. package/dist/components/ui/bibliography.d.ts +2 -2
  85. package/dist/components/ui/bibliography.d.ts.map +1 -1
  86. package/dist/components/ui/bibliography.js +10 -9
  87. package/dist/components/ui/button.d.ts +1 -1
  88. package/dist/components/ui/button.d.ts.map +1 -1
  89. package/dist/components/ui/button.js +2 -0
  90. package/dist/components/ui/calendar.d.ts +10 -0
  91. package/dist/components/ui/calendar.d.ts.map +1 -0
  92. package/dist/components/ui/calendar.js +85 -0
  93. package/dist/components/ui/citation.d.ts +2 -2
  94. package/dist/components/ui/citation.d.ts.map +1 -1
  95. package/dist/components/ui/citation.js +45 -15
  96. package/dist/components/ui/collapsible-backmatter-section.d.ts +28 -0
  97. package/dist/components/ui/collapsible-backmatter-section.d.ts.map +1 -0
  98. package/dist/components/ui/collapsible-backmatter-section.js +52 -0
  99. package/dist/components/ui/cookie-banner.d.ts +1 -1
  100. package/dist/components/ui/cookie-banner.d.ts.map +1 -1
  101. package/dist/components/ui/cookie-banner.js +9 -2
  102. package/dist/components/ui/document-outline.d.ts.map +1 -1
  103. package/dist/components/ui/document-outline.js +38 -20
  104. package/dist/components/ui/footer-links.d.ts.map +1 -1
  105. package/dist/components/ui/footer-links.js +4 -0
  106. package/dist/components/ui/frontmatter-badges.d.ts +15 -5
  107. package/dist/components/ui/frontmatter-badges.d.ts.map +1 -1
  108. package/dist/components/ui/frontmatter-badges.js +41 -15
  109. package/dist/components/ui/frontmatter.js +1 -1
  110. package/dist/components/ui/hash-link.d.ts +56 -0
  111. package/dist/components/ui/hash-link.d.ts.map +1 -0
  112. package/dist/components/ui/hash-link.js +83 -0
  113. package/dist/components/ui/image.d.ts +5 -2
  114. package/dist/components/ui/image.d.ts.map +1 -1
  115. package/dist/components/ui/image.js +10 -8
  116. package/dist/components/ui/index.d.ts +10 -2
  117. package/dist/components/ui/index.d.ts.map +1 -1
  118. package/dist/components/ui/index.js +9 -2
  119. package/dist/components/ui/keywords.d.ts +4 -0
  120. package/dist/components/ui/keywords.d.ts.map +1 -0
  121. package/dist/components/ui/keywords.js +18 -0
  122. package/dist/components/ui/label.d.ts +4 -0
  123. package/dist/components/ui/label.d.ts.map +1 -0
  124. package/dist/components/ui/label.js +6 -0
  125. package/dist/components/ui/license-description.d.ts.map +1 -1
  126. package/dist/components/ui/license-description.js +8 -5
  127. package/dist/components/ui/license-frontmatter.d.ts.map +1 -1
  128. package/dist/components/ui/license-frontmatter.js +14 -3
  129. package/dist/components/ui/lightbox-container.d.ts.map +1 -1
  130. package/dist/components/ui/lightbox-container.js +2 -2
  131. package/dist/components/ui/lightbox.d.ts +3 -1
  132. package/dist/components/ui/lightbox.d.ts.map +1 -1
  133. package/dist/components/ui/lightbox.js +155 -25
  134. package/dist/components/ui/link-card.d.ts +2 -1
  135. package/dist/components/ui/link-card.d.ts.map +1 -1
  136. package/dist/components/ui/link-card.js +2 -2
  137. package/dist/components/ui/pagination.d.ts +15 -0
  138. package/dist/components/ui/pagination.d.ts.map +1 -0
  139. package/dist/components/ui/pagination.js +31 -0
  140. package/dist/components/ui/popover.js +1 -1
  141. package/dist/components/ui/search.d.ts +2 -1
  142. package/dist/components/ui/search.d.ts.map +1 -1
  143. package/dist/components/ui/search.js +24 -8
  144. package/dist/components/ui/select.d.ts +16 -0
  145. package/dist/components/ui/select.d.ts.map +1 -0
  146. package/dist/components/ui/select.js +38 -0
  147. package/dist/components/ui/separator.js +1 -1
  148. package/dist/components/ui/sidebar.d.ts +2 -12
  149. package/dist/components/ui/sidebar.d.ts.map +1 -1
  150. package/dist/components/ui/sidebar.js +2 -34
  151. package/dist/components/ui/tooltip.d.ts +4 -2
  152. package/dist/components/ui/tooltip.d.ts.map +1 -1
  153. package/dist/components/ui/tooltip.js +5 -4
  154. package/dist/components/ui/types.d.ts +9 -8
  155. package/dist/components/ui/types.d.ts.map +1 -1
  156. package/dist/components/ui/types.js +1 -0
  157. package/dist/lib/dedupeAbbreviations.d.ts +10 -0
  158. package/dist/lib/dedupeAbbreviations.d.ts.map +1 -0
  159. package/dist/lib/dedupeAbbreviations.js +36 -0
  160. package/dist/lib/knownParts.d.ts +18 -0
  161. package/dist/lib/knownParts.d.ts.map +1 -1
  162. package/dist/lib/knownParts.js +38 -1
  163. package/package.json +12 -9
  164. package/dist/components/providers/types.d.ts +0 -30
  165. package/dist/components/providers/types.d.ts.map +0 -1
  166. package/dist/components/providers/types.js +0 -1
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useState } from 'react';
3
+ import { HashLink } from 'myst-to-react';
4
+ import { cn } from '../../lib/utils';
5
+ import { useAnalytics } from '../providers';
6
+ import { Button } from './button';
7
+ function trackBackmatterToggle(capture, { hidden, toggleLocation, sectionId, itemCount, itemLabel, }) {
8
+ capture(hidden ? 'content_backmatter_expanded' : 'content_backmatter_collapsed', {
9
+ surface: 'content',
10
+ sectionId,
11
+ toggleLocation,
12
+ itemCount,
13
+ itemLabel,
14
+ });
15
+ }
16
+ export function useCollapsibleBackmatter(totalCount, limit, defaultLimit = 15) {
17
+ const resolvedLimit = limit === true ? defaultLimit : typeof limit === 'number' ? limit : undefined;
18
+ const [hidden, setHidden] = useState(true);
19
+ const isCollapsible = !!resolvedLimit && totalCount > resolvedLimit;
20
+ const displayedCount = hidden && resolvedLimit ? resolvedLimit : totalCount;
21
+ return {
22
+ hidden,
23
+ toggle: () => setHidden((h) => !h),
24
+ displayedCount,
25
+ isCollapsible,
26
+ limit: resolvedLimit,
27
+ };
28
+ }
29
+ export function CollapsibleBackmatterHeader({ id, title, linkTitle, hidden, onToggle, isCollapsible, className, buttonClassName, }) {
30
+ const capture = useAnalytics();
31
+ return (_jsx("header", { className: cn('text-lg font-semibold text-stone-900 dark:text-white group', className), children: _jsxs("div", { className: "flex justify-between items-center", children: [_jsxs("span", { children: [title, _jsx(HashLink, { id: id, title: linkTitle, hover: true, className: "ml-2" })] }), isCollapsible && (_jsx(Button, { variant: "outline", onClick: () => {
32
+ trackBackmatterToggle(capture, {
33
+ hidden,
34
+ toggleLocation: 'header',
35
+ sectionId: id,
36
+ });
37
+ onToggle();
38
+ }, className: cn('text-xs', buttonClassName), children: hidden ? 'Show All' : 'Collapse' }))] }) }));
39
+ }
40
+ export function CollapsibleBackmatterFooterButton({ hidden, onToggle, totalCount, itemLabel, sectionId, className, as: Wrapper = 'div', }) {
41
+ const capture = useAnalytics();
42
+ return (_jsx(Wrapper, { className: className, children: _jsx(Button, { variant: "outline", onClick: () => {
43
+ trackBackmatterToggle(capture, {
44
+ hidden,
45
+ toggleLocation: 'footer',
46
+ sectionId: sectionId ?? itemLabel,
47
+ itemCount: totalCount,
48
+ itemLabel,
49
+ });
50
+ onToggle();
51
+ }, className: "text-xs", children: hidden ? `Show all ${totalCount} ${itemLabel}` : `Collapse ${itemLabel}` }) }));
52
+ }
@@ -3,6 +3,6 @@ interface CookieBannerProps {
3
3
  message?: string;
4
4
  privacyLink?: string;
5
5
  }
6
- export declare function CookieBanner({ className, message, privacyLink, }: CookieBannerProps): import("react/jsx-runtime").JSX.Element | null;
6
+ export declare function CookieBanner({ className, message: messageProp, privacyLink: privacyLinkProp, }: CookieBannerProps): import("react/jsx-runtime").JSX.Element | null;
7
7
  export {};
8
8
  //# sourceMappingURL=cookie-banner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cookie-banner.d.ts","sourceRoot":"","sources":["../../../src/components/ui/cookie-banner.tsx"],"names":[],"mappings":"AAIA,UAAU,iBAAiB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,OAAwI,EACxI,WAA2C,GAC5C,EAAE,iBAAiB,kDA8DnB"}
1
+ {"version":3,"file":"cookie-banner.d.ts","sourceRoot":"","sources":["../../../src/components/ui/cookie-banner.tsx"],"names":[],"mappings":"AASA,UAAU,iBAAiB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,eAAe,GAC7B,EAAE,iBAAiB,kDAkEnB"}
@@ -2,7 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
3
  import { Button } from '.';
4
4
  import { cn } from '../../lib/utils';
5
- export function CookieBanner({ className, message = 'We use cookies to enhance your experience on our website. By continuing to use this site, you consent to our use of cookies.', privacyLink = 'https://curvenote.com/legal', }) {
5
+ import { usePlatformSite } from '../providers/platform-providers';
6
+ const DEFAULT_MESSAGE = 'We use cookies to enhance your experience on our website. By continuing to use this site, you consent to our use of cookies.';
7
+ const DEFAULT_PRIVACY_URL = 'https://curvenote.com/legal';
8
+ export function CookieBanner({ className, message: messageProp, privacyLink: privacyLinkProp, }) {
9
+ const site = usePlatformSite();
10
+ const { cookie_banner_message, privacy_url } = site?.theme_config ?? {};
11
+ const message = messageProp || cookie_banner_message || DEFAULT_MESSAGE;
12
+ const privacyLink = privacyLinkProp || privacy_url || DEFAULT_PRIVACY_URL;
6
13
  const [isVisible, setIsVisible] = React.useState(false);
7
14
  const [hasConsented, setHasConsented] = React.useState(false);
8
15
  React.useEffect(() => {
@@ -27,5 +34,5 @@ export function CookieBanner({ className, message = 'We use cookies to enhance y
27
34
  // };
28
35
  if (!isVisible)
29
36
  return null;
30
- return (_jsx("div", { className: cn('fixed bottom-0 left-0 right-0 z-50 bg-background/60 dark:bg-background/50 backdrop-blur-md border-t border-border/50 dark:border-border/30 shadow-lg', className), role: "banner", "aria-label": "Cookie consent", children: _jsx("div", { className: "container mx-auto px-4 py-4", children: _jsxs("div", { className: "flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4", children: [_jsx("div", { className: "flex-1", children: _jsxs("p", { className: "text-sm text-muted-foreground", children: [message, ' ', _jsx("a", { href: privacyLink, className: "text-primary hover:underline underline-offset-4", children: "Learn more" }), "."] }) }), _jsx("div", { className: "flex flex-col sm:flex-row gap-2 shrink-0", children: _jsx(Button, { size: "sm", variant: "secondary", onClick: handleAccept, className: "whitespace-nowrap cursor-pointer", children: "Accept Cookies" }) })] }) }) }));
37
+ return (_jsx("div", { className: cn('fixed right-0 bottom-0 left-0 z-50 border-t shadow-lg backdrop-blur-md bg-background/60 dark:bg-background/50 border-border/50 dark:border-border/30', className), role: "banner", "aria-label": "Cookie consent", children: _jsx("div", { className: "container px-4 py-4 mx-auto", children: _jsxs("div", { className: "flex flex-col gap-4 justify-between items-start sm:flex-row sm:items-center", children: [_jsx("div", { className: "flex-1", children: _jsxs("p", { className: "text-sm text-muted-foreground", children: [message, ' ', _jsx("a", { href: privacyLink, className: "text-primary hover:underline underline-offset-4", children: "Learn more" }), "."] }) }), _jsx("div", { className: "flex flex-col gap-2 sm:flex-row shrink-0", children: _jsx(Button, { size: "sm", variant: "secondary", onClick: handleAccept, className: "whitespace-nowrap cursor-pointer", children: "Accept Cookies" }) })] }) }) }));
31
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"document-outline.d.ts","sourceRoot":"","sources":["../../../src/components/ui/document-outline.tsx"],"names":[],"mappings":"AAcA,UAAU,oBAAoB;IAC5B,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqBD,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,QAAY,EAAE,SAAS,EAAE,EAAE,oBAAoB,kDAoMvF"}
1
+ {"version":3,"file":"document-outline.d.ts","sourceRoot":"","sources":["../../../src/components/ui/document-outline.tsx"],"names":[],"mappings":"AAkBA,UAAU,oBAAoB;IAC5B,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqBD,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,QAAY,EAAE,SAAS,EAAE,EAAE,oBAAoB,kDAuNvF"}
@@ -1,10 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { selectAll } from 'unist-util-select';
3
3
  import { visit } from 'unist-util-visit';
4
+ import { toText } from 'myst-common';
5
+ import { useFrontmatter } from '@myst-theme/providers';
4
6
  import { MyST } from 'myst-to-react';
5
7
  import { Link } from 'react-router';
6
8
  import { cn } from '../../lib/utils';
7
- import { useEffect, useState, useRef } from 'react';
9
+ import { useEffect, useMemo, useState, useRef } from 'react';
10
+ import { useAnalytics } from '../providers';
11
+ import { articleMdastForOutline } from '../../lib/knownParts';
8
12
  // Transform function to remove links from header nodes using unist-util-visit
9
13
  function transformHeaders(headers) {
10
14
  return headers.map((header) => {
@@ -22,49 +26,56 @@ function transformHeaders(headers) {
22
26
  });
23
27
  }
24
28
  export function DocumentOutline({ mdast, maxdepth = 3, className }) {
29
+ const capture = useAnalytics();
30
+ const frontmatter = useFrontmatter();
31
+ const outlineMdast = useMemo(() => articleMdastForOutline(mdast, frontmatter?.parts), [mdast, frontmatter?.parts]);
25
32
  const [activeId, setActiveId] = useState(null);
26
33
  const timeoutRef = useRef(null);
27
34
  const lastActiveIdRef = useRef(null);
28
35
  const ulRef = useRef(null);
29
36
  const [maxHeight, setMaxHeight] = useState(undefined);
30
37
  const [isVisible, setIsVisible] = useState(true);
31
- // Extract all headers from the MDAST and transform them
32
- const rawHeaders = selectAll('heading', mdast);
38
+ // Extract headers from mdast with extracted parts removed (same as article body).
39
+ const rawHeaders = selectAll('heading', outlineMdast);
33
40
  const headers = transformHeaders(rawHeaders);
34
41
  useEffect(() => {
42
+ // Only consider headers that are actually rendered in the outline; if a
43
+ // deeper header (e.g. h4 when maxdepth is 3) is the closest to the
44
+ // viewport, we want to highlight the nearest preceding displayed header.
45
+ const visibleHeaders = headers.filter((h) => h.depth <= maxdepth && h.html_id);
35
46
  // Set initial active header based on scroll position
36
47
  const handleScroll = () => {
48
+ if (visibleHeaders.length === 0)
49
+ return;
37
50
  const scrollTop = window.scrollY;
38
51
  const windowHeight = window.innerHeight;
39
52
  const documentHeight = document.documentElement.scrollHeight;
40
53
  const scrollBottom = scrollTop + windowHeight;
41
- // If we're at the very top, activate the first header
54
+ // If we're at the very top, activate the first displayed header
42
55
  if (scrollTop < windowHeight * 0.1) {
43
- const firstHeader = headers.find((h) => h.html_id);
56
+ const firstHeader = visibleHeaders[0];
44
57
  if (firstHeader?.html_id) {
45
58
  setActiveId(firstHeader.html_id);
46
59
  }
47
60
  return;
48
61
  }
49
- // If we're near the bottom of the page, activate the last header
62
+ // If we're near the bottom of the page, activate the last displayed header
50
63
  if (scrollBottom > documentHeight - windowHeight * 0.2) {
51
- const lastHeader = headers[headers.length - 1];
64
+ const lastHeader = visibleHeaders[visibleHeaders.length - 1];
52
65
  if (lastHeader?.html_id) {
53
66
  setActiveId(lastHeader.html_id);
54
67
  }
55
68
  return;
56
69
  }
57
- // Find the header that's currently in view
58
- for (let i = headers.length - 1; i >= 0; i--) {
59
- const header = headers[i];
60
- if (header.html_id) {
61
- const element = document.getElementById(header.html_id);
62
- if (element) {
63
- const rect = element.getBoundingClientRect();
64
- if (rect.top <= windowHeight * 0.2) {
65
- setActiveId(header.html_id);
66
- break;
67
- }
70
+ // Find the displayed header that's currently in view
71
+ for (let i = visibleHeaders.length - 1; i >= 0; i--) {
72
+ const header = visibleHeaders[i];
73
+ const element = document.getElementById(header.html_id);
74
+ if (element) {
75
+ const rect = element.getBoundingClientRect();
76
+ if (rect.top <= windowHeight * 0.2) {
77
+ setActiveId(header.html_id);
78
+ break;
68
79
  }
69
80
  }
70
81
  }
@@ -79,7 +90,7 @@ export function DocumentOutline({ mdast, maxdepth = 3, className }) {
79
90
  clearTimeout(timeoutRef.current);
80
91
  }
81
92
  };
82
- }, [headers]);
93
+ }, [headers, maxdepth]);
83
94
  useEffect(() => {
84
95
  const ulElement = ulRef.current;
85
96
  if (!ulElement)
@@ -143,7 +154,7 @@ export function DocumentOutline({ mdast, maxdepth = 3, className }) {
143
154
  if (!headers || headers.length < 2) {
144
155
  return null;
145
156
  }
146
- return (_jsxs("nav", { className: cn('relative space-y-2 font-sans not-prose transition-opacity', { 'opacity-100': isVisible, 'opacity-0 pointer-events-none': !isVisible }, className), children: [_jsx("h3", { className: "mb-4 text-sm font-normal text-muted-foreground", children: "On this page" }), _jsx("ul", { ref: ulRef, className: "space-y-1 border-l-1 overflow-y-auto", style: { maxHeight }, children: headers
157
+ return (_jsxs("nav", { className: cn('relative space-y-2 font-sans not-prose transition-opacity', { 'opacity-100': isVisible, 'opacity-0 pointer-events-none': !isVisible }, className), children: [_jsx("h3", { className: "mb-4 text-sm font-normal text-muted-foreground", children: "On this page" }), _jsx("ul", { ref: ulRef, className: "overflow-y-auto space-y-1 border-l-1", style: { maxHeight }, children: headers
147
158
  .filter((header) => header.depth <= maxdepth)
148
159
  .map((header, index) => {
149
160
  const depth = header.depth;
@@ -163,6 +174,13 @@ export function DocumentOutline({ mdast, maxdepth = 3, className }) {
163
174
  if (el.tabIndex === -1)
164
175
  el.tabIndex = -1;
165
176
  el.focus({ preventScroll: true });
177
+ capture('document_outline_heading_clicked', {
178
+ surface: 'document_outline',
179
+ headingId: identifier,
180
+ headingDepth: depth,
181
+ headingIndex: index,
182
+ headingText: toText(header),
183
+ });
166
184
  }, children: _jsx(MyST, { ast: header.children }) }) }, index));
167
185
  }) })] }));
168
186
  }
@@ -1 +1 @@
1
- {"version":3,"file":"footer-links.d.ts","sourceRoot":"","sources":["../../../src/components/ui/footer-links.tsx"],"names":[],"mappings":"AAKA,UAAU,OAAO;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACb;AAgCD,UAAU,gBAAgB;IACxB,UAAU,CAAC,EAAE;QACX,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,IAAI,CAAC;CACV;AAED,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,gBAAgB,kDAqB3D"}
1
+ {"version":3,"file":"footer-links.d.ts","sourceRoot":"","sources":["../../../src/components/ui/footer-links.tsx"],"names":[],"mappings":"AAMA,UAAU,OAAO;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACb;AAgCD,UAAU,gBAAgB;IACxB,UAAU,CAAC,EAAE;QACX,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,IAAI,CAAC;CACV;AAED,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,gBAAgB,kDAuB3D"}
@@ -3,12 +3,16 @@ import { Link } from 'react-router';
3
3
  import { ChevronLeft, ChevronRight } from 'lucide-react';
4
4
  import { useBaseurl, withBaseurl } from '@myst-theme/providers';
5
5
  import { cn } from '../../lib/utils';
6
+ import { useDesign } from '../providers/design';
6
7
  function FooterLink({ item, align, label }) {
7
8
  const baseUrl = useBaseurl();
8
9
  const isLeft = align === 'left';
9
10
  return (_jsxs(Link, { to: withBaseurl(item.url, baseUrl), className: `w-full block border border-muted rounded-sm p-4 hover:bg-accent transition-colors no-underline ${isLeft ? '' : 'text-right'}`, children: [_jsxs("div", { className: `text-muted-foreground flex items-center gap-1 text-sm font-light mb-1 ${isLeft ? '' : 'justify-end'}`, children: [isLeft && _jsx(ChevronLeft, { size: 16 }), _jsx("span", { children: label }), !isLeft && _jsx(ChevronRight, { size: 16 })] }), _jsx("div", { className: "text-sm font-normal", children: item.short_title || item.title })] }));
10
11
  }
11
12
  export function FooterLinks({ navigation }) {
13
+ const design = useDesign();
14
+ if (design.hide_footer_links)
15
+ return null;
12
16
  if (!navigation)
13
17
  return null;
14
18
  const hasPrev = !!navigation.prev;
@@ -1,6 +1,7 @@
1
- export declare function DateString({ date, format, spacer, className, }: {
1
+ export declare function DateString({ date, format, mobileFormat, spacer, className, }: {
2
2
  date?: string;
3
3
  format?: Intl.DateTimeFormatOptions;
4
+ mobileFormat?: Intl.DateTimeFormatOptions;
4
5
  spacer?: boolean;
5
6
  className?: string;
6
7
  }): import("react/jsx-runtime").JSX.Element | null;
@@ -10,18 +11,27 @@ export declare function TwitterLink({ twitter: possibleLink }: {
10
11
  export declare function GitHubLink({ github: possibleLink }: {
11
12
  github?: string;
12
13
  }): import("react/jsx-runtime").JSX.Element | null;
13
- export declare function OpenAccessBadge({ open_access }: {
14
+ export declare function OpenAccessBadge({ open_access, className, }: {
14
15
  open_access?: boolean;
16
+ className?: string;
15
17
  }): import("react/jsx-runtime").JSX.Element | null;
16
- export declare function OrcidBadge({ orcid, className }: {
18
+ export declare function OrcidBadge({ orcid, className, authorIndex, authorName, isCorresponding, }: {
17
19
  orcid?: string;
18
20
  className?: string;
21
+ authorIndex?: number;
22
+ authorName?: string;
23
+ isCorresponding?: boolean;
19
24
  }): import("react/jsx-runtime").JSX.Element | null;
20
- export declare function DoiLink({ doi: possibleLink, spacer }: {
25
+ export declare function DoiLink({ doi: possibleLink, spacer, shortenOnSmallScreens, className, }: {
21
26
  doi?: string;
22
27
  spacer?: boolean;
28
+ shortenOnSmallScreens?: boolean;
29
+ className?: string;
23
30
  }): import("react/jsx-runtime").JSX.Element | null;
24
- export declare function EmailLink({ email }: {
31
+ export declare function EmailLink({ email, authorIndex, authorName, isCorresponding, }: {
25
32
  email?: string;
33
+ authorIndex?: number;
34
+ authorName?: string;
35
+ isCorresponding?: boolean;
26
36
  }): import("react/jsx-runtime").JSX.Element | null;
27
37
  //# sourceMappingURL=frontmatter-badges.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"frontmatter-badges.d.ts","sourceRoot":"","sources":["../../../src/components/ui/frontmatter-badges.tsx"],"names":[],"mappings":"AAOA,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,MAIC,EACD,MAAM,EACN,SAAS,GACV,EAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,kDAiBA;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,kDAkB1E;AAED,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,kDAkBvE;AACD,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,kDAiBzE;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,kDAmBtF;AAED,wBAAgB,OAAO,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,kDAgBxF;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,kDAYtD"}
1
+ {"version":3,"file":"frontmatter-badges.d.ts","sourceRoot":"","sources":["../../../src/components/ui/frontmatter-badges.tsx"],"names":[],"mappings":"AAQA,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,MAIC,EACD,YAAY,EACZ,MAAM,EACN,SAAS,GACV,EAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAC;IACpC,YAAY,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAC;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,kDAwBA;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,kDAqB1E;AAED,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,kDAqBvE;AACD,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,SAAS,GACV,EAAE;IACD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,kDA0BA;AAED,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,GAChB,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,kDAiCA;AAED,wBAAgB,OAAO,CAAC,EACtB,GAAG,EAAE,YAAY,EACjB,MAAM,EACN,qBAAqB,EACrB,SAAS,GACV,EAAE;IACD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,kDAuBA;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,WAAW,EACX,UAAU,EACV,eAAsB,GACvB,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,kDAgCA"}
@@ -1,15 +1,16 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { OpenAccessIcon, GithubIcon, XIcon, OrcidIcon } from '@scienceicons/react/24/solid';
3
3
  import { cn } from '../../lib/utils';
4
4
  import { SimpleTooltip } from './tooltip';
5
5
  import { Mail } from 'lucide-react';
6
- import { HashLink } from 'myst-to-react';
6
+ import { HashLink } from './hash-link';
7
7
  import { PART_IDS } from './types';
8
+ import { useAnalytics } from '../providers';
8
9
  export function DateString({ date, format = {
9
10
  year: 'numeric',
10
11
  month: 'long',
11
12
  day: 'numeric',
12
- }, spacer, className, }) {
13
+ }, mobileFormat, spacer, className, }) {
13
14
  if (!date)
14
15
  return null;
15
16
  // Parse the date
@@ -20,41 +21,66 @@ export function DateString({ date, format = {
20
21
  const localDate = new Date(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate());
21
22
  // Then format as human-readable in the local timezone.
22
23
  const dateString = localDate.toLocaleDateString('en-US', format);
23
- return (_jsx("time", { dateTime: date, className: cn(className, { 'text-spacer': spacer }), children: dateString }));
24
+ const mobileDateString = mobileFormat && localDate.toLocaleDateString('en-US', mobileFormat);
25
+ return (_jsxs("time", { dateTime: date, className: cn(className, { 'text-spacer': spacer }), children: [mobileFormat && (_jsxs(_Fragment, { children: [_jsx("span", { className: "sm:hidden", children: mobileDateString }), _jsx("span", { className: "hidden sm:inline", children: dateString })] })), !mobileFormat && dateString] }));
24
26
  }
25
27
  export function TwitterLink({ twitter: possibleLink }) {
26
28
  if (!possibleLink)
27
29
  return null;
28
30
  const twitter = possibleLink.replace(/^(https?:\/\/)?(?:twitter|x)\.com\//, '');
29
- return (_jsx("a", { href: `https://x.com/${twitter}`, title: `X Account: ${twitter}`, target: "_blank", rel: "noopener noreferrer", className: "text-inherit hover:text-inherit", children: _jsx(XIcon, { width: "1.25rem", height: "1.25rem", className: "inline-block opacity-60 hover:opacity-100" }) }));
31
+ return (_jsx("a", { href: `https://x.com/${twitter}`, title: `X Account: ${twitter}`, target: "_blank", rel: "noopener noreferrer", "aria-label": `X account: ${twitter}`, className: "text-inherit hover:text-inherit", children: _jsx(XIcon, { "aria-hidden": "true", focusable: "false", width: "1.25rem", height: "1.25rem", className: "inline-block opacity-60 hover:opacity-100" }) }));
30
32
  }
31
33
  export function GitHubLink({ github: possibleLink }) {
32
34
  if (!possibleLink)
33
35
  return null;
34
36
  const github = possibleLink.replace(/^(https?:\/\/)?github\.com\//, '');
35
- return (_jsx("a", { href: `https://github.com/${github}`, title: `GitHub Repository: ${github}`, target: "_blank", rel: "noopener noreferrer", className: "text-inherit hover:text-inherit", children: _jsx(GithubIcon, { width: "1.25rem", height: "1.25rem", className: "inline-block opacity-60 hover:opacity-100" }) }));
37
+ return (_jsx("a", { href: `https://github.com/${github}`, title: `GitHub Repository: ${github}`, target: "_blank", rel: "noopener noreferrer", "aria-label": `GitHub repository: ${github}`, className: "text-inherit hover:text-inherit", children: _jsx(GithubIcon, { "aria-hidden": "true", focusable: "false", width: "1.25rem", height: "1.25rem", className: "inline-block opacity-60 hover:opacity-100" }) }));
36
38
  }
37
- export function OpenAccessBadge({ open_access }) {
39
+ export function OpenAccessBadge({ open_access, className, }) {
38
40
  if (!open_access)
39
41
  return null;
40
- return (_jsx(HashLink, { id: PART_IDS.license, title: "Open Access", hover: false, scrollBehavior: "instant", className: "text-inherit hover:text-inherit", children: _jsx(OpenAccessIcon, { width: "1.25rem", height: "1.25rem", className: "inline-block opacity-60 hover:opacity-100 hover:text-[#E18435]" }) }));
42
+ return (_jsx(SimpleTooltip, { title: "Open Access", children: _jsx(HashLink, { id: PART_IDS.license, title: "Open Access", hover: false, scrollBehavior: "instant", className: cn('text-inherit hover:text-inherit', className), analyticsEvent: "article_license_anchor_clicked", analytics: {
43
+ linkKind: 'open_access_badge',
44
+ properties: { surface: 'article_bar', via: 'open_access_badge' },
45
+ }, children: _jsx(OpenAccessIcon, { "aria-hidden": "true", focusable: "false", width: "1.25rem", height: "1.25rem", className: "inline-block opacity-60 hover:opacity-100 hover:text-[#E18435]" }) }) }));
41
46
  }
42
- export function OrcidBadge({ orcid, className }) {
47
+ export function OrcidBadge({ orcid, className, authorIndex, authorName, isCorresponding, }) {
48
+ const capture = useAnalytics();
43
49
  if (!orcid)
44
50
  return null;
45
- return (_jsx(SimpleTooltip, { title: "Open ORCID Profile", children: _jsx("a", { href: `https://orcid.org/${orcid}`, target: "_blank", rel: "noopener noreferrer", title: "ORCID", className: cn('text-inherit hover:text-inherit', className), children: _jsx(OrcidIcon, { width: "1.25rem", height: "1.25rem", className: "inline-block opacity-60 hover:opacity-100 hover:text-[#A6CE39]" }) }) }));
51
+ const label = authorName ? `Open ORCID profile for ${authorName}` : 'Open ORCID profile';
52
+ return (_jsx(SimpleTooltip, { title: "Open ORCID Profile", children: _jsx("a", { href: `https://orcid.org/${orcid}`, target: "_blank", rel: "noopener noreferrer", "aria-label": label, className: cn('text-inherit hover:text-inherit', className), onClick: () => capture('article_author_interaction', {
53
+ action: 'orcid_opened',
54
+ surface: 'article_bar',
55
+ authorOrcid: orcid,
56
+ authorIndex,
57
+ authorName,
58
+ isCorresponding,
59
+ }), children: _jsx(OrcidIcon, { "aria-hidden": "true", focusable: "false", width: "1.25rem", height: "1.25rem", className: "inline-block opacity-60 hover:opacity-100 hover:text-[#A6CE39]" }) }) }));
46
60
  }
47
- export function DoiLink({ doi: possibleLink, spacer }) {
61
+ export function DoiLink({ doi: possibleLink, spacer, shortenOnSmallScreens, className, }) {
62
+ const capture = useAnalytics();
48
63
  if (!possibleLink)
49
64
  return null;
50
65
  const doi = possibleLink.replace(/^(https?:\/\/)?(dx\.)?doi\.org\//, '');
51
66
  const url = `https://doi.org/${doi}`;
52
- return (_jsx("a", { href: `https://doi.org/${doi}`, target: "_blank", rel: "noopener noreferrer", className: cn('font-light hover:underline text-muted-foreground hover:text-foreground', {
67
+ return (_jsxs("a", { href: `https://doi.org/${doi}`, target: "_blank", rel: "noopener noreferrer", className: cn('font-light hover:underline text-muted-foreground hover:text-foreground', {
53
68
  'text-spacer': spacer,
54
- }), children: url }));
69
+ }, className), onClick: () => capture('article_doi_clicked', { doi, surface: 'article_bar' }), children: [shortenOnSmallScreens && _jsx("span", { className: "hidden sm:inline", children: "https://doi.org/" }), shortenOnSmallScreens ? url.replace('https://doi.org/', '') : url] }));
55
70
  }
56
- export function EmailLink({ email }) {
71
+ export function EmailLink({ email, authorIndex, authorName, isCorresponding = true, }) {
72
+ const capture = useAnalytics();
57
73
  if (!email)
58
74
  return null;
59
- return (_jsx(SimpleTooltip, { title: "Email corresponding author", children: _jsx("a", { href: `mailto:${email}`, target: "_blank", rel: "noopener noreferrer", children: _jsx(Mail, { className: "inline-block ml-1 size-4 text-muted-foreground", "aria-label": "Corresponding author" }) }) }));
75
+ const label = authorName
76
+ ? `Email corresponding author ${authorName}`
77
+ : 'Email corresponding author';
78
+ return (_jsx(SimpleTooltip, { title: "Email corresponding author", children: _jsx("a", { href: `mailto:${email}`, target: "_blank", rel: "noopener noreferrer", "aria-label": label, onClick: () => capture('article_author_interaction', {
79
+ action: 'email_clicked',
80
+ surface: 'article_bar',
81
+ authorEmail: email,
82
+ authorIndex,
83
+ authorName,
84
+ isCorresponding,
85
+ }), children: _jsx(Mail, { "aria-hidden": "true", focusable: "false", className: "inline-block ml-1 size-4 text-muted-foreground" }) }) }));
60
86
  }
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from '../../lib/utils';
3
3
  export function Frontmatter({ title, subtitle }) {
4
- return (_jsxs("div", { children: [title && _jsx("h1", { className: cn({ 'mb-4': subtitle }), children: title }), subtitle && _jsx("div", { className: "text-muted-foreground text-lg mb-10", children: subtitle })] }));
4
+ return (_jsxs("div", { children: [title && _jsx("h1", { className: cn({ 'mb-4': subtitle }), children: title }), subtitle && _jsx("div", { className: "mb-10 text-lg text-muted-foreground", children: subtitle })] }));
5
5
  }
6
6
  export default Frontmatter;
@@ -0,0 +1,56 @@
1
+ import { type AnchorHTMLAttributes, type ReactNode } from 'react';
2
+ export type HashLinkAnalytics = {
3
+ linkKind: string;
4
+ properties?: Record<string, unknown>;
5
+ };
6
+ export type HashLinkProps = {
7
+ id?: string;
8
+ kind?: string;
9
+ title?: string | null;
10
+ children?: ReactNode;
11
+ canSelectText?: boolean;
12
+ hover?: boolean | 'desktop';
13
+ className?: string;
14
+ hideInPopup?: boolean;
15
+ scrollBehavior?: ScrollBehavior;
16
+ historyState?: 'push' | 'replace' | false;
17
+ focusTarget?: boolean;
18
+ noWidth?: boolean;
19
+ /** Milliseconds to wait before scrolling (e.g. after a menu closes). */
20
+ scrollDelay?: number;
21
+ analytics?: HashLinkAnalytics;
22
+ /**
23
+ * Override the analytics event name. Defaults to `'content_hash_link_clicked'`.
24
+ * Only used when `analytics` is provided.
25
+ */
26
+ analyticsEvent?: string;
27
+ } & Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'title' | 'children'>;
28
+ export declare function scrollToElement(el: HTMLElement | null, { htmlId, scrollBehavior, historyState, focusTarget, }?: {
29
+ htmlId?: string;
30
+ scrollBehavior?: ScrollBehavior;
31
+ historyState?: 'push' | 'replace' | false;
32
+ focusTarget?: boolean;
33
+ }): void;
34
+ export declare const HashLink: import("react").ForwardRefExoticComponent<{
35
+ id?: string;
36
+ kind?: string;
37
+ title?: string | null;
38
+ children?: ReactNode;
39
+ canSelectText?: boolean;
40
+ hover?: boolean | "desktop";
41
+ className?: string;
42
+ hideInPopup?: boolean;
43
+ scrollBehavior?: ScrollBehavior;
44
+ historyState?: "push" | "replace" | false;
45
+ focusTarget?: boolean;
46
+ noWidth?: boolean;
47
+ /** Milliseconds to wait before scrolling (e.g. after a menu closes). */
48
+ scrollDelay?: number;
49
+ analytics?: HashLinkAnalytics;
50
+ /**
51
+ * Override the analytics event name. Defaults to `'content_hash_link_clicked'`.
52
+ * Only used when `analytics` is provided.
53
+ */
54
+ analyticsEvent?: string;
55
+ } & Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "title" | "children" | "href"> & import("react").RefAttributes<HTMLAnchorElement>>;
56
+ //# sourceMappingURL=hash-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash-link.d.ts","sourceRoot":"","sources":["../../../src/components/ui/hash-link.tsx"],"names":[],"mappings":"AAOA,OAAO,EAGL,KAAK,oBAAoB,EAEzB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC,CAAC;AAUjF,wBAAgB,eAAe,CAC7B,EAAE,EAAE,WAAW,GAAG,IAAI,EACtB,EACE,MAAe,EACf,cAAyB,EACzB,YAAwB,EACxB,WAAkB,GACnB,GAAE;IACD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAC;CAClB,QAcP;AAED,eAAO,MAAM,QAAQ;SA1Dd,MAAM;WACJ,MAAM;YACL,MAAM,GAAG,IAAI;eACV,SAAS;oBACJ,OAAO;YACf,OAAO,GAAG,SAAS;gBACf,MAAM;kBACJ,OAAO;qBACJ,cAAc;mBAChB,MAAM,GAAG,SAAS,GAAG,KAAK;kBAC3B,OAAO;cACX,OAAO;IACjB,wEAAwE;kBAC1D,MAAM;gBACR,iBAAiB;IAC7B;;;OAGG;qBACc,MAAM;oIAiIvB,CAAC"}
@@ -0,0 +1,83 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // @curvenote-fork:myst-to-react
3
+ // MIT — https://github.com/jupyter-book/myst-theme (packages/myst-to-react)
4
+ // Baseline v1.1.4: https://github.com/jupyter-book/myst-theme/blob/e2e1db449c87294775717a8924d4dd1a4d32ceb2/packages/myst-to-react/src/hashLink.tsx
5
+ // Patched v1.3.0: https://github.com/jupyter-book/myst-theme/blob/be74efbb7d13b926a2a6c1e67913e1b1029f28e6/packages/myst-to-react/src/hashLink.tsx
6
+ import { useXRefState } from '@myst-theme/providers';
7
+ import { cn } from '@curvenote/react-utils';
8
+ import { forwardRef, useCallback, } from 'react';
9
+ import { useAnalytics } from '../providers';
10
+ function openDetails(el) {
11
+ if (!el)
12
+ return;
13
+ if (el.nodeName === 'DETAILS') {
14
+ el.open = true;
15
+ }
16
+ openDetails(el.parentElement);
17
+ }
18
+ export function scrollToElement(el, { htmlId = el?.id, scrollBehavior = 'smooth', historyState = 'replace', focusTarget = true, } = {}) {
19
+ if (!el)
20
+ return;
21
+ openDetails(el);
22
+ el.scrollIntoView({ behavior: scrollBehavior });
23
+ if (historyState === 'push') {
24
+ history.pushState(undefined, '', `#${htmlId}`);
25
+ }
26
+ else if (historyState === 'replace') {
27
+ history.replaceState(undefined, '', `#${htmlId}`);
28
+ }
29
+ if (focusTarget) {
30
+ if (el.tabIndex === -1)
31
+ el.tabIndex = -1;
32
+ el.focus({ preventScroll: true });
33
+ }
34
+ }
35
+ export const HashLink = forwardRef(function HashLink({ id, kind, title = kind ? `Link to this ${kind}` : 'Link to this section', children = '¶', canSelectText = false, hover, className = 'font-normal', hideInPopup, scrollBehavior, historyState, focusTarget, noWidth, scrollDelay, analytics, analyticsEvent = 'content_hash_link_clicked', onClick, ...rest }, ref) {
36
+ const capture = useAnalytics();
37
+ const { inCrossRef } = useXRefState();
38
+ const scroll = useCallback((evt) => {
39
+ onClick?.(evt);
40
+ if (evt.defaultPrevented)
41
+ return;
42
+ evt.preventDefault();
43
+ if (!id)
44
+ return;
45
+ if (analytics) {
46
+ capture(analyticsEvent, {
47
+ surface: 'content',
48
+ linkKind: analytics.linkKind,
49
+ targetId: id,
50
+ hashKind: kind,
51
+ ...analytics.properties,
52
+ });
53
+ }
54
+ const el = document.getElementById(id);
55
+ const scrollOptions = { htmlId: id, scrollBehavior, historyState, focusTarget };
56
+ if (scrollDelay != null) {
57
+ window.setTimeout(() => scrollToElement(el, scrollOptions), scrollDelay);
58
+ }
59
+ else {
60
+ scrollToElement(el, scrollOptions);
61
+ }
62
+ }, [
63
+ analytics,
64
+ analyticsEvent,
65
+ capture,
66
+ focusTarget,
67
+ historyState,
68
+ id,
69
+ kind,
70
+ onClick,
71
+ scrollBehavior,
72
+ scrollDelay,
73
+ ]);
74
+ if (inCrossRef || !id) {
75
+ return hideInPopup ? null : _jsx("span", { className: cn('select-none', className), children: children });
76
+ }
77
+ return (_jsx("a", { ...rest, ref: ref, className: cn('no-underline text-inherit hover:text-inherit', { 'inline-block w-0 px-0 translate-x-[10px]': noWidth === true }, {
78
+ 'select-none': !canSelectText,
79
+ 'transition-opacity opacity-0 focus:opacity-100 group-hover:opacity-70': hover === true,
80
+ '[@media(hover:hover)]:transition-opacity [@media(hover:hover)]:opacity-0 [@media(hover:hover)]:focus:opacity-100 [@media(hover:hover)]:group-hover:opacity-70': hover === 'desktop',
81
+ 'hover:underline': !hover,
82
+ }, className), onClick: scroll, href: `#${id}`, title: title ?? undefined, "aria-label": title ?? undefined, children: children }));
83
+ });
@@ -10,9 +10,11 @@ interface ImageProps {
10
10
  width?: string | number;
11
11
  height?: string | number;
12
12
  align?: 'left' | 'center' | 'right';
13
+ onLightboxOpen?: () => void;
14
+ onError?: () => void;
13
15
  }
14
16
  type Alignment = 'left' | 'center' | 'right';
15
- export declare function Picture({ ref, className, src, srcOptimized, urlSource, align, alt, width, height, onClick, style, onLoad, onLoadedData, }: {
17
+ export declare function Picture({ ref, className, src, srcOptimized, urlSource, align, alt, width, height, onClick, style, onLoad, onLoadedData, onError, }: {
16
18
  ref?: React.RefObject<HTMLImageElement | HTMLVideoElement | null>;
17
19
  className?: string;
18
20
  src: string;
@@ -26,8 +28,9 @@ export declare function Picture({ ref, className, src, srcOptimized, urlSource,
26
28
  style?: React.CSSProperties;
27
29
  onLoad?: () => void;
28
30
  onLoadedData?: () => void;
31
+ onError?: () => void;
29
32
  }): import("react/jsx-runtime").JSX.Element;
30
- export declare function Image({ src, srcOptimized, urlSource, alt, className, showLightbox, width, height, align, caption, }: ImageProps & {
33
+ export declare function Image({ src, srcOptimized, urlSource, alt, className, showLightbox, width, height, align, caption, onLightboxOpen, onError, }: ImageProps & {
31
34
  caption?: string | GenericParent;
32
35
  }): import("react/jsx-runtime").JSX.Element;
33
36
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/components/ui/image.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAOjD,UAAU,UAAU;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;CACrC;AAED,KAAK,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAiF7C,wBAAgB,OAAO,CAAC,EACtB,GAAG,EACH,SAAS,EACT,GAAG,EACH,YAAY,EACZ,SAAS,EACT,KAAgB,EAChB,GAAG,EACH,KAAK,EACL,MAAM,EACN,OAAO,EACP,KAAK,EACL,MAAM,EACN,YAAY,GACb,EAAE;IACD,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B,2CA8CA;AAED,wBAAgB,KAAK,CAAC,EACpB,GAAG,EACH,YAAY,EACZ,SAAS,EACT,GAAG,EACH,SAAS,EACT,YAAmB,EACnB,KAAK,EACL,MAAM,EACN,KAAgB,EAChB,OAAO,GACR,EAAE,UAAU,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa,CAAA;CAAE,2CAgDnD"}
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/components/ui/image.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,UAAU,UAAU;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,KAAK,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAiF7C,wBAAgB,OAAO,CAAC,EACtB,GAAG,EACH,SAAS,EACT,GAAG,EACH,YAAY,EACZ,SAAS,EACT,KAAgB,EAChB,GAAG,EACH,KAAK,EACL,MAAM,EACN,OAAO,EACP,KAAK,EACL,MAAM,EACN,YAAY,EACZ,OAAO,GACR,EAAE;IACD,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,2CA+CA;AAED,wBAAgB,KAAK,CAAC,EACpB,GAAG,EACH,YAAY,EACZ,SAAS,EACT,GAAG,EACH,SAAS,EACT,YAAmB,EACnB,KAAK,EACL,MAAM,EACN,KAAgB,EAChB,OAAO,EACP,cAAc,EACd,OAAO,GACR,EAAE,UAAU,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa,CAAA;CAAE,2CAyCnD"}