@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
@@ -1,5 +1,5 @@
1
1
  import type { LayoutProps } from './types';
2
- export declare function AppLayout({ children, header, footer }: LayoutProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function AppLayout({ children, header, footer, trailing }: LayoutProps): import("react/jsx-runtime").JSX.Element;
3
3
  export declare function AppContainer({ children, header, className, }: {
4
4
  children: React.ReactNode;
5
5
  header?: React.ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"app-layout.d.ts","sourceRoot":"","sources":["../../../src/components/layout/app-layout.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG3C,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,2CAuBlE;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,2CAwBA"}
1
+ {"version":3,"file":"app-layout.d.ts","sourceRoot":"","sources":["../../../src/components/layout/app-layout.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG3C,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAuB5E;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,2CAyBA"}
@@ -8,17 +8,18 @@ import { cn } from '../../lib/utils';
8
8
  import { Banner } from '../ui';
9
9
  import { createSkipToTargets, SkipTo } from '../ui/accessibility-skip-to';
10
10
  import { useDesign } from '../providers';
11
- export function AppLayout({ children, header, footer }) {
11
+ export function AppLayout({ children, header, footer, trailing }) {
12
12
  return (_jsxs(_Fragment, { children: [_jsx(SkipTo, { targets: createSkipToTargets() }), _jsx("div", { className: cn('flex flex-col min-h-screen', {
13
13
  '[--header-height:calc(--spacing(22))]': header,
14
14
  '[--header-height:calc(--spacing(14))]': !header,
15
- }), children: _jsxs(SidebarProvider, { className: "flex flex-col h-full", children: [_jsx(SiteHeader, { brandBar: header, showMobileMenu: true }), _jsxs("div", { className: "flex flex-1 pt-[var(--header-height)]", children: [_jsx(AppSidebar, {}), _jsxs(SidebarInset, { className: "flex flex-col min-h-full", children: [_jsx("div", { className: "flex-grow", children: children }), footer && _jsx("div", { className: "flex-shrink-0", children: footer })] })] })] }) })] }));
15
+ }), children: _jsxs(SidebarProvider, { className: "flex flex-col h-full", children: [_jsx(SiteHeader, { brandBar: header, trailing: trailing, showMobileMenu: true }), _jsxs("div", { className: "flex flex-1", children: [_jsx(AppSidebar, {}), _jsxs(SidebarInset, { className: "flex flex-col min-h-full", children: [_jsx("div", { className: "grow", children: children }), footer && _jsx("div", { className: "shrink-0", children: footer })] })] })] }) })] }));
16
16
  }
17
17
  export function AppContainer({ children, header, className, }) {
18
18
  const references = useReferences();
19
19
  const project = useProjectManifest();
20
20
  const design = useDesign();
21
- return (_jsxs("article", { className: cn('relative app-grid grid-gap z-0 max-w-full article', className, {
22
- 'pt-8': !project?.banner,
23
- }), children: [project?.banner && (_jsx(Banner, { thin: true, banner: project.banner, bannerOptimized: project.bannerOptimized })), header, !!header && _jsx("div", { className: "h-0 col-screen" }), !design.hide_outline && references?.article && (_jsx("aside", { className: "hidden sticky top-24 px-2 h-0 lg:block col-margin xl:px-4", children: _jsx(DocumentOutline, { mdast: references?.article }) })), children, _jsx("div", { id: "bottom-of-article", className: "h-0 col-screen" })] }));
21
+ const hasBanner = !design.hide_banner && project?.banner;
22
+ return (_jsxs("article", { className: cn('relative z-0 max-w-full app-grid grid-gap article', className, {
23
+ 'pt-8': !hasBanner && !design.hide_title_block,
24
+ }), children: [hasBanner && !design.hide_title_block && project?.banner && (_jsx(Banner, { thin: true, banner: project.banner, bannerOptimized: project.bannerOptimized })), header, !!header && _jsx("div", { className: "h-0 col-screen" }), !design.hide_outline && references?.article && (_jsx("aside", { className: "hidden sticky top-24 px-2 h-0 lg:block col-margin xl:px-4", children: _jsx(DocumentOutline, { mdast: references?.article }) })), children, _jsx("div", { id: "bottom-of-article", className: "h-0 col-screen" })] }));
24
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"app-sidebar.d.ts","sourceRoot":"","sources":["../../../src/components/layout/app-sidebar.tsx"],"names":[],"mappings":"AA8PA,wBAAgB,UAAU,4CA2EzB"}
1
+ {"version":3,"file":"app-sidebar.d.ts","sourceRoot":"","sources":["../../../src/components/layout/app-sidebar.tsx"],"names":[],"mappings":"AA8PA,wBAAgB,UAAU,4CA0FzB"}
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { ChevronRight, Home, Notebook } from 'lucide-react';
3
3
  import * as React from 'react';
4
- import { useNavigation, useLocation, Link, NavLink } from 'react-router';
5
- import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, } from '../ui/sidebar';
4
+ import { useNavigation, useLocation, NavLink } from 'react-router';
5
+ import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuItem, } from '../ui/sidebar';
6
6
  import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../ui';
7
7
  import { cn } from '../../lib/utils';
8
8
  import { getYear } from '../../lib/parse-date';
@@ -11,6 +11,7 @@ import { ThemeButton } from '../ui/theme-button';
11
11
  import { useBaseurl, useProjectManifest } from '@myst-theme/providers';
12
12
  import { useHydrated } from '../../hooks';
13
13
  import { usePage } from '../providers';
14
+ import { useDesign } from '../providers/design';
14
15
  const icons = {
15
16
  notebook: Notebook,
16
17
  notebooks: Notebook,
@@ -109,6 +110,7 @@ export function AppSidebar() {
109
110
  const currentPage = page.slug;
110
111
  const project = useProjectManifest();
111
112
  const baseUrl = useBaseurl() ?? '';
113
+ const { hide_powered_by, hide_sidebar_footer } = useDesign();
112
114
  // Build nested structure from config pages
113
115
  const sidebarItems = React.useMemo(() => {
114
116
  if (!project?.pages) {
@@ -118,8 +120,10 @@ export function AppSidebar() {
118
120
  }, [project]);
119
121
  const { title, authors, date: dateString } = project ?? {};
120
122
  const year = getYear(dateString);
121
- return (_jsxs(Sidebar, { className: "top-(--header-height) h-[calc(100svh-var(--header-height))]!", children: [_jsx(SidebarHeader, { children: _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children: _jsx(SidebarMenuButton, { size: "lg", asChild: true, children: _jsxs(Link, { to: baseUrl || '/', children: [_jsx("div", { className: "flex justify-center items-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground aspect-square size-8", children: _jsx(Home, { className: "size-4" }) }), _jsxs("div", { className: "grid flex-1 text-sm leading-tight text-left", children: [_jsx("span", { className: "font-medium truncate", children: title }), authors && (_jsxs("div", { className: "flex overflow-hidden gap-1 items-center", children: [_jsxs("div", { className: "text-xs truncate", children: [authors
122
- .map((author) => author.nameParsed?.family)
123
- .slice(0, 3)
124
- .join(', '), ' '] }), year && _jsxs("div", { className: "text-xs truncate shrink-0", children: ["(", year, ")"] })] }))] })] }) }) }) }) }), _jsx(SidebarContent, { children: _jsxs(SidebarGroup, { children: [_jsx(SidebarGroupLabel, { children: "Pages" }), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: sidebarItems.map((item) => (_jsx(SidebarItemComponent, { item: item, currentPage: currentPage }, item.title))) }) })] }) }), _jsx(SidebarFooter, { className: "border-t", children: _jsxs("div", { className: "flex gap-2 pl-2 opacity-80 grayscale hover:grayscale-0 hover:opacity-100", children: [_jsxs("a", { href: "https://curvenote.com", target: "_blank", rel: "noopener noreferrer", className: "flex gap-2 items-center my-1 text-xs transition-all duration-300 text-sidebar-secondary-foreground grow", children: [_jsx(CurvenoteLogo, { size: 16 }), _jsx("span", { children: "Powered by Curvenote" })] }), _jsx("div", { className: "flex-0", children: _jsx(ThemeButton, {}) })] }) })] }));
123
+ return (_jsxs(Sidebar, { className: "top-(--header-height) h-[calc(100svh-var(--header-height))]!", children: [_jsx(SidebarHeader, { children: _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children: _jsxs(NavLink, { to: baseUrl || '/', prefetch: "intent", end: true, className: ({ isActive }) => cn('flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2', {
124
+ 'bg-sidebar-accent text-sidebar-accent-foreground ': isActive,
125
+ }), children: [_jsx("div", { className: "flex justify-center items-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground aspect-square size-8", children: _jsx(Home, { className: "size-4" }) }), _jsxs("div", { className: "grid flex-1 text-sm leading-tight text-left", children: [_jsx("span", { className: "font-medium truncate", children: title }), authors && (_jsxs("div", { className: "flex overflow-hidden gap-1 items-center", children: [_jsxs("div", { className: "text-xs truncate", children: [authors
126
+ .map((author) => author.nameParsed?.family)
127
+ .slice(0, 3)
128
+ .join(', '), ' '] }), year && _jsxs("div", { className: "text-xs truncate shrink-0", children: ["(", year, ")"] })] }))] })] }) }) }) }), _jsx(SidebarContent, { children: _jsxs(SidebarGroup, { children: [_jsx(SidebarGroupLabel, { children: "Pages" }), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: sidebarItems.map((item) => (_jsx(SidebarItemComponent, { item: item, currentPage: currentPage }, item.title))) }) })] }) }), !hide_sidebar_footer && (_jsx(SidebarFooter, { className: "border-t", children: _jsxs("div", { className: "flex gap-2 pl-2 opacity-80 grayscale hover:grayscale-0 hover:opacity-100", children: [!hide_powered_by && (_jsxs("a", { href: "https://curvenote.com", target: "_blank", rel: "noopener noreferrer", className: "flex gap-2 items-center my-1 text-xs transition-all duration-300 text-sidebar-secondary-foreground grow", children: [_jsx(CurvenoteLogo, { size: 16 }), _jsx("span", { children: "Powered by Curvenote" })] })), _jsx("div", { className: cn('flex-0', { 'ml-auto': hide_powered_by }), children: _jsx(ThemeButton, {}) })] }) }))] }));
125
129
  }
@@ -1,5 +1,5 @@
1
1
  import type { LayoutProps } from './types';
2
- export declare function ArticleLayout({ children, header, footer }: LayoutProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function ArticleLayout({ children, header, footer, trailing }: LayoutProps): import("react/jsx-runtime").JSX.Element;
3
3
  export declare function ArticleContainer({ children, header, className, }: {
4
4
  children: React.ReactNode;
5
5
  header?: React.ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"article-layout.d.ts","sourceRoot":"","sources":["../../../src/components/layout/article-layout.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG3C,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,2CAWtE;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,2CAiCA"}
1
+ {"version":3,"file":"article-layout.d.ts","sourceRoot":"","sources":["../../../src/components/layout/article-layout.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG3C,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAWhF;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,2CAgCA"}
@@ -4,17 +4,16 @@ import { SiteHeader } from './site-header';
4
4
  import { cn } from '../../lib/utils';
5
5
  import { createSkipToTargets, DocumentOutline, SkipTo, Banner } from '../ui';
6
6
  import { useDesign } from '../providers';
7
- export function ArticleLayout({ children, header, footer }) {
8
- return (_jsxs(_Fragment, { children: [_jsx(SkipTo, { targets: createSkipToTargets() }), _jsxs("div", { className: "flex flex-col min-h-screen", children: [_jsx(SiteHeader, { brandBar: header }), _jsx("div", { className: "flex-grow", children: children }), footer && _jsx("div", { className: "flex-shrink-0", children: footer })] })] }));
7
+ export function ArticleLayout({ children, header, footer, trailing }) {
8
+ return (_jsxs(_Fragment, { children: [_jsx(SkipTo, { targets: createSkipToTargets() }), _jsxs("div", { className: "flex flex-col min-h-screen", children: [_jsx(SiteHeader, { brandBar: header, trailing: trailing }), _jsx("div", { className: "grow", children: children }), footer && _jsx("div", { className: "shrink-0", children: footer })] })] }));
9
9
  }
10
10
  export function ArticleContainer({ children, header, className, }) {
11
11
  const references = useReferences();
12
12
  const project = useProjectManifest();
13
13
  const design = useDesign();
14
- return (_jsxs("article", { className: cn('relative article-grid grid-gap prose dark:prose-invert w-full max-w-full', {
15
- // if the banner is shown, or the title block is hidden, this padding accounts for the site header
16
- 'pt-14': project?.banner || design.hide_title_block,
14
+ const hasBanner = !design.hide_banner && project?.banner;
15
+ return (_jsxs("article", { className: cn('relative w-full max-w-full article-grid grid-gap prose dark:prose-invert', {
17
16
  // if the banner is not shown, and the title block is not hidden, then more padding is needed
18
- 'pt-28': !project?.banner && !design.hide_title_block,
19
- }, 'z-0', className), children: [project?.banner && !design.hide_title_block && (_jsx(Banner, { inset: true, banner: project.banner, bannerOptimized: project.bannerOptimized })), header, !!header && _jsx("div", { className: "col-screen h-0" }), !design.hide_outline && references?.article && (_jsx("aside", { className: "hidden sticky top-24 px-2 h-0 lg:block col-margin xl:px-4", children: _jsx(DocumentOutline, { mdast: references?.article }) })), children, _jsx("div", { id: "bottom-of-article", className: "col-screen h-0" })] }));
17
+ 'pt-14': !hasBanner && !design.hide_title_block,
18
+ }, 'z-0', className), children: [hasBanner && !design.hide_title_block && project?.banner && (_jsx(Banner, { inset: true, banner: project.banner, bannerOptimized: project.bannerOptimized })), header, !!header && _jsx("div", { className: "h-0 col-screen" }), !design.hide_outline && references?.article && (_jsx("aside", { className: "hidden sticky top-24 px-2 h-0 lg:block col-margin xl:px-4", children: _jsx(DocumentOutline, { mdast: references?.article }) })), children, _jsx("div", { id: "bottom-of-article", className: "h-0 col-screen" })] }));
20
19
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { LoadingBar } from './loading-bar';
3
3
  export function HeaderContainer({ brandBar, children, }) {
4
- return (_jsx("header", { className: "fixed top-0 z-50 w-full border-b bg-background", children: _jsxs("div", { className: "flex flex-col items-center", children: [brandBar && _jsx("div", { className: "w-full shrink-0", children: brandBar }), children, _jsx(LoadingBar, {})] }) }));
4
+ return (_jsx("header", { className: "sticky top-0 z-50 w-full border-b bg-background", children: _jsxs("div", { className: "flex flex-col items-center", children: [brandBar && _jsx("div", { className: "w-full shrink-0", children: brandBar }), children, _jsx(LoadingBar, {})] }) }));
5
5
  }
@@ -4,4 +4,5 @@ export * from './article-layout';
4
4
  export * from './site-header';
5
5
  export * from './loading-bar';
6
6
  export * from './grid';
7
+ export * from './types';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/layout/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/layout/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC"}
@@ -5,3 +5,4 @@ export * from './article-layout';
5
5
  export * from './site-header';
6
6
  export * from './loading-bar';
7
7
  export * from './grid';
8
+ export * from './types';
@@ -5,8 +5,15 @@ export declare function useLoading(): {
5
5
  showLoading: boolean;
6
6
  isLoading: boolean;
7
7
  };
8
- export declare function LoadingBar({ align, className, }: {
8
+ export declare function LoadingBar({ align, className, position, fixedTopClassName, fixedTop, portal, }: {
9
9
  align?: 'bottom' | 'top';
10
10
  className?: string;
11
+ /** `fixed` pins the bar to the viewport (e.g. under a `h-14` nav). */
12
+ position?: 'absolute' | 'relative' | 'fixed';
13
+ fixedTopClassName?: string;
14
+ /** Viewport `top` in px for `position: fixed`. Overrides `fixedTopClassName`. */
15
+ fixedTop?: number;
16
+ /** Render via portal so `fixed` is not trapped by ancestor `backdrop-filter` / `transform`. */
17
+ portal?: boolean;
11
18
  }): import("react/jsx-runtime").JSX.Element | null;
12
19
  //# sourceMappingURL=loading-bar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loading-bar.d.ts","sourceRoot":"","sources":["../../../src/components/layout/loading-bar.tsx"],"names":[],"mappings":"AAIA;;GAEG;AACH,wBAAgB,UAAU;;;EAmCzB;AAED,wBAAgB,UAAU,CAAC,EACzB,KAAgB,EAChB,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,kDAiBA"}
1
+ {"version":3,"file":"loading-bar.d.ts","sourceRoot":"","sources":["../../../src/components/layout/loading-bar.tsx"],"names":[],"mappings":"AAKA;;GAEG;AACH,wBAAgB,UAAU;;;EAmCzB;AAED,wBAAgB,UAAU,CAAC,EACzB,KAAgB,EAChB,SAAS,EACT,QAAqB,EACrB,iBAA4B,EAC5B,QAAQ,EACR,MAAc,GACf,EAAE;IACD,KAAK,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,kDA+BA"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useNavigation } from 'react-router';
3
3
  import { useEffect, useMemo, useState } from 'react';
4
+ import { createPortal } from 'react-dom';
4
5
  import { cn } from '../../lib/utils';
5
6
  /**
6
7
  * Show a loading progress bar if the load takes more than 150ms
@@ -40,14 +41,21 @@ export function useLoading() {
40
41
  }, [transitionState]);
41
42
  return { showLoading, isLoading: transitionState === 'loading' };
42
43
  }
43
- export function LoadingBar({ align = 'bottom', className, }) {
44
+ export function LoadingBar({ align = 'bottom', className, position = 'absolute', fixedTopClassName = 'top-14', fixedTop, portal = false, }) {
44
45
  const { isLoading, showLoading } = useLoading();
45
46
  if (!showLoading)
46
47
  return null;
47
- return (_jsx("div", { className: cn('absolute left-0 w-screen bg-blue-500 transition-transform origin-left h-[2px]', {
48
- 'bottom-0': align === 'bottom',
49
- 'top-0': align === 'top',
48
+ const bar = (_jsx("div", { role: "progressbar", "aria-hidden": "true", style: position === 'fixed' && fixedTop !== undefined ? { top: fixedTop } : undefined, className: cn('left-0 bg-blue-500 transition-transform origin-left h-[2px] pointer-events-none', position === 'fixed'
49
+ ? cn('fixed z-[100] w-full', fixedTop === undefined && fixedTopClassName)
50
+ : cn('w-screen', position, {
51
+ 'bottom-0': align === 'bottom',
52
+ 'top-0': align === 'top',
53
+ }), {
50
54
  'animate-load scale-x-40': isLoading,
51
55
  'scale-x-100': !isLoading,
52
56
  }, className) }));
57
+ if (portal && typeof document !== 'undefined') {
58
+ return createPortal(bar, document.body);
59
+ }
60
+ return bar;
53
61
  }
@@ -1,8 +1,13 @@
1
- export declare function SiteHeader({ showSearch, showBreadcrumbs, showArticleActions, showMobileMenu, brandBar, }: {
1
+ export declare function SiteHeader({ showSearch, showBreadcrumbs, showArticleActions, showMobileMenu, brandBar, trailing, }: {
2
2
  showSearch?: boolean;
3
3
  showBreadcrumbs?: boolean;
4
4
  showArticleActions?: boolean;
5
5
  showMobileMenu?: boolean;
6
6
  brandBar?: React.ReactNode;
7
+ /**
8
+ * Arbitrary content rendered at the trailing (right) edge of the header,
9
+ * after any configured actions/search and before the mobile menu button.
10
+ */
11
+ trailing?: React.ReactNode;
7
12
  }): import("react/jsx-runtime").JSX.Element;
8
13
  //# sourceMappingURL=site-header.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"site-header.d.ts","sourceRoot":"","sources":["../../../src/components/layout/site-header.tsx"],"names":[],"mappings":"AAqKA,wBAAgB,UAAU,CAAC,EACzB,UAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,cAAqB,EACrB,QAAQ,GACT,EAAE;IACD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,2CA6IA"}
1
+ {"version":3,"file":"site-header.d.ts","sourceRoot":"","sources":["../../../src/components/layout/site-header.tsx"],"names":[],"mappings":"AAuKA,wBAAgB,UAAU,CAAC,EACzB,UAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,cAAqB,EACrB,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,2CAwKA"}
@@ -2,12 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useState } from 'react';
3
3
  import { ExternalLink, ChevronDown, MenuIcon, SearchIcon, SidebarIcon } from 'lucide-react';
4
4
  import { NavLink } from 'react-router';
5
- import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, buttonVariants, SearchDialog, SimpleTooltip, BreadcrumbList, Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator, BreadcrumbPage, Separator, ArticleActions, } from '../ui';
5
+ import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, buttonVariants, SearchDialog, SimpleTooltip, BreadcrumbList, Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator, BreadcrumbPage, Separator, ArticleActions, ThemeButton, } from '../ui';
6
6
  import { cn } from '../../lib/utils';
7
7
  import { usePlatformConfig, usePlatformSite } from '../providers/platform-providers';
8
8
  import { HeaderContainer } from './header';
9
9
  import { useBaseurl, useFrontmatter, useProjectManifest } from '@myst-theme/providers';
10
10
  import { useSidebar } from '../ui/sidebar';
11
+ import { useDesign } from '../providers';
11
12
  function NavigationItem({ item }) {
12
13
  if (item.children && item.children.length > 0) {
13
14
  return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", className: "text-sm font-medium", children: [item.title, _jsx(ChevronDown, { className: "w-3 h-3" })] }) }), _jsx(DropdownMenuContent, { align: "end", children: item.children.map((child) => (_jsx(DropdownMenuItem, { children: _jsx(NavigationItem, { item: child }) }, child.title))) })] }, item.title));
@@ -28,9 +29,10 @@ function MobileNavigationItem({ item, closeMobileMenu, }) {
28
29
  }
29
30
  return (_jsx(NavLink, { to: item.url, className: ({ isActive }) => cn('text-sm font-medium transition-colors hover:text-primary py-2 px-3 hover:bg-muted block', isActive && 'text-primary font-semibold bg-muted'), onClick: closeMobileMenu, children: item.title }, item.title));
30
31
  }
31
- export function SiteHeader({ showSearch = false, showBreadcrumbs, showArticleActions, showMobileMenu = true, brandBar, }) {
32
+ export function SiteHeader({ showSearch = false, showBreadcrumbs, showArticleActions, showMobileMenu = true, brandBar, trailing, }) {
32
33
  const { toggleSidebar } = useSidebar() ?? {};
33
34
  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
35
+ const { hide_theme_toggle_site_header } = useDesign();
34
36
  const site = usePlatformSite();
35
37
  const config = usePlatformConfig();
36
38
  const project = useProjectManifest();
@@ -40,15 +42,16 @@ export function SiteHeader({ showSearch = false, showBreadcrumbs, showArticleAct
40
42
  const showNavLinksOnMediumScreen = (nav?.length ?? 0) < 5;
41
43
  const showNavLinksOnLargeScreen = (nav?.length ?? 0) >= 5;
42
44
  const logo = site?.logo || config?.options?.logo;
45
+ const logo_dark = site?.logo_dark || config?.options?.logo_dark;
43
46
  const logo_text = config?.options?.logo_text;
44
47
  const title = site?.title || config?.title;
45
- return (_jsx(HeaderContainer, { brandBar: brandBar, children: _jsxs("div", { className: "flex items-center px-2 w-full h-14", children: [_jsxs("div", { className: cn('flex gap-2 items-center h-full', { 'md:w-[200px]': !showBreadcrumbs }), children: [toggleSidebar && (_jsxs(_Fragment, { children: [_jsx(Button, { className: "w-8 h-8", variant: "ghost", size: "icon", onClick: toggleSidebar, children: _jsx(SidebarIcon, {}) }), _jsx(Separator, { orientation: "vertical", className: "mr-2 h-4" })] })), (logo || logo_text) && (_jsxs("a", { href: "/", className: "flex gap-2 items-center mr-2 shrink-0", children: [logo && _jsx("img", { src: logo, alt: title || 'Site Logo', className: "w-auto h-8 max-h-8" }), logo_text && _jsx("div", { className: "text-lg", children: logo_text })] }))] }), showBreadcrumbs && (_jsx(Breadcrumb, { className: "hidden sm:block grow", children: _jsxs(BreadcrumbList, { children: [_jsx(BreadcrumbItem, { children: _jsx(BreadcrumbLink, { href: baseurl, className: "truncate max-w-12 md:max-w-24 lg:max-w-48 xl:max-w-64", children: project?.title }) }), _jsx(BreadcrumbSeparator, {}), _jsx(BreadcrumbItem, { children: _jsx(BreadcrumbPage, { className: "truncate max-w-64 lg:max-w-96", children: article?.title }) })] }) })), !showBreadcrumbs && (_jsx("nav", { className: cn('hidden flex-1 gap-1 justify-center items-center', {
48
+ return (_jsx(HeaderContainer, { brandBar: brandBar, children: _jsxs("div", { className: "flex items-center px-2 w-full h-14", children: [_jsxs("div", { className: cn('flex gap-2 items-center h-full', { 'md:w-[200px]': !showBreadcrumbs }), children: [toggleSidebar && (_jsxs(_Fragment, { children: [_jsx(SimpleTooltip, { title: "Toggle sidebar", children: _jsx(Button, { className: "w-8 h-8", variant: "ghost", size: "icon", onClick: toggleSidebar, "aria-label": "Toggle sidebar", children: _jsx(SidebarIcon, { "aria-hidden": "true", focusable: "false" }) }) }), _jsx(Separator, { orientation: "vertical", className: "mr-2 h-4" })] })), (logo || logo_dark || logo_text) && (_jsxs("a", { href: "/", className: "flex gap-2 items-center mr-2 shrink-0", children: [logo && (_jsx("img", { src: logo, alt: title || 'Site Logo', className: cn('w-auto h-8 max-h-8', { 'dark:hidden': !!logo_dark }) })), logo_dark && (_jsx("img", { src: logo_dark, alt: title || 'Site Logo', className: "hidden w-auto h-8 max-h-8 dark:block" })), logo_text && _jsx("div", { className: "text-lg", children: logo_text })] }))] }), showBreadcrumbs && (_jsx(Breadcrumb, { className: "hidden sm:block grow", children: _jsxs(BreadcrumbList, { children: [_jsx(BreadcrumbItem, { children: _jsx(BreadcrumbLink, { href: baseurl, className: "truncate max-w-12 md:max-w-24 lg:max-w-48 xl:max-w-64", children: project?.title }) }), _jsx(BreadcrumbSeparator, {}), _jsx(BreadcrumbItem, { children: _jsx(BreadcrumbPage, { className: "truncate max-w-64 lg:max-w-96", children: article?.title }) })] }) })), !showBreadcrumbs && (_jsx("nav", { className: cn('hidden flex-1 gap-1 justify-center items-center', {
46
49
  'md:flex': showNavLinksOnMediumScreen,
47
50
  'lg:flex': showNavLinksOnLargeScreen,
48
51
  }), children: nav?.map((item) => (_jsx(NavigationItem, { item: item }, item.title || item.url))) })), _jsxs("div", { className: cn('flex gap-2 justify-end items-center', {
49
52
  'grow md:grow-0 md:w-[200px]': showNavLinksOnMediumScreen,
50
53
  'grow lg:grow-0 lg:w-[200px]': showNavLinksOnLargeScreen,
51
- }), children: [actions?.map((action) => (_jsx(Button, { variant: "outline", "aria-label": action.title, asChild: true, children: _jsx("a", { href: action.url, target: "_blank", rel: "noopener noreferrer", children: action.title }) }, action.title))), showSearch && (_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, {}) }) }) }) })), showArticleActions && _jsx(ArticleActions, {}), showMobileMenu && nav && nav.length > 0 && (_jsxs(Sheet, { open: isMobileMenuOpen, onOpenChange: setIsMobileMenuOpen, children: [_jsx(SheetTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "sm", className: cn({
54
+ }), children: [actions?.map((action) => (_jsx(Button, { variant: "outline", "aria-label": action.title, asChild: true, children: _jsx("a", { href: action.url, target: "_blank", rel: "noopener noreferrer", children: action.title }) }, action.title))), showSearch && (_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, {}) }) }) }) })), showArticleActions && _jsx(ArticleActions, {}), trailing, hide_theme_toggle_site_header === false && _jsx(ThemeButton, {}), showMobileMenu && nav && nav.length > 0 && (_jsxs(Sheet, { open: isMobileMenuOpen, onOpenChange: setIsMobileMenuOpen, children: [_jsx(SheetTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "sm", className: cn({
52
55
  'md:hidden': !showBreadcrumbs && showNavLinksOnMediumScreen,
53
56
  'lg:hidden': !showBreadcrumbs && showNavLinksOnLargeScreen,
54
57
  }), children: _jsx(MenuIcon, { className: "w-5 h-5" }) }) }), _jsxs(SheetContent, { side: "right", className: "w-[300px] sm:w-[400px]", children: [_jsx(SheetHeader, { children: _jsx(SheetTitle, { children: "Navigation" }) }), _jsx("div", { className: "flex flex-col mt-6 space-y-4", children: _jsx("nav", { className: "flex flex-col space-y-2", children: nav?.map((item) => (_jsx(MobileNavigationItem, { item: item, closeMobileMenu: () => setIsMobileMenuOpen(false) }, item.title || item.url))) }) })] })] }))] })] }) }));
@@ -2,5 +2,11 @@ export interface LayoutProps {
2
2
  children: React.ReactNode;
3
3
  header?: React.ReactNode;
4
4
  footer?: React.ReactNode;
5
+ /**
6
+ * Arbitrary content rendered at the trailing (right) edge of the top
7
+ * navigation bar, after any configured actions/search and before the mobile
8
+ * menu button. Use for app-specific controls (e.g. a logs tray button).
9
+ */
10
+ trailing?: React.ReactNode;
5
11
  }
6
12
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/layout/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC1B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/layout/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B"}
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from 'react';
2
+ export type AnalyticsCaptureFn = (event: string, properties?: Record<string, unknown>) => void;
3
+ export declare function AnalyticsBoundary({ capture, context, children, }: {
4
+ capture: AnalyticsCaptureFn;
5
+ context?: Record<string, unknown>;
6
+ children: ReactNode;
7
+ }): import("react/jsx-runtime").JSX.Element;
8
+ /**
9
+ * Capture an analytics event. Merges per-call properties with the article-scoped
10
+ * context supplied by the nearest AnalyticsBoundary, adds `overlay_depth` from
11
+ * {@link OverlayInteractionDepthContext} (unless overridden in properties), transforms
12
+ * all keys to snake_case (PostHog convention), and forwards to the boundary's capture fn.
13
+ *
14
+ * Returns a no-op when no boundary is mounted, so packages/ui components can
15
+ * call this unconditionally.
16
+ */
17
+ export declare function useAnalytics(): AnalyticsCaptureFn;
18
+ //# sourceMappingURL=analytics-boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-boundary.d.ts","sourceRoot":"","sources":["../../../src/components/providers/analytics-boundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKxF,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AA0B/F,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAWA;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,IAAI,kBAAkB,CAmBjD"}
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useMemo } from 'react';
3
+ import { OverlayInteractionDepthContext } from './overlay-depth';
4
+ import { AnalyticsDevTray } from './analytics-dev-tray';
5
+ import { isAnalyticsDevMode, recordAnalyticsDevEvent } from './analytics-dev-log';
6
+ const AnalyticsBoundaryContext = createContext(undefined);
7
+ function camelToSnake(s) {
8
+ return s
9
+ .replace(/([A-Z])/g, '_$1')
10
+ .replace(/^_/, '')
11
+ .toLowerCase();
12
+ }
13
+ function toSnakeKeys(input) {
14
+ if (!input)
15
+ return {};
16
+ const out = {};
17
+ for (const [k, v] of Object.entries(input)) {
18
+ if (v === undefined)
19
+ continue;
20
+ out[camelToSnake(k)] = v;
21
+ }
22
+ return out;
23
+ }
24
+ export function AnalyticsBoundary({ capture, context, children, }) {
25
+ const value = useMemo(() => ({ capture, context: context ?? {} }), [capture, context]);
26
+ return (_jsxs(AnalyticsBoundaryContext.Provider, { value: value, children: [children, isAnalyticsDevMode() && _jsx(AnalyticsDevTray, {})] }));
27
+ }
28
+ /**
29
+ * Capture an analytics event. Merges per-call properties with the article-scoped
30
+ * context supplied by the nearest AnalyticsBoundary, adds `overlay_depth` from
31
+ * {@link OverlayInteractionDepthContext} (unless overridden in properties), transforms
32
+ * all keys to snake_case (PostHog convention), and forwards to the boundary's capture fn.
33
+ *
34
+ * Returns a no-op when no boundary is mounted, so packages/ui components can
35
+ * call this unconditionally.
36
+ */
37
+ export function useAnalytics() {
38
+ const ctx = useContext(AnalyticsBoundaryContext);
39
+ const overlayDepth = useContext(OverlayInteractionDepthContext);
40
+ return useCallback((event, properties) => {
41
+ if (!ctx)
42
+ return;
43
+ const snakeCtx = toSnakeKeys(ctx.context);
44
+ const snakeProps = toSnakeKeys(properties);
45
+ const merged = {
46
+ ...snakeCtx,
47
+ overlay_depth: snakeProps.overlay_depth !== undefined ? snakeProps.overlay_depth : overlayDepth,
48
+ ...snakeProps,
49
+ };
50
+ ctx.capture(event, merged);
51
+ recordAnalyticsDevEvent(event, merged);
52
+ }, [ctx, overlayDepth]);
53
+ }
@@ -0,0 +1,11 @@
1
+ export type AnalyticsDevLogEntry = {
2
+ id: string;
3
+ timestamp: number;
4
+ event: string;
5
+ payload: Record<string, unknown>;
6
+ };
7
+ export declare function isAnalyticsDevMode(): boolean;
8
+ export declare function recordAnalyticsDevEvent(event: string, properties: Record<string, unknown>): void;
9
+ export declare function clearAnalyticsDevLog(): void;
10
+ export declare function useAnalyticsDevLog(): AnalyticsDevLogEntry[];
11
+ //# sourceMappingURL=analytics-dev-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-dev-log.d.ts","sourceRoot":"","sources":["../../../src/components/providers/analytics-dev-log.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC;AAYF,wBAAgB,kBAAkB,IAAI,OAAO,CAK5C;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAahG;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C;AAeD,wBAAgB,kBAAkB,2BAEjC"}
@@ -0,0 +1,46 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ let snapshot = [];
3
+ const serverSnapshot = [];
4
+ const listeners = new Set();
5
+ function notify() {
6
+ listeners.forEach((listener) => {
7
+ listener();
8
+ });
9
+ }
10
+ export function isAnalyticsDevMode() {
11
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
12
+ return true;
13
+ }
14
+ return Boolean(import.meta.env?.DEV);
15
+ }
16
+ export function recordAnalyticsDevEvent(event, properties) {
17
+ if (!isAnalyticsDevMode() || typeof window === 'undefined')
18
+ return;
19
+ snapshot = [
20
+ {
21
+ id: crypto.randomUUID(),
22
+ timestamp: Date.now(),
23
+ event,
24
+ payload: { event, ...properties },
25
+ },
26
+ ...snapshot,
27
+ ];
28
+ notify();
29
+ }
30
+ export function clearAnalyticsDevLog() {
31
+ snapshot = [];
32
+ notify();
33
+ }
34
+ function subscribe(listener) {
35
+ listeners.add(listener);
36
+ return () => listeners.delete(listener);
37
+ }
38
+ function getSnapshot() {
39
+ return snapshot;
40
+ }
41
+ function getServerSnapshot() {
42
+ return serverSnapshot;
43
+ }
44
+ export function useAnalyticsDevLog() {
45
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
46
+ }
@@ -0,0 +1,2 @@
1
+ export declare function AnalyticsDevTray(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=analytics-dev-tray.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-dev-tray.d.ts","sourceRoot":"","sources":["../../../src/components/providers/analytics-dev-tray.tsx"],"names":[],"mappings":"AA8CA,wBAAgB,gBAAgB,4CA2H/B"}
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useLayoutEffect, useRef, useState } from 'react';
3
+ import { cn } from '../../lib/utils';
4
+ import { clearAnalyticsDevLog, useAnalyticsDevLog, } from './analytics-dev-log';
5
+ function formatTime(timestamp) {
6
+ return new Date(timestamp).toLocaleTimeString();
7
+ }
8
+ async function copyText(text) {
9
+ await navigator.clipboard.writeText(text);
10
+ }
11
+ function LogDetail({ entry }) {
12
+ if (!entry) {
13
+ return (_jsx("div", { className: "px-3 py-2 text-xs border-t text-muted-foreground", children: "Select an event to inspect its payload." }));
14
+ }
15
+ const json = JSON.stringify(entry.payload, null, 2);
16
+ return (_jsxs("div", { className: "border-t", children: [_jsxs("div", { className: "flex gap-2 justify-between items-center px-3 py-2", children: [_jsx("div", { className: "min-w-0 font-mono text-xs font-semibold truncate", children: entry.event }), _jsx("button", { type: "button", className: "text-xs text-blue-600 shrink-0 hover:underline dark:text-blue-400", onClick: () => copyText(json), children: "Copy JSON" })] }), _jsx("pre", { className: "max-h-40 overflow-auto px-3 pb-3 font-mono text-[10px] leading-snug whitespace-pre-wrap break-all text-muted-foreground", children: json })] }));
17
+ }
18
+ export function AnalyticsDevTray() {
19
+ const logs = useAnalyticsDevLog();
20
+ const [open, setOpen] = useState(false);
21
+ const [flash, setFlash] = useState(false);
22
+ const [selectedId, setSelectedId] = useState(null);
23
+ const prevCountRef = useRef(logs.length);
24
+ const listRef = useRef(null);
25
+ useEffect(() => {
26
+ if (logs.length > prevCountRef.current) {
27
+ const newestId = logs[0]?.id;
28
+ if (newestId)
29
+ setSelectedId(newestId);
30
+ setFlash(true);
31
+ const timer = window.setTimeout(() => setFlash(false), 600);
32
+ prevCountRef.current = logs.length;
33
+ return () => window.clearTimeout(timer);
34
+ }
35
+ prevCountRef.current = logs.length;
36
+ }, [logs]);
37
+ useLayoutEffect(() => {
38
+ if (!open || !selectedId)
39
+ return;
40
+ listRef.current
41
+ ?.querySelector(`[data-log-id="${selectedId}"]`)
42
+ ?.scrollIntoView({ block: 'nearest' });
43
+ }, [logs, open, selectedId]);
44
+ useEffect(() => {
45
+ if (selectedId && !logs.some((entry) => entry.id === selectedId)) {
46
+ setSelectedId(logs[0]?.id ?? null);
47
+ }
48
+ }, [logs, selectedId]);
49
+ const selected = logs.find((entry) => entry.id === selectedId) ?? logs[0] ?? null;
50
+ return (_jsxs("div", { className: "fixed bottom-12 left-4 z-[200] flex flex-col items-start gap-2 pointer-events-none", children: [open && (_jsxs("div", { className: "pointer-events-auto flex w-[min(24rem,calc(100vw-2rem))] max-h-[min(50vh,28rem)] flex-col overflow-hidden rounded-lg border bg-popover text-popover-foreground shadow-xl", children: [_jsxs("div", { className: "flex gap-2 justify-between items-center px-3 py-2 border-b", children: [_jsx("div", { className: "text-sm font-medium", children: "Analytics session" }), _jsxs("div", { className: "flex gap-2 items-center", children: [_jsx("button", { type: "button", className: "text-xs text-blue-600 hover:underline dark:text-blue-400", onClick: () => copyText(JSON.stringify(logs.map((entry) => entry.payload), null, 2)), disabled: logs.length === 0, children: "Copy all" }), _jsx("button", { type: "button", className: "text-xs text-muted-foreground hover:underline", onClick: () => {
51
+ clearAnalyticsDevLog();
52
+ setSelectedId(null);
53
+ }, disabled: logs.length === 0, children: "Clear" }), _jsx("button", { type: "button", className: "text-xs text-muted-foreground hover:underline", onClick: () => setOpen(false), children: "Close" })] })] }), _jsx("ul", { ref: listRef, className: "overflow-auto flex-1 min-h-0", children: logs.length === 0 ? (_jsx("li", { className: "px-3 py-4 text-xs text-muted-foreground", children: "No events captured yet." })) : ([...logs].reverse().map((entry) => (_jsx("li", { "data-log-id": entry.id, children: _jsxs("button", { type: "button", onClick: () => setSelectedId(entry.id), className: cn('flex w-full items-start justify-between gap-3 px-3 py-2 text-left hover:bg-muted/60', selected?.id === entry.id && 'bg-muted'), children: [_jsx("span", { className: "min-w-0 font-mono text-xs font-medium break-all", children: entry.event }), _jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: formatTime(entry.timestamp) })] }) }, entry.id)))) }), _jsx(LogDetail, { entry: selected })] })), _jsxs("button", { type: "button", onClick: () => setOpen((value) => !value), className: cn('pointer-events-auto rounded-full border px-3 py-1.5 text-xs font-medium shadow-md transition-colors duration-300', flash
54
+ ? 'border-blue-500 bg-blue-500 text-white'
55
+ : 'border-border bg-popover text-popover-foreground hover:bg-muted'), "aria-expanded": open, "aria-label": `Analytics session log, ${logs.length} events`, children: ["Analytics ", logs.length > 0 ? `(${logs.length})` : ''] })] }));
56
+ }
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from 'react';
2
+ export type ArticleVersionNavItem = {
3
+ tag: string;
4
+ href: string;
5
+ date?: string;
6
+ description?: string;
7
+ };
8
+ type ArticleVersionsContextValue = {
9
+ versions: ArticleVersionNavItem[];
10
+ currentVersionTag: string | null;
11
+ };
12
+ export declare function ArticleVersionsProvider({ children, versions, currentVersionTag, }: {
13
+ children: ReactNode;
14
+ versions?: ArticleVersionNavItem[];
15
+ currentVersionTag?: string | null;
16
+ }): import("react/jsx-runtime").JSX.Element;
17
+ export declare function useArticleVersions(): ArticleVersionsContextValue;
18
+ export {};
19
+ //# sourceMappingURL=article-versions-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article-versions-provider.d.ts","sourceRoot":"","sources":["../../../src/components/providers/article-versions-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAElE,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,2BAA2B,GAAG;IACjC,QAAQ,EAAE,qBAAqB,EAAE,CAAC;IAClC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC,CAAC;AASF,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,QAAa,EACb,iBAAwB,GACzB,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACnC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC,2CAMA;AAED,wBAAgB,kBAAkB,IAAI,2BAA2B,CAEhE"}
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from 'react';
3
+ const defaultValue = {
4
+ versions: [],
5
+ currentVersionTag: null,
6
+ };
7
+ const ArticleVersionsContext = createContext(defaultValue);
8
+ export function ArticleVersionsProvider({ children, versions = [], currentVersionTag = null, }) {
9
+ return (_jsx(ArticleVersionsContext.Provider, { value: { versions, currentVersionTag }, children: children }));
10
+ }
11
+ export function useArticleVersions() {
12
+ return useContext(ArticleVersionsContext);
13
+ }