@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
@@ -2,10 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { useFrontmatter, useReferences } from '@myst-theme/providers';
4
4
  import { toText } from 'myst-common';
5
- import { HashLink } from 'myst-to-react';
6
5
  import { cn } from '../../lib/utils';
7
6
  import { selectAll } from 'unist-util-select';
8
7
  import { PART_IDS } from './types';
8
+ import { useCollapsibleBackmatter, CollapsibleBackmatterHeader, CollapsibleBackmatterFooterButton, } from './collapsible-backmatter-section';
9
+ import { dedupeAbbreviationEntries } from '../../lib/dedupeAbbreviations';
10
+ const DEFAULT_MANY_ABBREVIATIONS = 5;
9
11
  function useAbbreviations() {
10
12
  const frontmatter = useFrontmatter();
11
13
  const references = useReferences();
@@ -15,17 +17,22 @@ function useAbbreviations() {
15
17
  .filter(Boolean);
16
18
  const fullTree = { type: 'page', children: [page, ...parts].filter(Boolean) };
17
19
  const inTextAbbreviations = selectAll('abbreviation', fullTree);
18
- const abbr = Object.fromEntries(inTextAbbreviations.map((node) => [toText(node), node.title]));
19
- if (Object.keys(abbr).length === 0)
20
+ const entries = inTextAbbreviations.map((node) => [toText(node), node.title]);
21
+ const deduped = dedupeAbbreviationEntries(entries, frontmatter?.abbreviations);
22
+ if (deduped.length === 0)
20
23
  return undefined;
21
- return abbr;
24
+ return Object.fromEntries(deduped);
22
25
  }
23
- export function Abbreviations({ sorted = true, className, }) {
26
+ export function Abbreviations({ sorted = true, className, hideManyAbbreviations = DEFAULT_MANY_ABBREVIATIONS, }) {
27
+ if (hideManyAbbreviations === true)
28
+ hideManyAbbreviations = DEFAULT_MANY_ABBREVIATIONS;
24
29
  const abbreviations = useAbbreviations();
25
30
  if (!abbreviations)
26
31
  return null;
27
32
  const sortedAbbreviations = sorted
28
33
  ? Object.entries(abbreviations).sort(([a], [b]) => a.localeCompare(b))
29
34
  : Object.entries(abbreviations);
30
- return (_jsxs("section", { id: PART_IDS.abbreviations, className: cn('min-w-full prose dark:prose-invert prose-sm', className), children: [_jsxs("header", { className: "text-lg font-semibold text-stone-900 dark:text-white group", children: ["Abbreviations", _jsx(HashLink, { id: PART_IDS.abbreviations, title: "Link to Abbreviations", hover: true, className: "ml-2" })] }), _jsx("div", { className: cn('pl-3 mb-8 text-xs text-stone-500 dark:text-stone-300'), children: _jsx("dl", { className: "grid grid-cols-[auto_1fr] gap-x-6 gap-y-2", children: sortedAbbreviations.map(([abbreviation, description]) => (_jsxs(React.Fragment, { children: [_jsx("dt", { className: "py-1 pt-0 m-0 mt-0 font-medium text-stone-900 dark:text-white", children: abbreviation }), _jsx("dd", { className: "flex items-center py-1 pt-0 m-0 mt-0 text-center text-stone-600 dark:text-stone-400", children: description })] }, abbreviation))) }) })] }));
35
+ const { hidden, toggle, displayedCount, isCollapsible } = useCollapsibleBackmatter(sortedAbbreviations.length, hideManyAbbreviations, DEFAULT_MANY_ABBREVIATIONS);
36
+ const displayed = sortedAbbreviations.slice(0, displayedCount);
37
+ return (_jsxs("section", { id: PART_IDS.abbreviations, className: cn('min-w-full prose dark:prose-invert prose-sm', className), children: [_jsx(CollapsibleBackmatterHeader, { id: PART_IDS.abbreviations, title: "Abbreviations", linkTitle: "Link to Abbreviations", hidden: hidden, onToggle: toggle, isCollapsible: isCollapsible }), _jsxs("div", { className: cn('pl-3 mb-8 text-xs text-stone-500 dark:text-stone-300'), children: [_jsx("dl", { className: "grid grid-cols-[auto_1fr] gap-x-6 gap-y-2", children: displayed.map(([abbreviation, description]) => (_jsxs(React.Fragment, { children: [_jsx("dt", { className: "py-1 pt-0 m-0 mt-0 font-medium text-stone-900 dark:text-white", children: abbreviation }), _jsx("dd", { className: "flex items-center py-1 pt-0 m-0 mt-0 text-center text-stone-600 dark:text-stone-400", children: description })] }, abbreviation))) }), isCollapsible && (_jsx(CollapsibleBackmatterFooterButton, { hidden: hidden, onToggle: toggle, totalCount: sortedAbbreviations.length, itemLabel: "abbreviations", sectionId: PART_IDS.abbreviations, className: "flex justify-center pt-2", as: "div" }))] })] }));
31
38
  }
@@ -1,8 +1,15 @@
1
1
  import React from 'react';
2
- export declare function createSkipToTargets(): {
3
- id: string;
2
+ import { PART_IDS } from './types';
3
+ export declare function createSkipToTargets(): ({
4
+ id: "skip-to-content";
4
5
  title: string;
5
- }[];
6
+ } | {
7
+ id: "skip-to-frontmatter";
8
+ title: string;
9
+ } | {
10
+ id: "skip-to-backmatter";
11
+ title: string;
12
+ })[];
6
13
  /**
7
14
  * Add a skip navigation unit with links based on a list of targets
8
15
  */
@@ -12,4 +19,7 @@ export declare const SkipTo: React.MemoExoticComponent<({ targets }: {
12
19
  title: string;
13
20
  }[];
14
21
  }) => import("react/jsx-runtime").JSX.Element>;
22
+ export declare function SkipToAnchor({ id }: {
23
+ id: (typeof PART_IDS)[keyof typeof PART_IDS];
24
+ }): import("react/jsx-runtime").JSX.Element;
15
25
  //# sourceMappingURL=accessibility-skip-to.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"accessibility-skip-to.d.ts","sourceRoot":"","sources":["../../../src/components/ui/accessibility-skip-to.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAiB1B,wBAAgB,mBAAmB;;;IAMlC;AAED;;GAEG;AACH,eAAO,MAAM,MAAM,0CAA4B;IAAE,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,6CAkBzF,CAAC"}
1
+ {"version":3,"file":"accessibility-skip-to.d.ts","sourceRoot":"","sources":["../../../src/components/ui/accessibility-skip-to.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAgBnC,wBAAgB,mBAAmB;;;;;;;;;KAMlC;AAED;;GAEG;AACH,eAAO,MAAM,MAAM,0CAA4B;IAAE,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,6CAkBzF,CAAC;AAEH,wBAAgB,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,EAAE,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAA;CAAE,2CAEpF"}
@@ -27,5 +27,8 @@ export function createSkipToTargets() {
27
27
  * Add a skip navigation unit with links based on a list of targets
28
28
  */
29
29
  export const SkipTo = React.memo(({ targets }) => {
30
- return (_jsx("div", { className: "fixed top-1 left-1 h-[0px] w-[0px] focus-within:z-100 focus-within:h-auto focus-within:w-auto bg-white overflow-hidden focus-within:p-2 focus-within:ring-1 focus-within:ring-blue-500 focus-within:shadow-lg", "aria-label": "skip to content options", children: targets.map(({ id, title }) => (_jsx("a", { href: `#${id}`, className: "block px-2 py-1 text-black underline hover:text-blue-600 focus:text-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500", onClick: makeSkipClickHandler(id), children: title }, id))) }));
30
+ return (_jsx("div", { className: "overflow-hidden fixed top-1 left-1 w-0 h-0 bg-white focus-within:z-100 focus-within:h-auto focus-within:w-auto focus-within:p-2 focus-within:ring-1 focus-within:ring-blue-500 focus-within:shadow-lg", "aria-label": "skip to content options", children: targets.map(({ id, title }) => (_jsx("a", { href: `#${id}`, className: "block px-2 py-1 text-black underline hover:text-blue-600 focus:text-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500", onClick: makeSkipClickHandler(id), children: title }, id))) }));
31
31
  });
32
+ export function SkipToAnchor({ id }) {
33
+ return _jsx("div", { id: id, className: "absolute w-0 h-0" });
34
+ }
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+ import type { LinkProps } from 'react-router';
3
+ export declare function Arrow(): import("react/jsx-runtime").JSX.Element;
4
+ export declare function LongArrowIcon({ className, ...props }: React.SVGProps<SVGSVGElement>): import("react/jsx-runtime").JSX.Element;
5
+ export declare function ArrowLink({ children, className, ...props }: LinkProps): import("react/jsx-runtime").JSX.Element | null;
6
+ //# sourceMappingURL=arrow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arrow.d.ts","sourceRoot":"","sources":["../../../src/components/ui/arrow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,wBAAgB,KAAK,4CASpB;AAED,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,2CAiBnF;AAED,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,SAAS,kDAerE"}
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import { clsx } from 'clsx';
4
+ import { Link } from 'react-router';
5
+ export function Arrow() {
6
+ return (_jsxs("div", { className: "flex h-5 w-full -translate-y-[25%] items-center", children: [_jsx("div", { className: "h-5", children: _jsx("div", { className: "w-3 h-4 rounded-bl-xl border-b-2 border-l-2 border-current" }) }), _jsx("div", { className: "mt-2.5 h-0.5 w-full bg-current" })] }));
7
+ }
8
+ export function LongArrowIcon({ className, ...props }) {
9
+ return (_jsx("svg", { width: "42", height: "8", viewBox: "0 0 42 8", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className, ...props, children: _jsx("path", { d: "M41.5 4.99365L36.5 2.1069V7.8804L41.5 4.99365ZM6 4.99365V5.49365H37V4.99365V4.49365H6V4.99365ZM1 -0.00634766H0.5C0.5 3.03122 2.96243 5.49365 6 5.49365V4.99365V4.49365C3.51472 4.49365 1.5 2.47893 1.5 -0.00634766H1Z", fill: "currentColor" }) }));
10
+ }
11
+ export function ArrowLink({ children, className, ...props }) {
12
+ if (typeof children !== 'string')
13
+ return null;
14
+ return (_jsxs(Link, { ...props, className: clsx('inline-flex items-center space-x-2 group font-nm type-nm-h6 text-brand hover:opacity-75', className), children: [_jsx("span", { children: children }), _jsx(LongArrowIcon, { className: "origin-top-left transition-transform duration-150 ease-out group-hover:scale-x-[1.05]" })] }));
15
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"article-actions-downloads.d.ts","sourceRoot":"","sources":["../../../src/components/ui/article-actions-downloads.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAI1D;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,gBAKzE;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,gBA0BtE;AAoCD,wBAAgB,YAAY;;;;;EAa3B;AAkDD,wBAAgB,oBAAoB,mDAwBnC;AAED,wBAAgB,kBAAkB,mDA0BjC"}
1
+ {"version":3,"file":"article-actions-downloads.d.ts","sourceRoot":"","sources":["../../../src/components/ui/article-actions-downloads.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK1D;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,gBAKzE;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,gBA0BtE;AAsCD,wBAAgB,YAAY;;;;;EAa3B;AA0HD,wBAAgB,oBAAoB,mDA8CnC;AAED,wBAAgB,kBAAkB,mDAgDjC"}
@@ -2,9 +2,10 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel } from '.';
3
3
  import { useFrontmatter, useProjectManifest, useSiteManifest } from '@myst-theme/providers';
4
4
  import { toast } from 'sonner';
5
- import { File, FileText, FileCode, FileArchive, ScrollText } from 'lucide-react';
5
+ import { File, FileText, FileCode, FileArchive, ScrollText, FileVolume } from 'lucide-react';
6
6
  import React from 'react';
7
7
  import { BibTeXCitationMenuItem, useShowCitation } from './citation';
8
+ import { useAnalytics, useDesign } from '../providers';
8
9
  /**
9
10
  * triggerDirectDownload - aims to trigger a direct download for the
10
11
  *
@@ -66,6 +67,8 @@ function getFormatDisplayName(format) {
66
67
  return 'BibTeX';
67
68
  case 'ipynb':
68
69
  return 'Jupyter Notebook';
70
+ case 'mp3':
71
+ return 'MP3';
69
72
  case 'tex':
70
73
  return 'LaTeX';
71
74
  case 'html':
@@ -91,9 +94,28 @@ export function useDownloads() {
91
94
  articleDownloads: article?.downloads ?? [],
92
95
  };
93
96
  }
94
- async function handleDownload(download) {
97
+ function buildDownloadAnalyticsProps(download, ctx) {
98
+ const format = download.format || formatFromUrl(download.url) || 'unknown';
99
+ return {
100
+ category: ctx.category,
101
+ scope: ctx.scope,
102
+ surface: ctx.surface,
103
+ format,
104
+ downloadUrl: download.url,
105
+ downloadFilename: download.filename,
106
+ downloadTitle: download.title,
107
+ };
108
+ }
109
+ async function handleDownload(download, capture, ctx) {
110
+ const baseProps = buildDownloadAnalyticsProps(download, ctx);
111
+ capture('article_download', { action: 'clicked', ...baseProps });
95
112
  toast.success(_jsxs(_Fragment, { children: ["Download starting... ", _jsx("br", {}), _jsx("code", { children: download.filename })] }));
96
113
  await triggerDirectDownload(download.url, download.filename).catch((error) => {
114
+ capture('article_download', {
115
+ action: 'failed',
116
+ ...baseProps,
117
+ errorMessage: error instanceof Error ? error.message : String(error),
118
+ });
97
119
  console.error('Download failed:', error);
98
120
  toast.error('Download failed. Please try again.');
99
121
  });
@@ -106,6 +128,7 @@ const formatIconMap = {
106
128
  omex: FileArchive,
107
129
  tex: ScrollText,
108
130
  typst: ScrollText,
131
+ mp3: FileVolume,
109
132
  };
110
133
  function getIconForFormat(format) {
111
134
  const lowerFormat = format.toLowerCase();
@@ -116,28 +139,72 @@ function getIconForFormat(format) {
116
139
  }
117
140
  return File;
118
141
  }
119
- function renderDownloadItems(downloads) {
142
+ function formatFromUrl(url) {
143
+ try {
144
+ const urlObj = new URL(url);
145
+ const pathname = urlObj.pathname;
146
+ const format = pathname.split('.').pop()?.toLowerCase();
147
+ if (format && format.length <= 6) {
148
+ return format;
149
+ }
150
+ return undefined;
151
+ }
152
+ catch (error) {
153
+ return undefined;
154
+ }
155
+ }
156
+ function renderDownloadItems(downloads, capture, ctx, options = {}) {
120
157
  return downloads.map((download, index) => {
121
- const format = download.format || 'unknown';
158
+ const format = download.format || formatFromUrl(download.url) || 'unknown';
122
159
  const downloadName = download.title || getFormatDisplayName(format);
123
160
  const icon = getIconForFormat(format);
124
- return (_jsxs(DropdownMenuItem, { onClick: () => handleDownload(download), children: [React.createElement(icon, { className: 'mr-2 h-4 w-4' }), downloadName] }, index));
161
+ if (options.linkMode) {
162
+ return (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs("a", { href: download.url, download: download.filename, target: "_blank", rel: "noopener noreferrer", onClick: () => capture('article_download', {
163
+ action: 'clicked',
164
+ ...buildDownloadAnalyticsProps(download, ctx),
165
+ }), children: [React.createElement(icon, { className: 'mr-2 h-4 w-4' }), downloadName] }) }, index));
166
+ }
167
+ return (_jsxs(DropdownMenuItem, { onClick: () => handleDownload(download, capture, ctx), children: [React.createElement(icon, { className: 'mr-2 h-4 w-4' }), downloadName] }, index));
125
168
  });
126
169
  }
127
170
  export function ArticleDownloadItems() {
128
171
  const { projectDownloads, articleDownloads } = useDownloads();
172
+ const capture = useAnalytics();
173
+ const { link_downloads } = useDesign();
129
174
  const hasProjectDownloads = projectDownloads.length > 0;
130
175
  const hasArticleDownloads = articleDownloads.length > 0;
131
176
  if (!hasProjectDownloads && !hasArticleDownloads)
132
177
  return null;
133
- return (_jsxs(_Fragment, { children: [hasProjectDownloads && (_jsxs(_Fragment, { children: [renderDownloadItems(projectDownloads), hasArticleDownloads && _jsx(DropdownMenuSeparator, {})] })), hasArticleDownloads && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuLabel, { children: "Page Downloads" }), renderDownloadItems(articleDownloads)] }))] }));
178
+ const surface = 'actions_menu_downloads_submenu';
179
+ const linkMode = !!link_downloads;
180
+ return (_jsxs(_Fragment, { children: [hasProjectDownloads && (_jsxs(_Fragment, { children: [renderDownloadItems(projectDownloads, capture, {
181
+ category: 'download',
182
+ scope: 'project',
183
+ surface,
184
+ }, { linkMode }), hasArticleDownloads && _jsx(DropdownMenuSeparator, {})] })), hasArticleDownloads && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuLabel, { children: "Page Downloads" }), renderDownloadItems(articleDownloads, capture, {
185
+ category: 'download',
186
+ scope: 'article',
187
+ surface,
188
+ }, { linkMode })] }))] }));
134
189
  }
135
190
  export function ArticleExportItems() {
136
191
  const { projectExports, articleExports } = useDownloads();
137
192
  const showCitation = useShowCitation();
193
+ const capture = useAnalytics();
194
+ const { link_downloads } = useDesign();
138
195
  const hasProjectExports = projectExports.length > 0;
139
196
  const hasArticleExports = articleExports.length > 0;
140
197
  if (!hasProjectExports && !hasArticleExports)
141
198
  return null;
142
- return (_jsxs(_Fragment, { children: [(hasProjectExports || showCitation) && (_jsxs(_Fragment, { children: [renderDownloadItems(projectExports), showCitation && _jsx(BibTeXCitationMenuItem, {}), hasArticleExports && _jsx(DropdownMenuSeparator, {})] })), hasArticleExports && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuLabel, { children: "Page Exports" }), renderDownloadItems(articleExports)] }))] }));
199
+ const surface = 'actions_menu_exports_submenu';
200
+ const linkMode = !!link_downloads;
201
+ return (_jsxs(_Fragment, { children: [(hasProjectExports || showCitation) && (_jsxs(_Fragment, { children: [renderDownloadItems(projectExports, capture, {
202
+ category: 'export',
203
+ scope: 'project',
204
+ surface,
205
+ }, { linkMode }), showCitation && _jsx(BibTeXCitationMenuItem, {}), hasArticleExports && _jsx(DropdownMenuSeparator, {})] })), hasArticleExports && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuLabel, { children: "Page Exports" }), renderDownloadItems(articleExports, capture, {
206
+ category: 'export',
207
+ scope: 'article',
208
+ surface,
209
+ }, { linkMode })] }))] }));
143
210
  }
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- export declare function ArticleActionsDropdown({ children }: {
2
+ export declare function ArticleActionsDropdown({ children, onOpenChange, }: {
3
3
  children: React.ReactNode;
4
+ onOpenChange?: (open: boolean) => void;
4
5
  }): import("react/jsx-runtime").JSX.Element;
5
6
  //# sourceMappingURL=article-actions-dropdown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"article-actions-dropdown.d.ts","sourceRoot":"","sources":["../../../src/components/ui/article-actions-dropdown.tsx"],"names":[],"mappings":"AAqCA,OAAO,KAAK,MAAM,OAAO,CAAC;AAiD1B,wBAAgB,sBAAsB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CAmLjF"}
1
+ {"version":3,"file":"article-actions-dropdown.d.ts","sourceRoot":"","sources":["../../../src/components/ui/article-actions-dropdown.tsx"],"names":[],"mappings":"AAqCA,OAAO,KAAK,MAAM,OAAO,CAAC;AAuF1B,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,YAAY,GACb,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,2CA4TA"}
@@ -1,26 +1,18 @@
1
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, Badge, } from '.';
3
3
  import { Citation } from './citation';
4
4
  import { useFrontmatter, useProjectManifest } from '@myst-theme/providers';
5
5
  import { toast } from 'sonner';
6
6
  import { doi } from 'doi-utils';
7
- import { Download, Quote, Copy, Rocket, Pencil, Clock, BlocksIcon, Share, Sun, Moon, Monitor, FolderCode, FileText, } from 'lucide-react';
7
+ import { Download, Quote, Copy, Pencil, Clock, BlocksIcon, Share, Sun, Moon, Monitor, FolderCode, FileText, Scale, } from 'lucide-react';
8
8
  import { ArticleDownloadItems, ArticleExportItems, useDownloads, triggerDirectDownload, } from './article-actions-downloads';
9
9
  import React from 'react';
10
- import { ArticleActionsShare } from './article-actions-share';
10
+ import { ArticleActionsShare, useVisibleShareOptions } from './article-actions-share';
11
11
  import { useTheme } from 'next-themes';
12
- const mockVersions = [
13
- {
14
- number: 2,
15
- date: '2025-03-17',
16
- description: 'Fixed a bug in the notebook that was causing the notebook to not run correctly.',
17
- },
18
- {
19
- number: 1,
20
- date: '2025-03-10',
21
- description: '',
22
- },
23
- ];
12
+ import { useAnalytics, useDesign } from '../providers';
13
+ import { useArticleVersions } from '../providers/article-versions-provider';
14
+ import { HashLink } from './hash-link';
15
+ import { PART_IDS } from './types';
24
16
  function formatDate(dateStr) {
25
17
  return new Date(dateStr).toLocaleDateString('en-US', {
26
18
  month: 'short',
@@ -28,14 +20,37 @@ function formatDate(dateStr) {
28
20
  year: 'numeric',
29
21
  });
30
22
  }
31
- function ArticleVersionList({ versions }) {
32
- return (_jsx(_Fragment, { children: versions.map((v) => (_jsx(DropdownMenuItem, { children: _jsxs("div", { className: "flex flex-col", children: [_jsxs("div", { className: "flex items-center", children: [_jsxs("span", { className: "mr-4 font-mono text-muted-foreground", children: ["v", v.number] }), _jsx("span", { children: formatDate(v.date) }), _jsx("span", { className: "flex-1" }), v.number === 2 && _jsx(Badge, { variant: "outline", children: "Current" })] }), v.description && (_jsx("div", { className: "mt-1 max-w-xs text-xs leading-snug text-muted-foreground", children: v.description }))] }) }, v.number))) }));
23
+ function ArticleVersionList() {
24
+ const { versions, currentVersionTag } = useArticleVersions();
25
+ const capture = useAnalytics();
26
+ if (!versions.length)
27
+ return null;
28
+ return (_jsx(_Fragment, { children: versions.map((v) => (_jsx(DropdownMenuItem, { asChild: true, children: _jsx("a", { href: v.href, className: "cursor-pointer", onClick: () => capture('article_version_selected', {
29
+ versionNumber: v.tag,
30
+ versionDate: v.date ?? null,
31
+ isCurrent: Boolean(currentVersionTag && v.tag.toLowerCase() === currentVersionTag.toLowerCase()),
32
+ surface: 'actions_menu_versions_submenu',
33
+ }), children: _jsxs("div", { className: "flex flex-col", children: [_jsxs("div", { className: "flex items-center", children: [_jsx("span", { className: "mr-4 font-mono text-muted-foreground", children: v.tag }), v.date ? _jsx("span", { className: "mr-4", children: formatDate(v.date) }) : null, _jsx("span", { className: "flex-1" }), currentVersionTag && v.tag.toLowerCase() === currentVersionTag.toLowerCase() && (_jsx(Badge, { variant: "outline", children: "Current" }))] }), v.description ? (_jsx("div", { className: "mt-1 max-w-xs text-xs leading-snug text-muted-foreground", children: v.description })) : null] }) }) }, v.tag))) }));
33
34
  }
34
- export function ArticleActionsDropdown({ children }) {
35
+ function ArticleVersionsMenu() {
36
+ const { versions } = useArticleVersions();
37
+ const capture = useAnalytics();
38
+ if (!versions.length)
39
+ return null;
40
+ return (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuSub, { onOpenChange: (open) => open &&
41
+ capture('article_versions_submenu_opened', {
42
+ versionCount: versions.length,
43
+ }), children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(Clock, { className: "mr-4 w-4 h-4" }), "Versions"] }), _jsx(DropdownMenuSubContent, { children: _jsx(ArticleVersionList, {}) })] }), _jsx(DropdownMenuSeparator, {})] }));
44
+ }
45
+ export function ArticleActionsDropdown({ children, onOpenChange, }) {
35
46
  const project = useProjectManifest();
36
47
  const article = useFrontmatter();
37
48
  const { projectDownloads, articleDownloads, projectExports, articleExports } = useDownloads();
38
49
  const { theme, setTheme } = useTheme();
50
+ const capture = useAnalytics();
51
+ const { hide_exports, link_downloads } = useDesign();
52
+ const visibleShareOptions = useVisibleShareOptions();
53
+ const hasShareOptions = visibleShareOptions.length > 0;
39
54
  const allDownloads = [...projectDownloads, ...articleDownloads];
40
55
  const hasDownloads = allDownloads.length > 0;
41
56
  const hasExports = projectExports.length > 0 || articleExports.length > 0;
@@ -44,11 +59,31 @@ export function ArticleActionsDropdown({ children }) {
44
59
  (allDownloads[0]?.format?.toLowerCase() === 'pdf' ||
45
60
  allDownloads[0]?.url?.toLowerCase().endsWith('.pdf'));
46
61
  const singlePdfDownload = hasSinglePdfDownload ? allDownloads[0] : null;
62
+ const singlePdfScope = singlePdfDownload && projectDownloads.includes(singlePdfDownload) ? 'project' : 'article';
47
63
  const handleSinglePdfDownload = async () => {
48
64
  if (!singlePdfDownload)
49
65
  return;
66
+ capture('article_download', {
67
+ action: 'clicked',
68
+ category: 'download',
69
+ format: 'pdf',
70
+ downloadUrl: singlePdfDownload.url,
71
+ downloadFilename: singlePdfDownload.filename,
72
+ scope: singlePdfScope,
73
+ surface: 'actions_menu_single_pdf',
74
+ });
50
75
  toast.success(_jsxs(_Fragment, { children: ["Download starting... ", _jsx("br", {}), _jsx("code", { children: singlePdfDownload.filename })] }));
51
76
  await triggerDirectDownload(singlePdfDownload.url, singlePdfDownload.filename).catch((error) => {
77
+ capture('article_download', {
78
+ action: 'failed',
79
+ category: 'download',
80
+ format: 'pdf',
81
+ downloadUrl: singlePdfDownload.url,
82
+ downloadFilename: singlePdfDownload.filename,
83
+ scope: singlePdfScope,
84
+ surface: 'actions_menu_single_pdf',
85
+ errorMessage: error instanceof Error ? error.message : String(error),
86
+ });
52
87
  console.error('Download failed:', error);
53
88
  toast.error('Download failed. Please try again.');
54
89
  });
@@ -57,12 +92,58 @@ export function ArticleActionsDropdown({ children }) {
57
92
  const handleCopyDOI = () => {
58
93
  if (doiString) {
59
94
  navigator.clipboard.writeText(doiString);
95
+ capture('article_doi_copied', { doi: doiString, surface: 'actions_menu' });
60
96
  toast.success(_jsxs("div", { children: ["DOI copied to clipboard: ", _jsx("br", {}), _jsx("a", { href: doiString, children: doiString })] }));
61
97
  }
62
98
  };
63
- return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: children }), _jsxs(DropdownMenuContent, { className: "w-56", align: "end", children: [_jsx(DropdownMenuLabel, { children: "Article Actions" }), _jsx(DropdownMenuSeparator, {}), hasSinglePdfDownload && singlePdfDownload ? (_jsxs(DropdownMenuItem, { onClick: handleSinglePdfDownload, children: [_jsx(FileText, { className: "mr-2 w-4 h-4" }), "Download PDF"] })) : (hasDownloads && (_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(Download, { className: "mr-4 w-4 h-4" }), "Download"] }), _jsx(DropdownMenuSubContent, { children: _jsx(ArticleDownloadItems, {}) })] }))), hasExports && (_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(FolderCode, { className: "mr-4 w-4 h-4" }), "Exports"] }), _jsx(DropdownMenuSubContent, { children: _jsx(ArticleExportItems, {}) })] })), _jsx(Citation, { children: _jsxs(DropdownMenuItem, { onSelect: (e) => e.preventDefault(), children: [_jsx(Quote, { className: "mr-2 w-4 h-4" }), "Cite Article"] }) }), _jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(Share, { className: "mr-4 w-4 h-4" }), "Share"] }), _jsx(DropdownMenuSubContent, { children: _jsx(ArticleActionsShare, { title: article?.title || '' }) })] }), project?.github && (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs("a", { href: project.github, target: "_blank", rel: "noopener noreferrer", children: [_jsx(BlocksIcon, { className: "mr-2 w-4 h-4" }), "GitHub Repository"] }) })), doiString && (_jsxs(DropdownMenuItem, { onClick: handleCopyDOI, children: [_jsx(Copy, { className: "mr-2 w-4 h-4" }), "Copy DOI"] })), _jsx(DropdownMenuSeparator, {}), article?.edit_url && (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs("a", { href: article?.edit_url, target: "_blank", rel: "noopener noreferrer", children: [_jsx(Pencil, { className: "mr-2 w-4 h-4" }), article?.edit_url?.includes('github.com')
99
+ const editProvider = article?.edit_url?.includes('github.com')
100
+ ? 'github'
101
+ : article?.edit_url?.includes('gitlab')
102
+ ? 'gitlab'
103
+ : 'other';
104
+ const hasLicenseSection = Boolean(project?.license ||
105
+ project?.open_access ||
106
+ project?.copyright ||
107
+ article?.license ||
108
+ article?.open_access ||
109
+ article?.copyright);
110
+ return (_jsxs(DropdownMenu, { onOpenChange: onOpenChange, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: children }), _jsxs(DropdownMenuContent, { className: "w-56", align: "end", children: [_jsx(DropdownMenuLabel, { children: "Article Actions" }), _jsx(DropdownMenuSeparator, {}), hasSinglePdfDownload && singlePdfDownload ? (link_downloads ? (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs("a", { href: singlePdfDownload.url, download: singlePdfDownload.filename, target: "_blank", rel: "noopener noreferrer", onClick: () => capture('article_download', {
111
+ action: 'clicked',
112
+ category: 'download',
113
+ format: 'pdf',
114
+ downloadUrl: singlePdfDownload.url,
115
+ downloadFilename: singlePdfDownload.filename,
116
+ scope: singlePdfScope,
117
+ surface: 'actions_menu_single_pdf',
118
+ }), children: [_jsx(FileText, { className: "mr-2 w-4 h-4" }), "Download PDF"] }) })) : (_jsxs(DropdownMenuItem, { onClick: handleSinglePdfDownload, children: [_jsx(FileText, { className: "mr-2 w-4 h-4" }), "Download PDF"] }))) : (hasDownloads && (_jsxs(DropdownMenuSub, { onOpenChange: (open) => open &&
119
+ capture('article_downloads_submenu_opened', {
120
+ downloadCount: allDownloads.length,
121
+ formats: allDownloads.map((d) => d.format ?? null),
122
+ }), children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(Download, { className: "mr-4 w-4 h-4" }), "Download"] }), _jsx(DropdownMenuSubContent, { children: _jsx(ArticleDownloadItems, {}) })] }))), hasExports && !hide_exports && (_jsxs(DropdownMenuSub, { onOpenChange: (open) => open &&
123
+ capture('article_exports_submenu_opened', {
124
+ exportCount: projectExports.length + articleExports.length,
125
+ formats: [...projectExports, ...articleExports].map((e) => e.format ?? null),
126
+ }), children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(FolderCode, { className: "mr-4 w-4 h-4" }), "Exports"] }), _jsx(DropdownMenuSubContent, { children: _jsx(ArticleExportItems, {}) })] })), _jsx(ArticleVersionsMenu, {}), _jsx(Citation, { onOpenChange: (open) => open && capture('article_citation_opened', { surface: 'actions_menu' }), children: _jsxs(DropdownMenuItem, { onSelect: (e) => e.preventDefault(), children: [_jsx(Quote, { className: "mr-2 w-4 h-4" }), "Cite Article"] }) }), hasShareOptions && (_jsxs(DropdownMenuSub, { onOpenChange: (open) => open && capture('article_share_submenu_opened'), children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(Share, { className: "mr-4 w-4 h-4" }), "Share"] }), _jsx(DropdownMenuSubContent, { children: _jsx(ArticleActionsShare, { title: article?.title || '' }) })] })), project?.github && (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs("a", { href: project.github, target: "_blank", rel: "noopener noreferrer", onClick: () => capture('article_repo_clicked', {
127
+ repoUrl: project.github,
128
+ surface: 'actions_menu',
129
+ }), children: [_jsx(BlocksIcon, { className: "mr-2 w-4 h-4" }), "GitHub Repository"] }) })), doiString && (_jsxs(DropdownMenuItem, { onClick: handleCopyDOI, children: [_jsx(Copy, { className: "mr-2 w-4 h-4" }), "Copy DOI"] })), hasLicenseSection && (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs(HashLink, { id: PART_IDS.license, title: null, className: "flex items-center px-2 py-1.5 text-sm cursor-pointer outline-none focus:bg-accent focus:text-accent-foreground hover:no-underline", scrollBehavior: "instant", scrollDelay: 300, analyticsEvent: "article_license_anchor_clicked", analytics: {
130
+ linkKind: 'license_information_menu',
131
+ properties: { surface: 'actions_menu' },
132
+ }, children: [_jsx(Scale, { className: "mr-2 w-4 h-4" }), "License Information"] }) })), _jsx(DropdownMenuSeparator, {}), article?.edit_url && (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs("a", { href: article?.edit_url, target: "_blank", rel: "noopener noreferrer", onClick: () => capture('article_edit_source_clicked', {
133
+ editUrl: article?.edit_url,
134
+ provider: editProvider,
135
+ }), children: [_jsx(Pencil, { className: "mr-2 w-4 h-4" }), editProvider === 'github'
64
136
  ? 'Edit in GitHub'
65
- : article?.edit_url?.includes('gitlab')
137
+ : editProvider === 'gitlab'
66
138
  ? 'Edit in GitLab'
67
- : 'Edit source'] }) })), _jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { children: [theme === 'dark' ? (_jsx(Sun, { className: "mr-2 w-4 h-4" })) : (_jsx(Moon, { className: "mr-2 w-4 h-4" })), "Theme"] }), _jsxs(DropdownMenuSubContent, { children: [_jsxs(DropdownMenuItem, { onClick: () => setTheme('light'), children: [_jsx(Sun, { className: "mr-2 w-4 h-4" }), "Light"] }), _jsxs(DropdownMenuItem, { onClick: () => setTheme('dark'), children: [_jsx(Moon, { className: "mr-2 w-4 h-4" }), "Dark"] }), _jsxs(DropdownMenuItem, { onClick: () => setTheme('system'), children: [_jsx(Monitor, { className: "mr-2 w-4 h-4" }), "System"] })] })] })] })] }));
139
+ : 'Edit source'] }) })), _jsxs(DropdownMenuSub, { onOpenChange: (open) => open && capture('theme_submenu_opened', { currentTheme: theme }), children: [_jsxs(DropdownMenuSubTrigger, { children: [theme === 'dark' ? (_jsx(Sun, { className: "mr-2 w-4 h-4" })) : (_jsx(Moon, { className: "mr-2 w-4 h-4" })), "Theme"] }), _jsxs(DropdownMenuSubContent, { children: [_jsxs(DropdownMenuItem, { onClick: () => {
140
+ capture('theme_changed', { theme: 'light', previousTheme: theme });
141
+ setTheme('light');
142
+ }, children: [_jsx(Sun, { className: "mr-2 w-4 h-4" }), "Light"] }), _jsxs(DropdownMenuItem, { onClick: () => {
143
+ capture('theme_changed', { theme: 'dark', previousTheme: theme });
144
+ setTheme('dark');
145
+ }, children: [_jsx(Moon, { className: "mr-2 w-4 h-4" }), "Dark"] }), _jsxs(DropdownMenuItem, { onClick: () => {
146
+ capture('theme_changed', { theme: 'system', previousTheme: theme });
147
+ setTheme('system');
148
+ }, children: [_jsx(Monitor, { className: "mr-2 w-4 h-4" }), "System"] })] })] })] })] }));
68
149
  }
@@ -1,5 +1,23 @@
1
+ import React from 'react';
2
+ import type { DesignOptions } from '@curvenote/common';
3
+ type ShareDestination = 'bluesky' | 'semble' | 'linkedin' | 'x' | 'email';
4
+ /**
5
+ * Returns share options visible under the current design configuration.
6
+ * Returns an empty array when the entire share menu is hidden via
7
+ * `hide_social_shares`, otherwise filters by per-platform `hide_social_share_*` flags.
8
+ */
9
+ export declare function useVisibleShareOptions(): {
10
+ name: string;
11
+ destination: ShareDestination;
12
+ hideKey: keyof DesignOptions;
13
+ Icon: React.ComponentType<{
14
+ className?: string;
15
+ }>;
16
+ getUrl: (url: string, title: string) => string;
17
+ }[];
1
18
  export declare function ArticleActionsShare({ url, title }: {
2
19
  url?: string;
3
20
  title: string;
4
21
  }): import("react/jsx-runtime").JSX.Element;
22
+ export {};
5
23
  //# sourceMappingURL=article-actions-share.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"article-actions-share.d.ts","sourceRoot":"","sources":["../../../src/components/ui/article-actions-share.tsx"],"names":[],"mappings":"AAwDA,wBAAgB,mBAAmB,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,2CAyBlF"}
1
+ {"version":3,"file":"article-actions-share.d.ts","sourceRoot":"","sources":["../../../src/components/ui/article-actions-share.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAavD,KAAK,gBAAgB,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,GAAG,GAAG,OAAO,CAAC;AAoE1E;;;;GAIG;AACH,wBAAgB,sBAAsB;UAtE9B,MAAM;iBACC,gBAAgB;aACpB,MAAM,aAAa;UACtB,KAAK,CAAC,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;YACzC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM;IAsE/C;AAED,wBAAgB,mBAAmB,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,2CAqClF"}
@@ -4,10 +4,13 @@ import { DropdownMenuItem } from '.';
4
4
  import { BlueskyIcon,
5
5
  // RedditIcon,
6
6
  // FacebookIcon,
7
- LinkedinIcon, XIcon, EmailIcon, } from '@scienceicons/react/24/solid';
7
+ LinkedinIcon, XIcon, EmailIcon, SembleIcon, } from '@scienceicons/react/24/solid';
8
+ import { useAnalytics, useDesign } from '../providers';
8
9
  const SHARE_OPTIONS = [
9
10
  {
10
11
  name: 'Bluesky',
12
+ destination: 'bluesky',
13
+ hideKey: 'hide_social_share_bluesky',
11
14
  Icon: BlueskyIcon,
12
15
  getUrl: (url, title) => `https://bsky.app/intent/compose?text=${encodeURIComponent(title + ' ' + url)}`,
13
16
  },
@@ -27,26 +30,59 @@ const SHARE_OPTIONS = [
27
30
  // },
28
31
  {
29
32
  name: 'Linkedin',
33
+ destination: 'linkedin',
34
+ hideKey: 'hide_social_share_linkedin',
30
35
  Icon: LinkedinIcon,
31
36
  getUrl: (url, title) => `https://www.linkedin.com/shareArticle?url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}`,
32
37
  },
33
38
  {
34
39
  name: '𝕏',
40
+ destination: 'x',
41
+ hideKey: 'hide_social_share_x',
35
42
  Icon: XIcon,
36
43
  getUrl: (url, title) => `https://twitter.com/intent/tweet?text=${encodeURIComponent(title)}&url=${encodeURIComponent(url)}`,
37
44
  },
45
+ {
46
+ name: 'Semble',
47
+ destination: 'semble',
48
+ hideKey: 'hide_social_share_semble',
49
+ Icon: SembleIcon,
50
+ getUrl: (url) => `https://semble.so/url?id=${encodeURIComponent(url)}`,
51
+ },
38
52
  {
39
53
  name: 'Email',
54
+ destination: 'email',
55
+ hideKey: 'hide_social_share_email',
40
56
  Icon: EmailIcon,
41
57
  getUrl: (url, title) => `mailto:?subject=${encodeURIComponent(title)}&body=${encodeURIComponent(url)}`,
42
58
  },
43
59
  ];
60
+ /**
61
+ * Returns share options visible under the current design configuration.
62
+ * Returns an empty array when the entire share menu is hidden via
63
+ * `hide_social_shares`, otherwise filters by per-platform `hide_social_share_*` flags.
64
+ */
65
+ export function useVisibleShareOptions() {
66
+ const design = useDesign();
67
+ if (design.hide_social_shares)
68
+ return [];
69
+ return SHARE_OPTIONS.filter(({ hideKey }) => !design[hideKey]);
70
+ }
44
71
  export function ArticleActionsShare({ url, title }) {
45
72
  const [shareUrl, setShareUrl] = useState(url || '');
73
+ const capture = useAnalytics();
74
+ const visibleOptions = useVisibleShareOptions();
46
75
  useEffect(() => {
47
76
  if (!url) {
48
77
  setShareUrl(window.location.href);
49
78
  }
50
79
  }, [url]);
51
- return (_jsx(_Fragment, { children: SHARE_OPTIONS.map(({ name, Icon, getUrl }) => (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs("a", { href: getUrl(shareUrl, title), target: "_blank", rel: "noopener noreferrer", "aria-label": `Share on ${name}`, children: [_jsx(Icon, { className: "mr-2 h-4 w-4" }), name] }) }, name))) }));
80
+ return (_jsx(_Fragment, { children: visibleOptions.map(({ name, destination, Icon, getUrl }) => {
81
+ const targetUrl = getUrl(shareUrl, title);
82
+ return (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs("a", { href: targetUrl, target: "_blank", rel: "noopener noreferrer", "aria-label": `Share on ${name}`, onClick: () => capture('article_share_clicked', {
83
+ destination,
84
+ shareUrl: targetUrl,
85
+ surface: 'actions_menu_share_submenu',
86
+ }), children: [_jsx(Icon, { className: "mr-2 w-4 h-4" }), name] }) }, name));
87
+ }) }));
52
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"article-actions.d.ts","sourceRoot":"","sources":["../../../src/components/ui/article-actions.tsx"],"names":[],"mappings":"AASA,wBAAgB,cAAc,4CAqE7B"}
1
+ {"version":3,"file":"article-actions.d.ts","sourceRoot":"","sources":["../../../src/components/ui/article-actions.tsx"],"names":[],"mappings":"AASA,wBAAgB,cAAc,4CA6G7B"}
@@ -5,16 +5,29 @@ import { Citation } from './citation';
5
5
  import { ArticleActionsDropdown } from './article-actions-dropdown';
6
6
  import { useDownloads } from './article-actions-downloads';
7
7
  import { SearchDialog } from './search';
8
- import { useDesign } from '../providers';
8
+ import { useAnalytics, useDesign } from '../providers';
9
9
  import { useBaseurl, withBaseurl } from '@myst-theme/providers';
10
10
  export function ArticleActions() {
11
11
  const { projectDownloads, articleDownloads } = useDownloads();
12
12
  const design = useDesign();
13
13
  const baseurl = useBaseurl();
14
+ const capture = useAnalytics();
14
15
  // Combine all downloads for PDF search
15
16
  const allDownloads = [...projectDownloads, ...articleDownloads];
16
17
  // Find PDF download
17
18
  const pdfDownload = allDownloads.find((download) => download.format?.toLowerCase() === 'pdf' || download.url?.toLowerCase().endsWith('.pdf'));
18
- const pdfDownloadUrl = baseurl ? withBaseurl('.pdf', baseurl) : pdfDownload?.url;
19
- return (_jsxs("div", { className: "flex gap-2 items-center", children: [!design.hide_search && (_jsx(SearchDialog, { children: _jsx(SimpleTooltip, { title: "Search", children: _jsx(Button, { variant: "outline", size: "icon", "aria-label": "Search", asChild: true, children: _jsx("div", { children: _jsx(SearchIcon, {}) }) }) }) })), pdfDownload && (_jsx(SimpleTooltip, { title: "Download PDF", children: _jsx(Button, { variant: "outline", className: "cursor-pointer", size: "icon", "aria-label": "Download PDF", asChild: true, children: _jsx("a", { href: pdfDownloadUrl, target: "_blank", rel: "noopener noreferrer", children: _jsx(FileDown, {}) }) }) })), _jsx(Citation, { children: _jsx(SimpleTooltip, { title: "Cite this article", children: _jsx(Button, { variant: "outline", className: "cursor-pointer", size: "icon", "aria-label": "Cite this article", children: _jsx(QuoteIcon, {}) }) }) }), _jsx(ArticleActionsDropdown, { children: _jsx(Button, { variant: "ghost", size: "icon", className: "cursor-pointer", children: _jsx(EllipsisVerticalIcon, {}) }) })] }));
19
+ const pdfDownloadScope = pdfDownload && projectDownloads.includes(pdfDownload) ? 'project' : 'article';
20
+ // When nice_pdf_links is disabled, always use the server-provided download URL rather than the
21
+ // rewritten `{baseurl}.pdf` link, which may not resolve in environments like theme previews.
22
+ const useNicePdfLink = design.nice_pdf_links !== false;
23
+ const pdfDownloadUrl = useNicePdfLink && baseurl ? withBaseurl('.pdf', baseurl) : pdfDownload?.url;
24
+ return (_jsxs("div", { className: "flex gap-2 items-center", children: [!design.hide_search && (_jsx(SearchDialog, { onOpenChange: (open) => open && capture('search_opened', { surface: 'article_bar' }), children: _jsx(SimpleTooltip, { title: "Search", children: _jsx(Button, { variant: "outline", size: "icon", "aria-label": "Search", asChild: true, children: _jsx("div", { children: _jsx(SearchIcon, {}) }) }) }) })), pdfDownload && (_jsx(SimpleTooltip, { title: "Download PDF", children: _jsx(Button, { variant: "outline", className: "hidden cursor-pointer sm:inline-flex", size: "icon", "aria-label": "Download PDF", asChild: true, children: _jsx("a", { href: pdfDownloadUrl, target: "_blank", rel: "noopener noreferrer", onClick: () => capture('article_download', {
25
+ action: 'clicked',
26
+ category: 'download',
27
+ format: 'pdf',
28
+ downloadUrl: pdfDownloadUrl,
29
+ downloadFilename: pdfDownload.filename,
30
+ scope: pdfDownloadScope,
31
+ surface: 'article_bar_button',
32
+ }), children: _jsx(FileDown, {}) }) }) })), _jsx(Citation, { onOpenChange: (open) => open && capture('article_citation_opened', { surface: 'article_bar_button' }), children: _jsx(SimpleTooltip, { title: "Cite this article", children: _jsx(Button, { variant: "outline", className: "hidden cursor-pointer sm:inline-flex", size: "icon", "aria-label": "Cite this article", children: _jsx(QuoteIcon, {}) }) }) }), _jsx(ArticleActionsDropdown, { onOpenChange: (open) => open && capture('article_actions_menu_opened', { surface: 'article_bar' }), children: _jsx(SimpleTooltip, { title: "More article actions", children: _jsx(Button, { variant: "ghost", size: "icon", className: "cursor-pointer", "aria-label": "More article actions", children: _jsx(EllipsisVerticalIcon, {}) }) }) })] }));
20
33
  }