@barodoc/theme-docs 3.0.0 → 6.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 (63) hide show
  1. package/package.json +6 -2
  2. package/src/components/Breadcrumb.astro +8 -8
  3. package/src/components/CodeCopy.astro +14 -13
  4. package/src/components/Contributors.astro +71 -0
  5. package/src/components/DocHeader.tsx +24 -24
  6. package/src/components/DocsSidebar.tsx +11 -15
  7. package/src/components/KeyboardShortcuts.astro +108 -0
  8. package/src/components/LanguageSwitcher.astro +3 -3
  9. package/src/components/MobileNavSheet.tsx +1 -1
  10. package/src/components/Search.tsx +10 -10
  11. package/src/components/SearchDialog.tsx +8 -8
  12. package/src/components/TableOfContents.astro +5 -5
  13. package/src/components/ThemeToggle.tsx +4 -4
  14. package/src/components/VersionSwitcher.tsx +79 -0
  15. package/src/components/api/ApiEndpoint.astro +5 -5
  16. package/src/components/api/ApiParam.astro +4 -4
  17. package/src/components/api/ApiParams.astro +3 -3
  18. package/src/components/api/ApiResponse.astro +2 -2
  19. package/src/components/index.ts +5 -0
  20. package/src/components/mdx/Accordion.tsx +6 -6
  21. package/src/components/mdx/ApiPlayground.tsx +200 -0
  22. package/src/components/mdx/Badge.tsx +1 -1
  23. package/src/components/mdx/Callout.astro +20 -20
  24. package/src/components/mdx/Card.astro +5 -5
  25. package/src/components/mdx/CodeGroup.astro +23 -20
  26. package/src/components/mdx/CodeGroup.tsx +6 -6
  27. package/src/components/mdx/DocAccordion.tsx +5 -5
  28. package/src/components/mdx/DocCard.tsx +2 -2
  29. package/src/components/mdx/DocTabs.tsx +3 -3
  30. package/src/components/mdx/Expandable.tsx +11 -11
  31. package/src/components/mdx/FileTree.tsx +6 -6
  32. package/src/components/mdx/Frame.tsx +2 -2
  33. package/src/components/mdx/ImageZoom.tsx +35 -0
  34. package/src/components/mdx/Mermaid.tsx +3 -3
  35. package/src/components/mdx/ParamField.tsx +7 -7
  36. package/src/components/mdx/ResponseField.tsx +6 -6
  37. package/src/components/mdx/Step.astro +2 -2
  38. package/src/components/mdx/Steps.astro +3 -3
  39. package/src/components/mdx/Steps.tsx +10 -19
  40. package/src/components/mdx/Tabs.tsx +5 -8
  41. package/src/components/mdx/Tooltip.tsx +20 -19
  42. package/src/components/mdx/Video.tsx +71 -0
  43. package/src/components/ui/accordion.tsx +2 -2
  44. package/src/components/ui/alert.tsx +1 -1
  45. package/src/components/ui/button.tsx +3 -3
  46. package/src/components/ui/card.tsx +2 -2
  47. package/src/components/ui/dialog.tsx +2 -2
  48. package/src/components/ui/scroll-area.tsx +1 -1
  49. package/src/components/ui/separator.tsx +1 -1
  50. package/src/components/ui/sheet.tsx +3 -3
  51. package/src/components/ui/tabs.tsx +2 -2
  52. package/src/components/ui/tooltip.tsx +1 -1
  53. package/src/index.ts +33 -1
  54. package/src/layouts/BaseLayout.astro +10 -1
  55. package/src/layouts/BlogLayout.astro +93 -0
  56. package/src/layouts/DocsLayout.astro +72 -23
  57. package/src/pages/404.astro +5 -5
  58. package/src/pages/blog/[...slug].astro +39 -0
  59. package/src/pages/blog/index.astro +92 -0
  60. package/src/pages/changelog/index.astro +72 -0
  61. package/src/pages/docs/[...slug].astro +6 -2
  62. package/src/pages/index.astro +21 -21
  63. package/src/styles/global.css +1041 -166
@@ -7,6 +7,8 @@ import MobileNav from "../components/MobileNav.astro";
7
7
  import CodeCopy from "../components/CodeCopy.astro";
8
8
  import Breadcrumb from "../components/Breadcrumb.astro";
9
9
  import Banner from "../components/Banner.astro";
10
+ import KeyboardShortcuts from "../components/KeyboardShortcuts.astro";
11
+ import Contributors from "../components/Contributors.astro";
10
12
  import { defaultLocale } from "virtual:barodoc/i18n";
11
13
  import { getLocaleFromPath } from "@barodoc/core";
12
14
  import config from "virtual:barodoc/config";
@@ -30,9 +32,11 @@ interface Props {
30
32
  editUrl?: string | null;
31
33
  lastUpdated?: Date | null;
32
34
  breadcrumbs?: BreadcrumbItem[];
35
+ readingTime?: string;
36
+ filePath?: string;
33
37
  }
34
38
 
35
- const { title, description, headings = [], prevPage, nextPage, editUrl, lastUpdated, breadcrumbs = [] } = Astro.props;
39
+ const { title, description, headings = [], prevPage, nextPage, editUrl, lastUpdated, breadcrumbs = [], readingTime, filePath } = Astro.props;
36
40
  const currentPath = Astro.url.pathname;
37
41
 
38
42
  // Get locale from path
@@ -48,40 +52,48 @@ const feedbackEndpoint = config.feedback?.endpoint;
48
52
  <Header currentLocale={currentLocale} currentPath={currentPath} />
49
53
 
50
54
  <!-- Centered container for docs layout -->
51
- <div class="w-full min-w-0 min-h-[calc(100vh-3.5rem)] flex justify-center overflow-x-hidden">
52
- <div class="flex w-full max-w-[1120px] min-w-0">
55
+ <div class="w-full min-w-0 min-h-[calc(100vh-3.5rem)] flex justify-center overflow-x-clip">
56
+ <div class="flex w-full max-w-[1280px] min-w-0">
53
57
  <!-- Desktop Sidebar -->
54
- <aside class="hidden lg:block w-[220px] shrink-0">
55
- <div class="sticky top-14 h-[calc(100vh-3.5rem)] overflow-y-auto py-6 pr-4">
58
+ <aside class="hidden lg:block w-[240px] shrink-0 border-r border-[var(--bd-border)]">
59
+ <div class="sticky top-14 h-[calc(100vh-3.5rem)] overflow-y-auto overscroll-contain pt-2 pb-8 px-4 sidebar-scroll">
56
60
  <Sidebar currentPath={currentPath} currentLocale={currentLocale} />
57
61
  </div>
58
62
  </aside>
59
63
 
60
- <!-- Main Content: no min-width on mobile to avoid horizontal scroll -->
61
- <main class="flex-1 min-w-0 lg:min-w-[650px] max-w-[720px]">
62
- <div class="px-4 py-6 sm:px-6 sm:py-8 lg:px-8 lg:py-8">
64
+ <!-- Main Content -->
65
+ <main class="flex-1 min-w-0 max-w-[768px]">
66
+ <div class="px-4 py-8 sm:px-6 sm:py-10 lg:px-10 lg:py-10">
63
67
  {breadcrumbs.length > 0 && <Breadcrumb items={breadcrumbs} />}
68
+ {readingTime && (
69
+ <div class="bd-reading-time mb-4">
70
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
71
+ <circle cx="12" cy="12" r="10" /><polyline points="12 6 12 12 16 14" />
72
+ </svg>
73
+ <span>{readingTime}</span>
74
+ </div>
75
+ )}
64
76
  <article class="prose prose-gray dark:prose-invert max-w-none min-w-0 overflow-x-auto">
65
77
  <slot />
66
78
  </article>
67
79
 
68
80
  <!-- Page Navigation -->
69
81
  {(prevPage || nextPage) && (
70
- <nav class="mt-8 pt-6 border-t border-[var(--color-border)]" aria-label="Page navigation">
82
+ <nav class="mt-12 pt-8 border-t border-[var(--bd-border)]" aria-label="Page navigation">
71
83
  <div class="grid grid-cols-2 gap-4">
72
84
  <div class="col-span-1">
73
85
  {prevPage && (
74
86
  <a
75
87
  href={prevPage.href}
76
- class="group flex flex-col gap-1 p-3 rounded-lg border border-[var(--color-border)] hover:border-primary-300 dark:hover:border-primary-700 hover:bg-[var(--color-bg-secondary)] transition-all"
88
+ class="group flex flex-col gap-1.5 p-4 rounded-lg border border-[var(--bd-border)] hover:border-primary-400/50 dark:hover:border-primary-500/40 hover:shadow-[var(--bd-shadow-sm)] transition-all"
77
89
  >
78
- <span class="text-xs text-[var(--color-text-muted)] flex items-center gap-1">
90
+ <span class="text-xs font-medium text-[var(--bd-text-muted)] flex items-center gap-1">
79
91
  <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
80
92
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
81
93
  </svg>
82
94
  Previous
83
95
  </span>
84
- <span class="text-sm font-medium text-[var(--color-text)] group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors">
96
+ <span class="text-sm font-medium text-[var(--bd-text)] group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors">
85
97
  {prevPage.title}
86
98
  </span>
87
99
  </a>
@@ -91,15 +103,15 @@ const feedbackEndpoint = config.feedback?.endpoint;
91
103
  {nextPage && (
92
104
  <a
93
105
  href={nextPage.href}
94
- class="group flex flex-col gap-1 p-3 rounded-lg border border-[var(--color-border)] hover:border-primary-300 dark:hover:border-primary-700 hover:bg-[var(--color-bg-secondary)] transition-all text-right"
106
+ class="group flex flex-col gap-1.5 p-4 rounded-lg border border-[var(--bd-border)] hover:border-primary-400/50 dark:hover:border-primary-500/40 hover:shadow-[var(--bd-shadow-sm)] transition-all text-right"
95
107
  >
96
- <span class="text-xs text-[var(--color-text-muted)] flex items-center gap-1 justify-end">
108
+ <span class="text-xs font-medium text-[var(--bd-text-muted)] flex items-center gap-1 justify-end">
97
109
  Next
98
110
  <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
99
111
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
100
112
  </svg>
101
113
  </span>
102
- <span class="text-sm font-medium text-[var(--color-text)] group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors">
114
+ <span class="text-sm font-medium text-[var(--bd-text)] group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors">
103
115
  {nextPage.title}
104
116
  </span>
105
117
  </a>
@@ -110,8 +122,9 @@ const feedbackEndpoint = config.feedback?.endpoint;
110
122
  )}
111
123
 
112
124
  <!-- Page footer -->
113
- <footer class="mt-6 pt-4 border-t border-[var(--color-border-light)] flex flex-col gap-3">
114
- <div class="flex flex-wrap items-center justify-between gap-2 text-sm text-[var(--color-text-muted)]">
125
+ <footer class="mt-8 pt-6 border-t border-[var(--bd-border-subtle)] flex flex-col gap-3">
126
+ {filePath && <Contributors filePath={filePath} />}
127
+ <div class="flex flex-wrap items-center justify-between gap-2 text-[13px] text-[var(--bd-text-muted)]">
115
128
  {lastUpdated && (
116
129
  <span>
117
130
  Last updated: <time datetime={lastUpdated.toISOString()}>
@@ -124,7 +137,7 @@ const feedbackEndpoint = config.feedback?.endpoint;
124
137
  href={editUrl}
125
138
  target="_blank"
126
139
  rel="noopener noreferrer"
127
- class="inline-flex items-center gap-1 text-primary-600 dark:text-primary-400 hover:underline"
140
+ class="inline-flex items-center gap-1.5 text-[var(--bd-text-secondary)] hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
128
141
  >
129
142
  <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
130
143
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
@@ -134,12 +147,12 @@ const feedbackEndpoint = config.feedback?.endpoint;
134
147
  )}
135
148
  </div>
136
149
  {hasFeedback && (
137
- <div class="doc-feedback flex items-center gap-3 text-sm text-[var(--color-text-muted)]" data-endpoint={feedbackEndpoint}>
150
+ <div class="doc-feedback flex items-center gap-3 text-sm text-[var(--bd-text-muted)]" data-endpoint={feedbackEndpoint}>
138
151
  <span>Was this page helpful?</span>
139
- <button type="button" class="feedback-btn inline-flex items-center gap-1 px-2 py-1 rounded border border-[var(--color-border)] hover:bg-[var(--color-bg-secondary)] transition-colors" data-value="yes" aria-label="Yes">
152
+ <button type="button" class="feedback-btn inline-flex items-center gap-1 px-2 py-1 rounded border border-[var(--bd-border)] hover:bg-[var(--bd-bg-subtle)] transition-colors" data-value="yes" aria-label="Yes">
140
153
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 9V5a3 3 0 00-3-3l-4 9v11h11.28a2 2 0 002-1.7l1.38-9a2 2 0 00-2-2.3H14z" /></svg>
141
154
  </button>
142
- <button type="button" class="feedback-btn inline-flex items-center gap-1 px-2 py-1 rounded border border-[var(--color-border)] hover:bg-[var(--color-bg-secondary)] transition-colors" data-value="no" aria-label="No">
155
+ <button type="button" class="feedback-btn inline-flex items-center gap-1 px-2 py-1 rounded border border-[var(--bd-border)] hover:bg-[var(--bd-bg-subtle)] transition-colors" data-value="no" aria-label="No">
143
156
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 15v3.586a1 1 0 01-.293.707l-2 2A1 1 0 016 20.586V15m4 0h5.17a2 2 0 001.98-1.742l1.08-7.56A1 1 0 0017.242 4.5H10m0 10.5V4.5m0 0H7.5A2.5 2.5 0 005 7v3" /></svg>
144
157
  </button>
145
158
  <span class="feedback-thanks hidden text-green-600 dark:text-green-400">Thanks for your feedback!</span>
@@ -151,8 +164,8 @@ const feedbackEndpoint = config.feedback?.endpoint;
151
164
 
152
165
  <!-- Table of Contents -->
153
166
  {headings.length > 0 && (
154
- <aside class="hidden xl:block w-[180px] shrink-0">
155
- <div class="sticky top-14 h-[calc(100vh-3.5rem)] overflow-y-auto py-6 pl-6">
167
+ <aside class="hidden xl:block w-[220px] shrink-0">
168
+ <div class="sticky top-14 h-[calc(100vh-3.5rem)] overflow-y-auto py-10 pl-8">
156
169
  <TableOfContents headings={headings} />
157
170
  </div>
158
171
  </aside>
@@ -166,6 +179,42 @@ const feedbackEndpoint = config.feedback?.endpoint;
166
179
  <!-- Code copy functionality -->
167
180
  <CodeCopy />
168
181
 
182
+ <!-- Keyboard shortcuts -->
183
+ <KeyboardShortcuts />
184
+
185
+ <!-- Image zoom for all prose images -->
186
+ <script>
187
+ async function initImageZoom() {
188
+ const images = document.querySelectorAll('.prose img:not(.bd-no-zoom):not(.medium-zoom-image)');
189
+ if (images.length === 0) return;
190
+ const { default: mediumZoom } = await import('medium-zoom');
191
+ mediumZoom(images as any, {
192
+ margin: 24,
193
+ background: 'var(--bd-bg)',
194
+ scrollOffset: 0,
195
+ });
196
+ }
197
+ initImageZoom();
198
+ document.addEventListener('astro:page-load', initImageZoom);
199
+ </script>
200
+
201
+ <!-- Heading anchor links -->
202
+ <script>
203
+ function initHeadingAnchors() {
204
+ document.querySelectorAll('.prose h2[id], .prose h3[id], .prose h4[id]').forEach((heading) => {
205
+ if (heading.querySelector('.heading-anchor')) return;
206
+ const anchor = document.createElement('a');
207
+ anchor.className = 'heading-anchor';
208
+ anchor.href = `#${heading.id}`;
209
+ anchor.textContent = '#';
210
+ anchor.setAttribute('aria-label', `Link to ${heading.textContent}`);
211
+ heading.appendChild(anchor);
212
+ });
213
+ }
214
+ initHeadingAnchors();
215
+ document.addEventListener('astro:page-load', initHeadingAnchors);
216
+ </script>
217
+
169
218
  {hasFeedback && (
170
219
  <script>
171
220
  function initFeedback() {
@@ -8,10 +8,10 @@ const pathname = new URL(Astro.request.url).pathname;
8
8
  <BaseLayout title={`${config.name} · Page not found`} description="The page you are looking for does not exist.">
9
9
  <div class="min-h-screen flex flex-col items-center justify-center px-4">
10
10
  <div class="text-center max-w-md">
11
- <p class="text-8xl font-bold text-[var(--color-text-muted)] select-none" aria-hidden="true">404</p>
12
- <h1 class="text-2xl font-semibold text-[var(--color-text)] mt-4">Page not found</h1>
13
- <p class="text-[var(--color-text-secondary)] mt-2">
14
- The page at <code class="text-sm bg-[var(--color-bg-secondary)] px-2 py-1 rounded break-all">{pathname}</code> does not exist.
11
+ <p class="text-8xl font-bold text-[var(--bd-text-muted)] select-none" aria-hidden="true">404</p>
12
+ <h1 class="text-2xl font-semibold text-[var(--bd-text)] mt-4">Page not found</h1>
13
+ <p class="text-[var(--bd-text-secondary)] mt-2">
14
+ The page at <code class="text-sm bg-[var(--bd-bg-subtle)] px-2 py-1 rounded break-all">{pathname}</code> does not exist.
15
15
  </p>
16
16
  <div class="flex flex-col sm:flex-row items-center justify-center gap-3 mt-8">
17
17
  <a
@@ -22,7 +22,7 @@ const pathname = new URL(Astro.request.url).pathname;
22
22
  </a>
23
23
  <a
24
24
  href="/docs/introduction"
25
- class="px-5 py-2.5 border border-[var(--color-border)] text-[var(--color-text)] rounded-xl hover:bg-[var(--color-bg-secondary)] transition-colors font-medium"
25
+ class="px-5 py-2.5 border border-[var(--bd-border)] text-[var(--bd-text)] rounded-xl hover:bg-[var(--bd-bg-subtle)] transition-colors font-medium"
26
26
  >
27
27
  Documentation
28
28
  </a>
@@ -0,0 +1,39 @@
1
+ ---
2
+ import { getCollection } from "astro:content";
3
+ import BlogLayout from "../../layouts/BlogLayout.astro";
4
+ import readingTime from "reading-time";
5
+
6
+ export async function getStaticPaths() {
7
+ let posts: Awaited<ReturnType<typeof getCollection<"blog">>> = [];
8
+ try {
9
+ posts = await getCollection("blog");
10
+ } catch {
11
+ return [];
12
+ }
13
+
14
+ return posts.map((post) => ({
15
+ params: { slug: post.slug },
16
+ props: { post },
17
+ }));
18
+ }
19
+
20
+ interface Props {
21
+ post: Awaited<ReturnType<typeof getCollection<"blog">>>[number];
22
+ }
23
+
24
+ const { post } = Astro.props;
25
+ const { Content } = await post.render();
26
+ const readTime = readingTime(post.body ?? "");
27
+ ---
28
+
29
+ <BlogLayout
30
+ title={post.data.title}
31
+ description={post.data.description || post.data.excerpt}
32
+ date={post.data.date ? new Date(post.data.date) : undefined}
33
+ author={post.data.author}
34
+ image={post.data.image}
35
+ tags={post.data.tags}
36
+ readingTime={readTime.text}
37
+ >
38
+ <Content />
39
+ </BlogLayout>
@@ -0,0 +1,92 @@
1
+ ---
2
+ import { getCollection } from "astro:content";
3
+ import BaseLayout from "../../layouts/BaseLayout.astro";
4
+ import Header from "../../components/Header.astro";
5
+ import Banner from "../../components/Banner.astro";
6
+ import { defaultLocale } from "virtual:barodoc/i18n";
7
+ import { getLocaleFromPath } from "@barodoc/core";
8
+ import config from "virtual:barodoc/config";
9
+
10
+ const currentPath = Astro.url.pathname;
11
+ const i18nConfig = { defaultLocale, locales: [defaultLocale] };
12
+ const currentLocale = getLocaleFromPath(currentPath, i18nConfig);
13
+
14
+ let posts: Awaited<ReturnType<typeof getCollection<"blog">>> = [];
15
+ try {
16
+ posts = await getCollection("blog");
17
+ } catch {
18
+ // blog collection may not exist
19
+ }
20
+
21
+ const sortedPosts = posts.sort((a, b) => {
22
+ const dateA = a.data.date ? new Date(a.data.date).getTime() : 0;
23
+ const dateB = b.data.date ? new Date(b.data.date).getTime() : 0;
24
+ return dateB - dateA;
25
+ });
26
+ ---
27
+
28
+ <BaseLayout title={`Blog - ${config.name}`} description="Latest blog posts and updates">
29
+ <Banner />
30
+ <Header currentLocale={currentLocale} currentPath={currentPath} />
31
+
32
+ <div class="w-full min-w-0 min-h-[calc(100vh-3.5rem)] flex justify-center">
33
+ <div class="w-full max-w-[960px] px-4 py-10 sm:px-6 lg:px-8">
34
+ <header class="mb-10">
35
+ <h1 class="text-3xl font-bold tracking-tight text-[var(--bd-text-heading)]">Blog</h1>
36
+ <p class="mt-2 text-[var(--bd-text-secondary)]">Latest posts and updates</p>
37
+ </header>
38
+
39
+ {sortedPosts.length === 0 ? (
40
+ <p class="text-[var(--bd-text-muted)]">No posts yet.</p>
41
+ ) : (
42
+ <div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
43
+ {sortedPosts.map((post) => (
44
+ <a
45
+ href={`/blog/${post.slug}`}
46
+ class="group flex flex-col rounded-lg border border-[var(--bd-border)] hover:border-primary-400/50 dark:hover:border-primary-500/40 hover:shadow-[var(--bd-shadow-sm)] transition-all overflow-hidden"
47
+ >
48
+ {post.data.image && (
49
+ <div class="aspect-[16/9] overflow-hidden bg-[var(--bd-bg-subtle)]">
50
+ <img
51
+ src={post.data.image}
52
+ alt={post.data.title}
53
+ class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
54
+ loading="lazy"
55
+ />
56
+ </div>
57
+ )}
58
+ <div class="flex flex-col gap-2 p-5">
59
+ {post.data.tags && post.data.tags.length > 0 && (
60
+ <div class="flex flex-wrap gap-1.5">
61
+ {post.data.tags.slice(0, 3).map((tag: string) => (
62
+ <span class="text-[11px] font-medium px-2 py-0.5 rounded-full bg-primary-50 text-primary-700 dark:bg-primary-950 dark:text-primary-300">
63
+ {tag}
64
+ </span>
65
+ ))}
66
+ </div>
67
+ )}
68
+ <h2 class="text-base font-semibold text-[var(--bd-text)] group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors leading-snug">
69
+ {post.data.title}
70
+ </h2>
71
+ {post.data.excerpt && (
72
+ <p class="text-sm text-[var(--bd-text-muted)] line-clamp-2">
73
+ {post.data.excerpt}
74
+ </p>
75
+ )}
76
+ <div class="mt-auto pt-2 flex items-center gap-2 text-xs text-[var(--bd-text-muted)]">
77
+ {post.data.author && <span>{post.data.author}</span>}
78
+ {post.data.author && post.data.date && <span>&middot;</span>}
79
+ {post.data.date && (
80
+ <time datetime={new Date(post.data.date).toISOString()}>
81
+ {new Date(post.data.date).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" })}
82
+ </time>
83
+ )}
84
+ </div>
85
+ </div>
86
+ </a>
87
+ ))}
88
+ </div>
89
+ )}
90
+ </div>
91
+ </div>
92
+ </BaseLayout>
@@ -0,0 +1,72 @@
1
+ ---
2
+ import { getCollection } from "astro:content";
3
+ import BaseLayout from "../../layouts/BaseLayout.astro";
4
+ import Header from "../../components/Header.astro";
5
+ import Banner from "../../components/Banner.astro";
6
+ import { defaultLocale } from "virtual:barodoc/i18n";
7
+ import { getLocaleFromPath } from "@barodoc/core";
8
+ import config from "virtual:barodoc/config";
9
+
10
+ const currentPath = Astro.url.pathname;
11
+ const i18nConfig = { defaultLocale, locales: [defaultLocale] };
12
+ const currentLocale = getLocaleFromPath(currentPath, i18nConfig);
13
+
14
+ let entries: Awaited<ReturnType<typeof getCollection<"changelog">>> = [];
15
+ try {
16
+ entries = await getCollection("changelog");
17
+ } catch {
18
+ // changelog collection may not exist
19
+ }
20
+
21
+ const sorted = entries.sort((a, b) => {
22
+ return new Date(b.data.date).getTime() - new Date(a.data.date).getTime();
23
+ });
24
+ ---
25
+
26
+ <BaseLayout title={`Changelog - ${config.name}`} description="Release history and changelog">
27
+ <Banner />
28
+ <Header currentLocale={currentLocale} currentPath={currentPath} />
29
+
30
+ <div class="w-full min-w-0 min-h-[calc(100vh-3.5rem)] flex justify-center">
31
+ <div class="w-full max-w-[720px] px-4 py-10 sm:px-6 lg:px-8">
32
+ <header class="mb-10">
33
+ <h1 class="text-3xl font-bold tracking-tight text-[var(--bd-text-heading)]">Changelog</h1>
34
+ <p class="mt-2 text-[var(--bd-text-secondary)]">Release history and notable changes</p>
35
+ </header>
36
+
37
+ {sorted.length === 0 ? (
38
+ <p class="text-[var(--bd-text-muted)]">No changelog entries yet.</p>
39
+ ) : (
40
+ <div class="bd-changelog-timeline">
41
+ {sorted.map(async (entry) => {
42
+ const { Content } = await entry.render();
43
+ return (
44
+ <article class="bd-changelog-entry">
45
+ <div class="bd-changelog-marker">
46
+ <div class="bd-changelog-dot" />
47
+ <div class="bd-changelog-line" />
48
+ </div>
49
+ <div class="bd-changelog-content">
50
+ <div class="bd-changelog-header">
51
+ <span class="bd-changelog-version">{entry.data.version}</span>
52
+ <time class="bd-changelog-date" datetime={new Date(entry.data.date).toISOString()}>
53
+ {new Date(entry.data.date).toLocaleDateString("en-US", {
54
+ year: "numeric", month: "long", day: "numeric",
55
+ })}
56
+ </time>
57
+ </div>
58
+ {entry.data.title && (
59
+ <h2 class="bd-changelog-title">{entry.data.title}</h2>
60
+ )}
61
+ <div class="prose prose-sm prose-gray dark:prose-invert max-w-none bd-changelog-body">
62
+ <Content />
63
+ </div>
64
+ </div>
65
+ </article>
66
+ );
67
+ })}
68
+ </div>
69
+ )}
70
+ </div>
71
+ </div>
72
+ </BaseLayout>
@@ -4,6 +4,7 @@ import DocsLayout from "../../layouts/DocsLayout.astro";
4
4
  import config from "virtual:barodoc/config";
5
5
  import { defaultLocale, locales } from "virtual:barodoc/i18n";
6
6
  import { getLocalizedNavGroup } from "@barodoc/core";
7
+ import readingTime from "reading-time";
7
8
 
8
9
  export async function getStaticPaths() {
9
10
  const docs = await getCollection("docs");
@@ -41,6 +42,7 @@ interface Props {
41
42
 
42
43
  const { doc, locale, cleanSlug } = Astro.props;
43
44
  const { Content, headings } = await doc.render();
45
+ const readTime = readingTime(doc.body ?? "");
44
46
 
45
47
  // Find the category (navigation group) for this page
46
48
  function findCategory(slug: string): string | null {
@@ -122,15 +124,17 @@ breadcrumbs.push({ label: doc.data.title });
122
124
  editUrl={editUrl}
123
125
  lastUpdated={lastUpdated}
124
126
  breadcrumbs={breadcrumbs}
127
+ readingTime={readTime.text}
128
+ filePath={doc.id}
125
129
  >
126
130
  {category && (
127
- <p class="text-xs font-medium uppercase tracking-wider text-primary-600 dark:text-primary-400 mb-2">
131
+ <p class="text-xs font-semibold uppercase tracking-widest text-primary-600 dark:text-primary-400 mb-3">
128
132
  {category}
129
133
  </p>
130
134
  )}
131
135
  <h1>{doc.data.title}</h1>
132
136
  {doc.data.description && (
133
- <p class="lead text-sm text-[var(--color-text-secondary)] mt-1 mb-4">
137
+ <p class="lead text-base text-[var(--bd-text-secondary)] mt-2 mb-6 leading-relaxed">
134
138
  {doc.data.description}
135
139
  </p>
136
140
  )}
@@ -40,10 +40,10 @@ const features = [
40
40
  <BaseLayout title={config.name}>
41
41
  <div class="min-h-screen flex flex-col">
42
42
  <!-- Header -->
43
- <header class="sticky top-0 z-50 border-b border-[var(--color-border)] bg-[var(--color-bg)]/95 backdrop-blur-md">
43
+ <header class="sticky top-0 z-50 border-b border-[var(--bd-border)] bg-[var(--bd-bg)]/95 backdrop-blur-md">
44
44
  <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
45
45
  <div class="flex h-16 items-center justify-between">
46
- <a href="/" class="flex items-center gap-2.5 font-semibold text-[var(--color-text)]">
46
+ <a href="/" class="flex items-center gap-2.5 font-semibold text-[var(--bd-text)]">
47
47
  {config.logo && <img src={config.logo} alt={config.name} class="h-7 w-7" />}
48
48
  <span class="text-lg">{config.name}</span>
49
49
  </a>
@@ -53,7 +53,7 @@ const features = [
53
53
  href={config.topbar.github}
54
54
  target="_blank"
55
55
  rel="noopener noreferrer"
56
- class="p-2.5 rounded-xl text-[var(--color-text-secondary)] hover:text-[var(--color-text)] hover:bg-[var(--color-bg-secondary)] transition-all"
56
+ class="p-2.5 rounded-xl text-[var(--bd-text-secondary)] hover:text-[var(--bd-text)] hover:bg-[var(--bd-bg-subtle)] transition-all"
57
57
  aria-label="GitHub"
58
58
  >
59
59
  <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
@@ -91,12 +91,12 @@ const features = [
91
91
  <span class="text-sm font-medium text-primary-700 dark:text-primary-300">Powered by Astro</span>
92
92
  </div>
93
93
 
94
- <h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold tracking-tight text-[var(--color-text)] mb-6 leading-tight">
94
+ <h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold tracking-tight text-[var(--bd-text)] mb-6 leading-tight">
95
95
  Build beautiful
96
96
  <span class="text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-primary-400"> documentation</span>
97
97
  </h1>
98
98
 
99
- <p class="text-lg sm:text-xl text-[var(--color-text-secondary)] mb-10 max-w-2xl mx-auto leading-relaxed">
99
+ <p class="text-lg sm:text-xl text-[var(--bd-text-secondary)] mb-10 max-w-2xl mx-auto leading-relaxed">
100
100
  Create stunning documentation sites with MDX, Astro, and Tailwind CSS.
101
101
  Deploy anywhere with static site generation.
102
102
  </p>
@@ -110,19 +110,19 @@ const features = [
110
110
  </a>
111
111
  <a
112
112
  href="/docs/quickstart"
113
- class="w-full sm:w-auto px-8 py-3.5 border border-[var(--color-border)] text-[var(--color-text)] rounded-xl hover:bg-[var(--color-bg-secondary)] hover:border-[var(--color-text-muted)] transition-all font-medium"
113
+ class="w-full sm:w-auto px-8 py-3.5 border border-[var(--bd-border)] text-[var(--bd-text)] rounded-xl hover:bg-[var(--bd-bg-subtle)] hover:border-[var(--bd-text-muted)] transition-all font-medium"
114
114
  >
115
115
  Quick Start
116
116
  </a>
117
117
  </div>
118
118
 
119
119
  <!-- Install command -->
120
- <div class="mt-12 inline-flex items-center gap-3 px-5 py-3 bg-[var(--color-bg-secondary)] border border-[var(--color-border)] rounded-xl">
121
- <code class="text-sm text-[var(--color-text-secondary)] font-mono">
120
+ <div class="mt-12 inline-flex items-center gap-3 px-5 py-3 bg-[var(--bd-bg-subtle)] border border-[var(--bd-border)] rounded-xl">
121
+ <code class="text-sm text-[var(--bd-text-secondary)] font-mono">
122
122
  npx create-barodoc my-docs
123
123
  </code>
124
124
  <button
125
- class="p-1.5 rounded-lg hover:bg-[var(--color-bg-tertiary)] text-[var(--color-text-muted)] hover:text-[var(--color-text)] transition-colors"
125
+ class="p-1.5 rounded-lg hover:bg-[var(--bd-bg-muted)] text-[var(--bd-text-muted)] hover:text-[var(--bd-text)] transition-colors"
126
126
  onclick="navigator.clipboard.writeText('npx create-barodoc my-docs')"
127
127
  aria-label="Copy command"
128
128
  >
@@ -136,27 +136,27 @@ const features = [
136
136
  </div>
137
137
 
138
138
  <!-- Features Section -->
139
- <div class="py-20 bg-[var(--color-bg-secondary)] border-y border-[var(--color-border)]">
139
+ <div class="py-20 bg-[var(--bd-bg-subtle)] border-y border-[var(--bd-border)]">
140
140
  <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
141
141
  <div class="text-center mb-16">
142
- <h2 class="text-3xl font-bold text-[var(--color-text)] mb-4">
142
+ <h2 class="text-3xl font-bold text-[var(--bd-text)] mb-4">
143
143
  Everything you need
144
144
  </h2>
145
- <p class="text-lg text-[var(--color-text-secondary)] max-w-2xl mx-auto">
145
+ <p class="text-lg text-[var(--bd-text-secondary)] max-w-2xl mx-auto">
146
146
  Barodoc comes with all the features you need to create beautiful documentation.
147
147
  </p>
148
148
  </div>
149
149
 
150
150
  <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
151
151
  {features.map((feature) => (
152
- <div class="group p-6 bg-[var(--color-bg)] rounded-2xl border border-[var(--color-border)] hover:border-primary-300 dark:hover:border-primary-700 hover:shadow-lg transition-all duration-200">
152
+ <div class="group p-6 bg-[var(--bd-bg)] rounded-2xl border border-[var(--bd-border)] hover:border-primary-300 dark:hover:border-primary-700 hover:shadow-lg transition-all duration-200">
153
153
  <div class="flex items-center justify-center w-12 h-12 rounded-xl bg-primary-50 dark:bg-primary-950/50 mb-4 group-hover:scale-110 transition-transform">
154
154
  <span class="text-2xl">{feature.icon}</span>
155
155
  </div>
156
- <h3 class="text-lg font-semibold text-[var(--color-text)] mb-2">
156
+ <h3 class="text-lg font-semibold text-[var(--bd-text)] mb-2">
157
157
  {feature.title}
158
158
  </h3>
159
- <p class="text-sm text-[var(--color-text-secondary)] leading-relaxed">
159
+ <p class="text-sm text-[var(--bd-text-secondary)] leading-relaxed">
160
160
  {feature.description}
161
161
  </p>
162
162
  </div>
@@ -168,10 +168,10 @@ const features = [
168
168
  <!-- CTA Section -->
169
169
  <div class="py-20">
170
170
  <div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
171
- <h2 class="text-3xl font-bold text-[var(--color-text)] mb-4">
171
+ <h2 class="text-3xl font-bold text-[var(--bd-text)] mb-4">
172
172
  Ready to get started?
173
173
  </h2>
174
- <p class="text-lg text-[var(--color-text-secondary)] mb-8">
174
+ <p class="text-lg text-[var(--bd-text-secondary)] mb-8">
175
175
  Create your documentation site in minutes.
176
176
  </p>
177
177
  <a
@@ -185,10 +185,10 @@ const features = [
185
185
  </main>
186
186
 
187
187
  <!-- Footer -->
188
- <footer class="border-t border-[var(--color-border)] bg-[var(--color-bg-secondary)]">
188
+ <footer class="border-t border-[var(--bd-border)] bg-[var(--bd-bg-subtle)]">
189
189
  <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
190
190
  <div class="flex flex-col sm:flex-row items-center justify-between gap-4">
191
- <div class="flex items-center gap-2 text-[var(--color-text-secondary)]">
191
+ <div class="flex items-center gap-2 text-[var(--bd-text-secondary)]">
192
192
  {config.logo && <img src={config.logo} alt={config.name} class="h-5 w-5 opacity-60" />}
193
193
  <span class="text-sm">Built with Barodoc</span>
194
194
  </div>
@@ -198,14 +198,14 @@ const features = [
198
198
  href={config.topbar.github}
199
199
  target="_blank"
200
200
  rel="noopener noreferrer"
201
- class="text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)] transition-colors"
201
+ class="text-sm text-[var(--bd-text-secondary)] hover:text-[var(--bd-text)] transition-colors"
202
202
  >
203
203
  GitHub
204
204
  </a>
205
205
  )}
206
206
  <a
207
207
  href="/docs/introduction"
208
- class="text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)] transition-colors"
208
+ class="text-sm text-[var(--bd-text-secondary)] hover:text-[var(--bd-text)] transition-colors"
209
209
  >
210
210
  Documentation
211
211
  </a>