docs-i18n 0.6.3 → 0.7.1

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 (169) hide show
  1. package/{src/admin/ui → admin/app}/components/JobDialog.tsx +21 -2
  2. package/{src/admin/ui → admin/app}/components/JobPanel.tsx +1 -1
  3. package/{src/admin/ui → admin/app}/components/Preview.tsx +2 -5
  4. package/{src/admin/ui → admin/app}/lib/api.ts +18 -39
  5. package/admin/app/routeTree.gen.ts +68 -0
  6. package/admin/app/router.tsx +23 -0
  7. package/admin/app/routes/__root.tsx +55 -0
  8. package/admin/app/routes/index.tsx +416 -0
  9. package/{src/admin/ui → admin/app}/styles.css +36 -3
  10. package/admin/package.json +26 -0
  11. package/admin/server/functions/jobs.ts +53 -0
  12. package/admin/server/functions/misc.ts +84 -0
  13. package/{src/admin/server/routes → admin/server/functions}/models.ts +16 -29
  14. package/admin/server/functions/status.ts +61 -0
  15. package/admin/server/index.ts +35 -0
  16. package/admin/server/init.ts +46 -0
  17. package/{src/admin → admin}/server/services/job-manager.ts +39 -10
  18. package/{src/admin → admin}/server/services/status.ts +6 -6
  19. package/admin/tsconfig.json +19 -0
  20. package/{src/admin → admin}/vite.config.ts +8 -2
  21. package/dist/{assemble-7H4QCW35.js → assemble-CP2BRYQJ.js} +6 -4
  22. package/dist/{chunk-A3YQNPKZ.js → chunk-CLYUAWZE.js} +1 -1
  23. package/dist/{chunk-YN4VJHCQ.js → chunk-JHBSHTXC.js} +1 -1
  24. package/dist/chunk-L64GJ4OB.js +32 -0
  25. package/dist/{chunk-SKKZIV3L.js → chunk-PNKVD2UK.js} +1 -29
  26. package/dist/{chunk-XEOYZUHS.js → chunk-QKIR7RKQ.js} +4 -31
  27. package/dist/chunk-TRURQFP4.js +31 -0
  28. package/dist/cli.js +108 -7
  29. package/dist/index.d.ts +41 -1
  30. package/dist/index.js +92 -3
  31. package/dist/{rescan-O5D3CYC2.js → rescan-HXMWFAOC.js} +5 -3
  32. package/dist/{status-F4MYIAAY.js → status-AGZDXOTZ.js} +4 -2
  33. package/dist/{translate-ZIVKNAC4.js → translate-A5X6MX4Y.js} +14 -7
  34. package/dist/upload-XL6KG6S2.js +132 -0
  35. package/package.json +17 -15
  36. package/template/app/components/BlogArticle.tsx +159 -0
  37. package/template/app/components/BlogList.tsx +88 -0
  38. package/template/app/components/Breadcrumbs.tsx +81 -0
  39. package/template/app/components/Card.tsx +31 -0
  40. package/template/app/components/Doc.tsx +191 -0
  41. package/template/app/components/DocBreadcrumb.tsx +60 -0
  42. package/template/app/components/DocContainer.tsx +13 -0
  43. package/template/app/components/DocTitle.tsx +11 -0
  44. package/template/app/components/DocsLayout.tsx +715 -0
  45. package/template/app/components/Dropdown.tsx +116 -0
  46. package/template/app/components/FallbackBanner.tsx +36 -0
  47. package/template/app/components/Footer.tsx +29 -0
  48. package/template/app/components/FrameworkSelect.tsx +150 -0
  49. package/template/app/components/LibraryCard.tsx +178 -0
  50. package/template/app/components/LocaleSwitcher.tsx +43 -0
  51. package/template/app/components/Navbar.tsx +430 -0
  52. package/template/app/components/PostNotFound.tsx +20 -0
  53. package/template/app/components/SearchButton.tsx +32 -0
  54. package/template/app/components/Select.tsx +103 -0
  55. package/template/app/components/Spinner.tsx +18 -0
  56. package/template/app/components/ThemeProvider.tsx +141 -0
  57. package/template/app/components/ThemeToggle.tsx +31 -0
  58. package/template/app/components/Toc.tsx +86 -0
  59. package/template/app/components/VersionSelect.tsx +118 -0
  60. package/template/app/components/icons/BSkyIcon.tsx +27 -0
  61. package/template/app/components/icons/BaseballCapIcon.tsx +25 -0
  62. package/template/app/components/icons/BrandXIcon.tsx +28 -0
  63. package/template/app/components/icons/CheckCircleIcon.tsx +28 -0
  64. package/template/app/components/icons/CogsIcon.tsx +25 -0
  65. package/template/app/components/icons/DiscordIcon.tsx +24 -0
  66. package/template/app/components/icons/GithubIcon.tsx +24 -0
  67. package/template/app/components/icons/GoogleIcon.tsx +24 -0
  68. package/template/app/components/icons/InstagramIcon.tsx +24 -0
  69. package/template/app/components/icons/NpmIcon.tsx +26 -0
  70. package/template/app/components/icons/YinYangIcon.tsx +26 -0
  71. package/template/app/components/icons/YouTubeIcon.tsx +24 -0
  72. package/template/app/components/markdown/CodeBlock.tsx +254 -0
  73. package/template/app/components/markdown/FileTabs.tsx +58 -0
  74. package/template/app/components/markdown/FrameworkContent.tsx +76 -0
  75. package/template/app/components/markdown/Markdown.tsx +216 -0
  76. package/template/app/components/markdown/MarkdownContent.tsx +89 -0
  77. package/template/app/components/markdown/MarkdownFrameworkHandler.tsx +66 -0
  78. package/template/app/components/markdown/MarkdownHeadingContext.tsx +35 -0
  79. package/template/app/components/markdown/MarkdownLink.tsx +46 -0
  80. package/template/app/components/markdown/MarkdownTabsHandler.tsx +109 -0
  81. package/template/app/components/markdown/PackageManagerTabs.tsx +95 -0
  82. package/template/app/components/markdown/Tabs.tsx +139 -0
  83. package/template/app/components/markdown/index.ts +15 -0
  84. package/template/app/components/ui/Button.tsx +141 -0
  85. package/template/app/components/ui/InlineCode.tsx +16 -0
  86. package/template/app/components/ui/MarkdownImg.tsx +21 -0
  87. package/template/app/config/frameworks.ts +93 -0
  88. package/template/app/contexts/SearchContext.tsx +36 -0
  89. package/template/app/db/index.ts +17 -0
  90. package/template/app/db/schema.ts +74 -0
  91. package/template/app/hooks/useClickOutside.ts +106 -0
  92. package/template/app/routeTree.gen.ts +584 -0
  93. package/template/app/router.tsx +29 -0
  94. package/template/app/routes/$lang.$project.$version.docs.$.tsx +128 -0
  95. package/template/app/routes/$lang.$project.$version.docs.framework.$framework.$.tsx +106 -0
  96. package/template/app/routes/$lang.$project.$version.docs.framework.$framework.index.tsx +27 -0
  97. package/template/app/routes/$lang.$project.$version.docs.framework.index.tsx +44 -0
  98. package/template/app/routes/$lang.$project.$version.docs.index.tsx +27 -0
  99. package/template/app/routes/$lang.$project.$version.docs.tsx +70 -0
  100. package/template/app/routes/$lang.$project.$version.tsx +69 -0
  101. package/template/app/routes/$lang.$project.docs.$.tsx +104 -0
  102. package/template/app/routes/$lang.$project.docs.index.tsx +20 -0
  103. package/template/app/routes/$lang.$project.docs.tsx +79 -0
  104. package/template/app/routes/$lang.$project.tsx +89 -0
  105. package/template/app/routes/$lang.blog.$.tsx +82 -0
  106. package/template/app/routes/$lang.blog.index.tsx +56 -0
  107. package/template/app/routes/$lang.blog.tsx +26 -0
  108. package/template/app/routes/$lang.docs.$.tsx +100 -0
  109. package/template/app/routes/$lang.docs.framework.$framework.$.tsx +104 -0
  110. package/template/app/routes/$lang.docs.framework.$framework.index.tsx +32 -0
  111. package/template/app/routes/$lang.docs.framework.index.tsx +47 -0
  112. package/template/app/routes/$lang.docs.index.tsx +20 -0
  113. package/template/app/routes/$lang.docs.tsx +90 -0
  114. package/template/app/routes/$lang.tsx +16 -0
  115. package/template/app/routes/__root.tsx +180 -0
  116. package/template/app/routes/index.tsx +89 -0
  117. package/template/app/site.config.ts +182 -0
  118. package/template/app/styles/app.css +1029 -0
  119. package/template/app/types/index.ts +77 -0
  120. package/template/app/utils/blog.server.ts +193 -0
  121. package/template/app/utils/blog.ts +42 -0
  122. package/template/app/utils/config.ts +120 -0
  123. package/template/app/utils/content-loader.ts +400 -0
  124. package/template/app/utils/dates.ts +29 -0
  125. package/template/app/utils/docs.server.ts +150 -0
  126. package/template/app/utils/markdown/filterFrameworkContent.ts +233 -0
  127. package/template/app/utils/markdown/index.ts +2 -0
  128. package/template/app/utils/markdown/installCommand.ts +143 -0
  129. package/template/app/utils/markdown/plugins/collectHeadings.ts +104 -0
  130. package/template/app/utils/markdown/plugins/extractCodeMeta.ts +57 -0
  131. package/template/app/utils/markdown/plugins/helpers.ts +33 -0
  132. package/template/app/utils/markdown/plugins/index.ts +8 -0
  133. package/template/app/utils/markdown/plugins/parseCommentComponents.ts +103 -0
  134. package/template/app/utils/markdown/plugins/transformCommentComponents.ts +23 -0
  135. package/template/app/utils/markdown/plugins/transformFrameworkComponent.ts +217 -0
  136. package/template/app/utils/markdown/plugins/transformTabsComponent.ts +359 -0
  137. package/template/app/utils/markdown/processor.ts +75 -0
  138. package/template/app/utils/site-config.tsx +11 -0
  139. package/template/app/utils/upload.ts +232 -0
  140. package/template/app/utils/useLocalStorage.ts +65 -0
  141. package/template/app/utils/utils.ts +23 -0
  142. package/template/package.json +53 -0
  143. package/template/public/favicon.svg +1 -0
  144. package/template/public/fonts/Inter-latin-ext.woff2 +0 -0
  145. package/template/public/fonts/Inter-latin.woff2 +0 -0
  146. package/template/public/images/frameworks/angular-logo.svg +1 -0
  147. package/template/public/images/frameworks/js-logo.svg +1 -0
  148. package/template/public/images/frameworks/lit-logo.svg +1 -0
  149. package/template/public/images/frameworks/preact-logo.svg +6 -0
  150. package/template/public/images/frameworks/qwik-logo.svg +1 -0
  151. package/template/public/images/frameworks/react-logo.svg +1 -0
  152. package/template/public/images/frameworks/solid-logo.svg +1 -0
  153. package/template/public/images/frameworks/svelte-logo.svg +1 -0
  154. package/template/public/images/frameworks/vue-logo.svg +4 -0
  155. package/template/tsconfig.json +24 -0
  156. package/template/vite.config.ts +43 -0
  157. package/template/wrangler.jsonc +16 -0
  158. package/README.md +0 -161
  159. package/dist/server-73AVSOL5.js +0 -598
  160. package/src/admin/index.html +0 -13
  161. package/src/admin/server/index.ts +0 -138
  162. package/src/admin/server/routes/jobs.ts +0 -113
  163. package/src/admin/server/routes/status.ts +0 -57
  164. package/src/admin/ui/App.tsx +0 -332
  165. package/src/admin/ui/main.tsx +0 -19
  166. /package/{src/admin/ui → admin/app}/components/FileList.tsx +0 -0
  167. /package/{src/admin/ui → admin/app}/components/LangGrid.tsx +0 -0
  168. /package/{src/admin/ui → admin/app}/components/ProgressBar.tsx +0 -0
  169. /package/{src/admin/ui → admin/app}/lib/flags.ts +0 -0
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Blog post list component.
3
+ *
4
+ * Displays a responsive grid of blog post cards matching tanstack.com styling:
5
+ * - Header image (if present)
6
+ * - Title, author, date
7
+ * - Excerpt with line clamping
8
+ * - "Read More" link
9
+ */
10
+
11
+ import { Link } from '@tanstack/react-router'
12
+ import { Card } from '~/components/Card'
13
+ import { format } from '~/utils/dates'
14
+ import { formatAuthors } from '~/utils/blog'
15
+ import type { BlogPostMeta } from '~/utils/blog'
16
+
17
+ type BlogListProps = {
18
+ posts: BlogPostMeta[]
19
+ lang: string
20
+ }
21
+
22
+ export function BlogList({ posts, lang }: BlogListProps) {
23
+ if (posts.length === 0) {
24
+ return (
25
+ <div className="py-20 text-center">
26
+ <p className="text-lg text-gray-500 dark:text-gray-400">
27
+ No blog posts yet. Check back soon!
28
+ </p>
29
+ </div>
30
+ )
31
+ }
32
+
33
+ return (
34
+ <section className="grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-3 gap-4">
35
+ {posts.map(
36
+ ({ slug, title, published, excerpt, headerImage, authors }) => {
37
+ return (
38
+ <Card
39
+ key={slug}
40
+ as={Link}
41
+ to={`/${lang}/blog/${slug}`}
42
+ className="relative flex flex-col justify-between overflow-hidden transition-all hover:shadow-sm hover:border-blue-500"
43
+ >
44
+ {headerImage ? (
45
+ <div className="aspect-video overflow-hidden bg-gray-100 dark:bg-gray-800">
46
+ <img
47
+ src={headerImage}
48
+ alt=""
49
+ loading="lazy"
50
+ decoding="async"
51
+ className="w-full h-full object-cover"
52
+ />
53
+ </div>
54
+ ) : null}
55
+ <div className="p-4 md:p-8 flex flex-col gap-4 flex-1 justify-between">
56
+ <div>
57
+ <div className="text-lg font-extrabold">{title}</div>
58
+ <div className="text-xs italic font-light mt-1">
59
+ by {formatAuthors(authors)}
60
+ {published ? (
61
+ <time
62
+ dateTime={published}
63
+ title={format(new Date(published), 'MMM d, yyyy')}
64
+ >
65
+ {' '}
66
+ on {format(new Date(published), 'MMM d, yyyy')}
67
+ </time>
68
+ ) : null}
69
+ </div>
70
+ {excerpt ? (
71
+ <p className="text-sm mt-4 text-gray-600 dark:text-gray-400 leading-7 line-clamp-4">
72
+ {excerpt}
73
+ </p>
74
+ ) : null}
75
+ </div>
76
+ <div>
77
+ <div className="text-blue-500 uppercase font-black text-sm">
78
+ Read More
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </Card>
83
+ )
84
+ },
85
+ )}
86
+ </section>
87
+ )
88
+ }
@@ -0,0 +1,81 @@
1
+ import { Link } from '@tanstack/react-router'
2
+ import { ChevronDown } from 'lucide-react'
3
+ import { twMerge } from 'tailwind-merge'
4
+ import type { MarkdownHeading } from '~/types'
5
+ import {
6
+ Dropdown,
7
+ DropdownTrigger,
8
+ DropdownContent,
9
+ DropdownItem,
10
+ } from './Dropdown'
11
+
12
+ type BreadcrumbsProps = {
13
+ /** Section label (e.g., "Getting Started", "Blog") */
14
+ section: string
15
+ /** Optional link for the section */
16
+ sectionTo?: string
17
+ headings?: MarkdownHeading[]
18
+ /** Breakpoint at which the TOC toggle is hidden (default: 'lg') */
19
+ tocHiddenBreakpoint?: 'md' | 'lg'
20
+ }
21
+
22
+ export function Breadcrumbs({
23
+ section,
24
+ sectionTo,
25
+ headings,
26
+ tocHiddenBreakpoint = 'lg',
27
+ }: BreadcrumbsProps) {
28
+ const showTocToggle = headings && headings.length > 1
29
+ const hiddenClass = tocHiddenBreakpoint === 'md' ? 'md:hidden' : 'lg:hidden'
30
+
31
+ return (
32
+ <div className="flex flex-col">
33
+ <div className="flex items-center justify-between gap-4 text-sm text-gray-500 dark:text-gray-400">
34
+ {sectionTo ? (
35
+ <Link
36
+ to={sectionTo}
37
+ className="whitespace-nowrap hover:text-gray-700 dark:hover:text-gray-200 transition-colors"
38
+ >
39
+ {section}
40
+ </Link>
41
+ ) : (
42
+ <span className="whitespace-nowrap">{section}</span>
43
+ )}
44
+ {showTocToggle && (
45
+ <Dropdown>
46
+ <DropdownTrigger>
47
+ <button
48
+ className={twMerge(
49
+ hiddenClass,
50
+ 'whitespace-nowrap inline-flex items-center gap-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors cursor-pointer',
51
+ )}
52
+ >
53
+ <span>On this page</span>
54
+ <ChevronDown className="w-3.5 h-3.5" />
55
+ </button>
56
+ </DropdownTrigger>
57
+ <DropdownContent align="end" sideOffset={8} className={hiddenClass}>
58
+ {headings.map((heading) => (
59
+ <DropdownItem key={`breadcrumb-toc-${heading.id}`} asChild>
60
+ <Link
61
+ to="."
62
+ hash={heading.id}
63
+ style={{
64
+ paddingLeft: `${(heading.level - 2) * 0.5 + 0.5}rem`,
65
+ }}
66
+ resetScroll={false}
67
+ hashScrollIntoView={{
68
+ behavior: 'smooth',
69
+ }}
70
+ >
71
+ <span dangerouslySetInnerHTML={{ __html: heading.text }} />
72
+ </Link>
73
+ </DropdownItem>
74
+ ))}
75
+ </DropdownContent>
76
+ </Dropdown>
77
+ )}
78
+ </div>
79
+ </div>
80
+ )
81
+ }
@@ -0,0 +1,31 @@
1
+ import * as React from 'react'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ type CardOwnProps<TElement extends React.ElementType = 'div'> = {
5
+ as?: TElement
6
+ children: React.ReactNode
7
+ className?: string
8
+ }
9
+
10
+ type CardProps<TElement extends React.ElementType = 'div'> =
11
+ CardOwnProps<TElement> &
12
+ Omit<React.ComponentPropsWithoutRef<TElement>, keyof CardOwnProps<TElement>>
13
+
14
+ type CardComponent = <TElement extends React.ElementType = 'div'>(
15
+ props: CardProps<TElement>,
16
+ ) => React.ReactNode
17
+
18
+ export const Card: CardComponent = ({ as, children, className, ...props }) => {
19
+ const Component = as || 'div'
20
+ return React.createElement(
21
+ Component,
22
+ {
23
+ className: twMerge(
24
+ 'bg-white dark:bg-gray-900 rounded-lg shadow-md border border-gray-200 dark:border-gray-800',
25
+ className,
26
+ ),
27
+ ...props,
28
+ },
29
+ children,
30
+ )
31
+ }
@@ -0,0 +1,191 @@
1
+ import * as React from 'react'
2
+ import { FoldHorizontal, UnfoldHorizontal } from 'lucide-react'
3
+ import { twMerge } from 'tailwind-merge'
4
+ import { useWidthToggle, DocNavigation } from '~/components/DocsLayout'
5
+
6
+ import { Toc } from './Toc'
7
+ import { renderMarkdown } from '~/utils/markdown'
8
+ import { DocBreadcrumb } from './DocBreadcrumb'
9
+ import { FallbackBanner } from './FallbackBanner'
10
+ import { MarkdownContent } from '~/components/markdown'
11
+ import type { DocsConfig, MarkdownHeading } from '~/types'
12
+ import { useLocalCurrentFramework } from './FrameworkSelect'
13
+ import { useParams } from '@tanstack/react-router'
14
+
15
+ type DocProps = {
16
+ title: string
17
+ content: string
18
+ repo: string
19
+ branch: string
20
+ filePath: string
21
+ shouldRenderToc?: boolean
22
+ colorFrom?: string
23
+ colorTo?: string
24
+ textColor?: string
25
+ // Breadcrumb props (optional)
26
+ config?: DocsConfig
27
+ // Footer content rendered after markdown
28
+ footer?: React.ReactNode
29
+ // Optional framework to use (overrides URL and local storage)
30
+ framework?: string
31
+ // Whether the content is a fallback (untranslated)
32
+ isFallback?: boolean
33
+ // Locale display name for fallback banner
34
+ locale?: string
35
+ }
36
+
37
+ export function Doc({
38
+ title,
39
+ content,
40
+ repo,
41
+ branch,
42
+ filePath,
43
+ shouldRenderToc = true,
44
+ colorFrom,
45
+ colorTo,
46
+ textColor,
47
+ config,
48
+ footer,
49
+ framework: frameworkProp,
50
+ isFallback = false,
51
+ locale,
52
+ }: DocProps) {
53
+ // Extract headings synchronously during render to avoid hydration mismatch
54
+ const { headings, markup } = React.useMemo(
55
+ () => renderMarkdown(content),
56
+ [content],
57
+ )
58
+
59
+ // Get current framework from prop, URL params, or local storage
60
+ const { framework: paramsFramework } = useParams({ strict: false })
61
+ const localCurrentFramework = useLocalCurrentFramework()
62
+ const currentFramework = React.useMemo(() => {
63
+ const fw =
64
+ frameworkProp ||
65
+ paramsFramework ||
66
+ localCurrentFramework.currentFramework ||
67
+ 'react'
68
+ return typeof fw === 'string' ? fw.toLowerCase() : fw
69
+ }, [frameworkProp, paramsFramework, localCurrentFramework.currentFramework])
70
+
71
+ const isTocVisible = shouldRenderToc && headings.length > 1
72
+
73
+ const markdownContainerRef = React.useRef<HTMLDivElement>(null)
74
+ const [activeHeadings, setActiveHeadings] = React.useState<Array<string>>([])
75
+
76
+ const headingElementRefs = React.useRef<
77
+ Record<string, IntersectionObserverEntry>
78
+ >({})
79
+
80
+ // Try to get the width toggle context from DocsLayout
81
+ let isFullWidth = false
82
+ let setIsFullWidth: ((isFullWidth: boolean) => void) | undefined
83
+
84
+ try {
85
+ const context = useWidthToggle()
86
+ isFullWidth = context.isFullWidth
87
+ setIsFullWidth = context.setIsFullWidth
88
+ } catch {
89
+ // Context not available, that's okay
90
+ }
91
+
92
+ React.useEffect(() => {
93
+ const callback = (headingsList: Array<IntersectionObserverEntry>) => {
94
+ headingElementRefs.current = headingsList.reduce(
95
+ (map, headingElement) => {
96
+ map[headingElement.target.id] = headingElement
97
+ return map
98
+ },
99
+ headingElementRefs.current,
100
+ )
101
+
102
+ const visibleHeadings: Array<IntersectionObserverEntry> = []
103
+ Object.keys(headingElementRefs.current).forEach((key) => {
104
+ const headingElement = headingElementRefs.current[key]
105
+ if (headingElement.isIntersecting) {
106
+ visibleHeadings.push(headingElement)
107
+ }
108
+ })
109
+
110
+ if (visibleHeadings.length >= 1) {
111
+ setActiveHeadings(visibleHeadings.map((h) => h.target.id))
112
+ }
113
+ }
114
+
115
+ const observer = new IntersectionObserver(callback, {
116
+ rootMargin: '0px',
117
+ threshold: 0.2,
118
+ })
119
+
120
+ const headingElements = Array.from(
121
+ markdownContainerRef.current?.querySelectorAll(
122
+ 'h2[id], h3[id], h4[id], h5[id], h6[id]',
123
+ ) ?? [],
124
+ )
125
+ headingElements.forEach((el) => observer.observe(el))
126
+
127
+ return () => observer.disconnect()
128
+ }, [headings])
129
+
130
+ return (
131
+ <div className="flex-1 min-h-0 flex flex-col pt-4 lg:pt-6 xl:pt-8">
132
+ {isFallback && locale && <FallbackBanner locale={locale} />}
133
+
134
+ <div
135
+ className={twMerge(
136
+ 'w-full flex mx-auto max-w-[768px]',
137
+ (isTocVisible || isFullWidth) && 'max-w-full',
138
+ )}
139
+ >
140
+ <div className="flex flex-col w-full min-w-0">
141
+ {config && (
142
+ <div className="mb-3">
143
+ <DocBreadcrumb
144
+ config={config}
145
+ headings={isTocVisible ? headings : undefined}
146
+ />
147
+ </div>
148
+ )}
149
+ <MarkdownContent
150
+ title={title}
151
+ repo={repo}
152
+ branch={branch}
153
+ filePath={filePath}
154
+ htmlMarkup={markup}
155
+ containerRef={markdownContainerRef}
156
+ currentFramework={currentFramework}
157
+ titleBarActions={
158
+ setIsFullWidth ? (
159
+ <button
160
+ onClick={() => setIsFullWidth(!isFullWidth)}
161
+ className="p-2 mr-4 text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors shrink-0 hidden [@media(min-width:1535px)]:inline-flex"
162
+ title={isFullWidth ? 'Constrain width' : 'Expand width'}
163
+ >
164
+ {isFullWidth ? (
165
+ <FoldHorizontal className="w-4 h-4" />
166
+ ) : (
167
+ <UnfoldHorizontal className="w-4 h-4" />
168
+ )}
169
+ </button>
170
+ ) : null
171
+ }
172
+ />
173
+ {footer ?? <DocNavigation />}
174
+ </div>
175
+
176
+ {isTocVisible && (
177
+ <div className="pl-4 w-32 lg:w-36 xl:w-44 2xl:w-56 3xl:w-64 shrink-0 hidden lg:block transition-all">
178
+ <Toc
179
+ headings={headings}
180
+ activeHeadings={activeHeadings}
181
+ colorFrom={colorFrom}
182
+ colorTo={colorTo}
183
+ textColor={textColor}
184
+ currentFramework={currentFramework}
185
+ />
186
+ </div>
187
+ )}
188
+ </div>
189
+ </div>
190
+ )
191
+ }
@@ -0,0 +1,60 @@
1
+ import { useParams } from '@tanstack/react-router'
2
+ import { Breadcrumbs } from './Breadcrumbs'
3
+ import type { DocsConfig, MarkdownHeading } from '~/types'
4
+
5
+ function findSectionForDoc(
6
+ config: DocsConfig,
7
+ docPath: string,
8
+ ): string | null {
9
+ for (const section of config.sections) {
10
+ // Check core docs
11
+ for (const child of section.children) {
12
+ if (child.to === docPath) {
13
+ return section.label
14
+ }
15
+ }
16
+
17
+ // Check framework-specific docs in all frameworks
18
+ if (section.frameworks) {
19
+ for (const frameworkSection of section.frameworks) {
20
+ for (const child of frameworkSection.children) {
21
+ if (child.to === docPath) {
22
+ return section.label
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+
29
+ return null
30
+ }
31
+
32
+ export function DocBreadcrumb({
33
+ config,
34
+ headings,
35
+ }: {
36
+ config: DocsConfig
37
+ headings?: MarkdownHeading[]
38
+ }) {
39
+ const { _splat, framework } = useParams({ strict: false })
40
+
41
+ // Build the full doc path as it appears in config
42
+ // Config paths are like "overview" or "framework/react/overview"
43
+ const fullDocPath = framework ? `framework/${framework}/${_splat}` : _splat
44
+
45
+ // Find the section for this doc
46
+ const section = fullDocPath ? findSectionForDoc(config, fullDocPath) : null
47
+
48
+ // Only show if we found a section
49
+ if (!section) {
50
+ return null
51
+ }
52
+
53
+ return (
54
+ <Breadcrumbs
55
+ section={section}
56
+ headings={headings}
57
+ tocHiddenBreakpoint="lg"
58
+ />
59
+ )
60
+ }
@@ -0,0 +1,13 @@
1
+ import { HTMLProps } from 'react'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export function DocContainer({
5
+ children,
6
+ ...props
7
+ }: { children: React.ReactNode } & HTMLProps<HTMLDivElement>) {
8
+ return (
9
+ <div {...props} className={twMerge('w-full max-w-full', props.className)}>
10
+ {children}
11
+ </div>
12
+ )
13
+ }
@@ -0,0 +1,11 @@
1
+ export function DocTitle(props: { children: React.ReactNode }) {
2
+ return (
3
+ <>
4
+ <h1
5
+ className={`flex gap-4 items-center flex-wrap text-xl md:text-2xl font-black`}
6
+ >
7
+ {props.children}
8
+ </h1>
9
+ </>
10
+ )
11
+ }