@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.
- package/package.json +6 -2
- package/src/components/Breadcrumb.astro +8 -8
- package/src/components/CodeCopy.astro +14 -13
- package/src/components/Contributors.astro +71 -0
- package/src/components/DocHeader.tsx +24 -24
- package/src/components/DocsSidebar.tsx +11 -15
- package/src/components/KeyboardShortcuts.astro +108 -0
- package/src/components/LanguageSwitcher.astro +3 -3
- package/src/components/MobileNavSheet.tsx +1 -1
- package/src/components/Search.tsx +10 -10
- package/src/components/SearchDialog.tsx +8 -8
- package/src/components/TableOfContents.astro +5 -5
- package/src/components/ThemeToggle.tsx +4 -4
- package/src/components/VersionSwitcher.tsx +79 -0
- package/src/components/api/ApiEndpoint.astro +5 -5
- package/src/components/api/ApiParam.astro +4 -4
- package/src/components/api/ApiParams.astro +3 -3
- package/src/components/api/ApiResponse.astro +2 -2
- package/src/components/index.ts +5 -0
- package/src/components/mdx/Accordion.tsx +6 -6
- package/src/components/mdx/ApiPlayground.tsx +200 -0
- package/src/components/mdx/Badge.tsx +1 -1
- package/src/components/mdx/Callout.astro +20 -20
- package/src/components/mdx/Card.astro +5 -5
- package/src/components/mdx/CodeGroup.astro +23 -20
- package/src/components/mdx/CodeGroup.tsx +6 -6
- package/src/components/mdx/DocAccordion.tsx +5 -5
- package/src/components/mdx/DocCard.tsx +2 -2
- package/src/components/mdx/DocTabs.tsx +3 -3
- package/src/components/mdx/Expandable.tsx +11 -11
- package/src/components/mdx/FileTree.tsx +6 -6
- package/src/components/mdx/Frame.tsx +2 -2
- package/src/components/mdx/ImageZoom.tsx +35 -0
- package/src/components/mdx/Mermaid.tsx +3 -3
- package/src/components/mdx/ParamField.tsx +7 -7
- package/src/components/mdx/ResponseField.tsx +6 -6
- package/src/components/mdx/Step.astro +2 -2
- package/src/components/mdx/Steps.astro +3 -3
- package/src/components/mdx/Steps.tsx +10 -19
- package/src/components/mdx/Tabs.tsx +5 -8
- package/src/components/mdx/Tooltip.tsx +20 -19
- package/src/components/mdx/Video.tsx +71 -0
- package/src/components/ui/accordion.tsx +2 -2
- package/src/components/ui/alert.tsx +1 -1
- package/src/components/ui/button.tsx +3 -3
- package/src/components/ui/card.tsx +2 -2
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/ui/scroll-area.tsx +1 -1
- package/src/components/ui/separator.tsx +1 -1
- package/src/components/ui/sheet.tsx +3 -3
- package/src/components/ui/tabs.tsx +2 -2
- package/src/components/ui/tooltip.tsx +1 -1
- package/src/index.ts +33 -1
- package/src/layouts/BaseLayout.astro +10 -1
- package/src/layouts/BlogLayout.astro +93 -0
- package/src/layouts/DocsLayout.astro +72 -23
- package/src/pages/404.astro +5 -5
- package/src/pages/blog/[...slug].astro +39 -0
- package/src/pages/blog/index.astro +92 -0
- package/src/pages/changelog/index.astro +72 -0
- package/src/pages/docs/[...slug].astro +6 -2
- package/src/pages/index.astro +21 -21
- package/src/styles/global.css +1041 -166
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
3
|
-
import { cn } from "../../lib/utils.js";
|
|
4
3
|
|
|
5
4
|
interface TooltipProps {
|
|
6
5
|
children: React.ReactNode;
|
|
@@ -8,28 +7,30 @@ interface TooltipProps {
|
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
export function Tooltip({ children, tip }: TooltipProps) {
|
|
10
|
+
const [container, setContainer] = React.useState<HTMLElement | null>(null);
|
|
11
|
+
|
|
12
|
+
React.useEffect(() => {
|
|
13
|
+
setContainer(document.body);
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
11
16
|
return (
|
|
12
|
-
<TooltipPrimitive.Provider delayDuration={
|
|
17
|
+
<TooltipPrimitive.Provider delayDuration={150}>
|
|
13
18
|
<TooltipPrimitive.Root>
|
|
14
19
|
<TooltipPrimitive.Trigger asChild>
|
|
15
|
-
<span className="
|
|
16
|
-
{children}
|
|
17
|
-
</span>
|
|
20
|
+
<span className="bd-tooltip-trigger">{children}</span>
|
|
18
21
|
</TooltipPrimitive.Trigger>
|
|
19
|
-
|
|
20
|
-
<TooltipPrimitive.
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
</TooltipPrimitive.Content>
|
|
32
|
-
</TooltipPrimitive.Portal>
|
|
22
|
+
{container && (
|
|
23
|
+
<TooltipPrimitive.Portal container={container}>
|
|
24
|
+
<TooltipPrimitive.Content
|
|
25
|
+
className="bd-tooltip-content"
|
|
26
|
+
sideOffset={6}
|
|
27
|
+
style={{ zIndex: 99999 }}
|
|
28
|
+
>
|
|
29
|
+
{tip}
|
|
30
|
+
<TooltipPrimitive.Arrow className="bd-tooltip-arrow" />
|
|
31
|
+
</TooltipPrimitive.Content>
|
|
32
|
+
</TooltipPrimitive.Portal>
|
|
33
|
+
)}
|
|
33
34
|
</TooltipPrimitive.Root>
|
|
34
35
|
</TooltipPrimitive.Provider>
|
|
35
36
|
);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
|
|
4
|
+
interface VideoProps {
|
|
5
|
+
url: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
caption?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function parseVideoUrl(url: string): { provider: string; embedUrl: string } | null {
|
|
12
|
+
// YouTube: youtube.com/watch?v=ID, youtu.be/ID, youtube.com/embed/ID
|
|
13
|
+
const ytMatch = url.match(
|
|
14
|
+
/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/
|
|
15
|
+
);
|
|
16
|
+
if (ytMatch) {
|
|
17
|
+
return {
|
|
18
|
+
provider: "youtube",
|
|
19
|
+
embedUrl: `https://www.youtube-nocookie.com/embed/${ytMatch[1]}?rel=0`,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Vimeo: vimeo.com/ID, player.vimeo.com/video/ID
|
|
24
|
+
const vimeoMatch = url.match(/(?:vimeo\.com\/|player\.vimeo\.com\/video\/)(\d+)/);
|
|
25
|
+
if (vimeoMatch) {
|
|
26
|
+
return {
|
|
27
|
+
provider: "vimeo",
|
|
28
|
+
embedUrl: `https://player.vimeo.com/video/${vimeoMatch[1]}?dnt=1`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Loom: loom.com/share/ID, loom.com/embed/ID
|
|
33
|
+
const loomMatch = url.match(/loom\.com\/(?:share|embed)\/([a-f0-9]+)/);
|
|
34
|
+
if (loomMatch) {
|
|
35
|
+
return {
|
|
36
|
+
provider: "loom",
|
|
37
|
+
embedUrl: `https://www.loom.com/embed/${loomMatch[1]}`,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function Video({ url, title, caption, className }: VideoProps) {
|
|
45
|
+
const parsed = parseVideoUrl(url);
|
|
46
|
+
|
|
47
|
+
if (!parsed) {
|
|
48
|
+
return (
|
|
49
|
+
<div className={cn("bd-video", className)} style={{ paddingTop: 0, padding: "2rem" }}>
|
|
50
|
+
<p style={{ color: "var(--bd-text-muted)", margin: 0, textAlign: "center" }}>
|
|
51
|
+
Unsupported video URL: {url}
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<figure className={className}>
|
|
59
|
+
<div className="bd-video">
|
|
60
|
+
<iframe
|
|
61
|
+
src={parsed.embedUrl}
|
|
62
|
+
title={title || `${parsed.provider} video`}
|
|
63
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
64
|
+
allowFullScreen
|
|
65
|
+
loading="lazy"
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
{caption && <figcaption className="bd-video-caption">{caption}</figcaption>}
|
|
69
|
+
</figure>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -11,7 +11,7 @@ const AccordionItem = React.forwardRef<
|
|
|
11
11
|
>(({ className, ...props }, ref) => (
|
|
12
12
|
<AccordionPrimitive.Item
|
|
13
13
|
ref={ref}
|
|
14
|
-
className={cn("border-b border-[var(--
|
|
14
|
+
className={cn("border-b border-[var(--bd-border)]", className)}
|
|
15
15
|
{...props}
|
|
16
16
|
/>
|
|
17
17
|
));
|
|
@@ -31,7 +31,7 @@ const AccordionTrigger = React.forwardRef<
|
|
|
31
31
|
{...props}
|
|
32
32
|
>
|
|
33
33
|
{children}
|
|
34
|
-
<ChevronDown className="h-4 w-4 shrink-0 text-[var(--
|
|
34
|
+
<ChevronDown className="h-4 w-4 shrink-0 text-[var(--bd-text-secondary)] transition-transform duration-200" />
|
|
35
35
|
</AccordionPrimitive.Trigger>
|
|
36
36
|
</AccordionPrimitive.Header>
|
|
37
37
|
));
|
|
@@ -7,7 +7,7 @@ const alertVariants = cva(
|
|
|
7
7
|
{
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
|
10
|
-
default: "bg-[var(--
|
|
10
|
+
default: "bg-[var(--bd-bg-subtle)] border-l-[var(--bd-border)] text-[var(--bd-text)]",
|
|
11
11
|
info: "bg-blue-50/70 dark:bg-blue-950/30 border-l-blue-500 text-blue-900 dark:text-blue-100 [&>svg]:text-blue-600",
|
|
12
12
|
warning: "bg-orange-50/70 dark:bg-orange-950/30 border-l-orange-500 text-orange-900 dark:text-orange-100 [&>svg]:text-orange-600",
|
|
13
13
|
success: "bg-green-50/70 dark:bg-green-950/30 border-l-green-500 text-green-900 dark:text-green-100 [&>svg]:text-green-600",
|
|
@@ -13,11 +13,11 @@ const buttonVariants = cva(
|
|
|
13
13
|
destructive:
|
|
14
14
|
"bg-red-600 text-white shadow-sm hover:bg-red-700",
|
|
15
15
|
outline:
|
|
16
|
-
"border border-[var(--
|
|
16
|
+
"border border-[var(--bd-border)] bg-transparent shadow-sm hover:bg-[var(--bd-bg-subtle)] hover:text-[var(--bd-text)]",
|
|
17
17
|
secondary:
|
|
18
|
-
"bg-[var(--
|
|
18
|
+
"bg-[var(--bd-bg-subtle)] text-[var(--bd-text)] shadow-sm hover:bg-[var(--bd-bg-muted)]",
|
|
19
19
|
ghost:
|
|
20
|
-
"hover:bg-[var(--
|
|
20
|
+
"hover:bg-[var(--bd-bg-subtle)] hover:text-[var(--bd-text)]",
|
|
21
21
|
link: "text-primary-600 underline-offset-4 hover:underline",
|
|
22
22
|
},
|
|
23
23
|
size: {
|
|
@@ -8,7 +8,7 @@ const Card = React.forwardRef<
|
|
|
8
8
|
<div
|
|
9
9
|
ref={ref}
|
|
10
10
|
className={cn(
|
|
11
|
-
"rounded-xl border border-[var(--
|
|
11
|
+
"rounded-xl border border-[var(--bd-border)] bg-[var(--bd-bg)] text-[var(--bd-text)] shadow-sm",
|
|
12
12
|
className
|
|
13
13
|
)}
|
|
14
14
|
{...props}
|
|
@@ -46,7 +46,7 @@ const CardDescription = React.forwardRef<
|
|
|
46
46
|
>(({ className, ...props }, ref) => (
|
|
47
47
|
<div
|
|
48
48
|
ref={ref}
|
|
49
|
-
className={cn("text-sm text-[var(--
|
|
49
|
+
className={cn("text-sm text-[var(--bd-text-secondary)]", className)}
|
|
50
50
|
{...props}
|
|
51
51
|
/>
|
|
52
52
|
));
|
|
@@ -35,7 +35,7 @@ const DialogContent = React.forwardRef<
|
|
|
35
35
|
<DialogPrimitive.Content
|
|
36
36
|
ref={ref}
|
|
37
37
|
className={cn(
|
|
38
|
-
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-[var(--
|
|
38
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-[var(--bd-border)] bg-[var(--bd-bg)] p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
39
39
|
className
|
|
40
40
|
)}
|
|
41
41
|
{...props}
|
|
@@ -99,7 +99,7 @@ const DialogDescription = React.forwardRef<
|
|
|
99
99
|
>(({ className, ...props }, ref) => (
|
|
100
100
|
<DialogPrimitive.Description
|
|
101
101
|
ref={ref}
|
|
102
|
-
className={cn("text-sm text-[var(--
|
|
102
|
+
className={cn("text-sm text-[var(--bd-text-secondary)]", className)}
|
|
103
103
|
{...props}
|
|
104
104
|
/>
|
|
105
105
|
));
|
|
@@ -37,7 +37,7 @@ const ScrollBar = React.forwardRef<
|
|
|
37
37
|
)}
|
|
38
38
|
{...props}
|
|
39
39
|
>
|
|
40
|
-
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-[var(--
|
|
40
|
+
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-[var(--bd-border)]" />
|
|
41
41
|
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
42
42
|
));
|
|
43
43
|
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
@@ -15,7 +15,7 @@ const Separator = React.forwardRef<
|
|
|
15
15
|
decorative={decorative}
|
|
16
16
|
orientation={orientation}
|
|
17
17
|
className={cn(
|
|
18
|
-
"shrink-0 bg-[var(--
|
|
18
|
+
"shrink-0 bg-[var(--bd-border)]",
|
|
19
19
|
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
|
20
20
|
className
|
|
21
21
|
)}
|
|
@@ -28,7 +28,7 @@ const SheetOverlay = React.forwardRef<
|
|
|
28
28
|
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
|
29
29
|
|
|
30
30
|
const sheetVariants = cva(
|
|
31
|
-
"fixed z-50 gap-4 bg-[var(--
|
|
31
|
+
"fixed z-50 gap-4 bg-[var(--bd-bg)] p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
32
32
|
{
|
|
33
33
|
variants: {
|
|
34
34
|
side: {
|
|
@@ -105,7 +105,7 @@ const SheetTitle = React.forwardRef<
|
|
|
105
105
|
>(({ className, ...props }, ref) => (
|
|
106
106
|
<SheetPrimitive.Title
|
|
107
107
|
ref={ref}
|
|
108
|
-
className={cn("text-lg font-semibold text-[var(--
|
|
108
|
+
className={cn("text-lg font-semibold text-[var(--bd-text)]", className)}
|
|
109
109
|
{...props}
|
|
110
110
|
/>
|
|
111
111
|
));
|
|
@@ -117,7 +117,7 @@ const SheetDescription = React.forwardRef<
|
|
|
117
117
|
>(({ className, ...props }, ref) => (
|
|
118
118
|
<SheetPrimitive.Description
|
|
119
119
|
ref={ref}
|
|
120
|
-
className={cn("text-sm text-[var(--
|
|
120
|
+
className={cn("text-sm text-[var(--bd-text-secondary)]", className)}
|
|
121
121
|
{...props}
|
|
122
122
|
/>
|
|
123
123
|
));
|
|
@@ -11,7 +11,7 @@ const TabsList = React.forwardRef<
|
|
|
11
11
|
<TabsPrimitive.List
|
|
12
12
|
ref={ref}
|
|
13
13
|
className={cn(
|
|
14
|
-
"inline-flex h-10 items-center justify-center rounded-lg bg-[var(--
|
|
14
|
+
"inline-flex h-10 items-center justify-center rounded-lg bg-[var(--bd-bg-subtle)] p-1 text-[var(--bd-text-secondary)]",
|
|
15
15
|
className
|
|
16
16
|
)}
|
|
17
17
|
{...props}
|
|
@@ -26,7 +26,7 @@ const TabsTrigger = React.forwardRef<
|
|
|
26
26
|
<TabsPrimitive.Trigger
|
|
27
27
|
ref={ref}
|
|
28
28
|
className={cn(
|
|
29
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-[var(--
|
|
29
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-[var(--bd-bg)] data-[state=active]:text-[var(--bd-text)] data-[state=active]:shadow",
|
|
30
30
|
className
|
|
31
31
|
)}
|
|
32
32
|
{...props}
|
|
@@ -17,7 +17,7 @@ const TooltipContent = React.forwardRef<
|
|
|
17
17
|
ref={ref}
|
|
18
18
|
sideOffset={sideOffset}
|
|
19
19
|
className={cn(
|
|
20
|
-
"z-50 overflow-hidden rounded-md bg-[var(--
|
|
20
|
+
"z-50 overflow-hidden rounded-md bg-[var(--bd-text)] px-3 py-1.5 text-xs text-[var(--bd-bg)] animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
21
21
|
className
|
|
22
22
|
)}
|
|
23
23
|
{...props}
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { ThemeExport, ResolvedBarodocConfig } from "@barodoc/core";
|
|
|
3
3
|
import mdx from "@astrojs/mdx";
|
|
4
4
|
import react from "@astrojs/react";
|
|
5
5
|
import tailwindcss from "@tailwindcss/vite";
|
|
6
|
+
import remarkMath from "remark-math";
|
|
7
|
+
import rehypeKatex from "rehype-katex";
|
|
6
8
|
|
|
7
9
|
export interface DocsThemeOptions {
|
|
8
10
|
customCss?: string[];
|
|
@@ -79,11 +81,41 @@ function createThemeIntegration(
|
|
|
79
81
|
entrypoint: "@barodoc/theme-docs/pages/docs/[...slug].astro",
|
|
80
82
|
});
|
|
81
83
|
|
|
84
|
+
// Blog routes
|
|
85
|
+
if (config?.blog?.enabled !== false) {
|
|
86
|
+
injectRoute({
|
|
87
|
+
pattern: "/blog",
|
|
88
|
+
entrypoint: "@barodoc/theme-docs/pages/blog/index.astro",
|
|
89
|
+
});
|
|
90
|
+
injectRoute({
|
|
91
|
+
pattern: "/blog/[...slug]",
|
|
92
|
+
entrypoint: "@barodoc/theme-docs/pages/blog/[...slug].astro",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Changelog route
|
|
97
|
+
injectRoute({
|
|
98
|
+
pattern: "/changelog",
|
|
99
|
+
entrypoint: "@barodoc/theme-docs/pages/changelog/index.astro",
|
|
100
|
+
});
|
|
101
|
+
|
|
82
102
|
// Update Astro config with integrations and Vite plugins
|
|
83
103
|
updateConfig({
|
|
84
|
-
integrations: [
|
|
104
|
+
integrations: [
|
|
105
|
+
mdx({
|
|
106
|
+
remarkPlugins: [remarkMath],
|
|
107
|
+
rehypePlugins: [rehypeKatex],
|
|
108
|
+
}),
|
|
109
|
+
react(),
|
|
110
|
+
],
|
|
85
111
|
vite: {
|
|
86
112
|
plugins: [tailwindcss()],
|
|
113
|
+
optimizeDeps: {
|
|
114
|
+
include: ["mermaid"],
|
|
115
|
+
},
|
|
116
|
+
ssr: {
|
|
117
|
+
noExternal: [],
|
|
118
|
+
},
|
|
87
119
|
resolve: {
|
|
88
120
|
dedupe: ["react", "react-dom"],
|
|
89
121
|
},
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
import ThemeScript from "../components/ThemeScript.astro";
|
|
3
3
|
import { SearchDialog } from "../components/SearchDialog";
|
|
4
|
+
import { ClientRouter } from "astro:transitions";
|
|
4
5
|
import config from "virtual:barodoc/config";
|
|
6
|
+
import { generateThemeCSS } from "@barodoc/core";
|
|
5
7
|
import "@barodoc/theme-docs/styles/global.css";
|
|
6
8
|
|
|
7
9
|
interface Props {
|
|
@@ -16,6 +18,8 @@ const canonicalUrl = config.site ? new URL(Astro.url.pathname, config.site).href
|
|
|
16
18
|
const ogImageUrl = ogImage
|
|
17
19
|
? (config.site ? new URL(ogImage, config.site).href : ogImage)
|
|
18
20
|
: undefined;
|
|
21
|
+
|
|
22
|
+
const themeCSS = config.theme?.colors ? generateThemeCSS(config.theme.colors) : "";
|
|
19
23
|
---
|
|
20
24
|
|
|
21
25
|
<!doctype html>
|
|
@@ -41,10 +45,15 @@ const ogImageUrl = ogImage
|
|
|
41
45
|
<meta name="twitter:description" content={description} />
|
|
42
46
|
{ogImageUrl && <meta name="twitter:image" content={ogImageUrl} />}
|
|
43
47
|
|
|
48
|
+
<!-- KaTeX math rendering -->
|
|
49
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css" crossorigin="anonymous" />
|
|
50
|
+
|
|
44
51
|
<title>{title}</title>
|
|
45
52
|
<ThemeScript />
|
|
53
|
+
<ClientRouter />
|
|
54
|
+
{themeCSS && <style set:html={themeCSS} />}
|
|
46
55
|
</head>
|
|
47
|
-
<body class="min-h-screen bg-[var(--
|
|
56
|
+
<body class="min-h-screen bg-[var(--bd-bg)] text-[var(--bd-text)] antialiased">
|
|
48
57
|
<slot />
|
|
49
58
|
<SearchDialog client:load />
|
|
50
59
|
</body>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
import BaseLayout from "./BaseLayout.astro";
|
|
3
|
+
import Header from "../components/Header.astro";
|
|
4
|
+
import Banner from "../components/Banner.astro";
|
|
5
|
+
import { defaultLocale } from "virtual:barodoc/i18n";
|
|
6
|
+
import { getLocaleFromPath } from "@barodoc/core";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
title: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
date?: Date;
|
|
12
|
+
author?: string;
|
|
13
|
+
image?: string;
|
|
14
|
+
tags?: string[];
|
|
15
|
+
readingTime?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { title, description, date, author, image, tags = [], readingTime } = Astro.props;
|
|
19
|
+
const currentPath = Astro.url.pathname;
|
|
20
|
+
const i18nConfig = { defaultLocale, locales: [defaultLocale] };
|
|
21
|
+
const currentLocale = getLocaleFromPath(currentPath, i18nConfig);
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<BaseLayout title={title} description={description} ogImage={image}>
|
|
25
|
+
<Banner />
|
|
26
|
+
<Header currentLocale={currentLocale} currentPath={currentPath} />
|
|
27
|
+
|
|
28
|
+
<div class="w-full min-w-0 min-h-[calc(100vh-3.5rem)] flex justify-center">
|
|
29
|
+
<article class="w-full max-w-[720px] px-4 py-10 sm:px-6 lg:px-8">
|
|
30
|
+
<!-- Blog post header -->
|
|
31
|
+
<header class="mb-10">
|
|
32
|
+
{tags.length > 0 && (
|
|
33
|
+
<div class="flex flex-wrap gap-2 mb-4">
|
|
34
|
+
{tags.map((tag) => (
|
|
35
|
+
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary-50 text-primary-700 dark:bg-primary-950 dark:text-primary-300">
|
|
36
|
+
{tag}
|
|
37
|
+
</span>
|
|
38
|
+
))}
|
|
39
|
+
</div>
|
|
40
|
+
)}
|
|
41
|
+
<h1 class="text-3xl sm:text-4xl font-bold tracking-tight text-[var(--bd-text-heading)] leading-tight">
|
|
42
|
+
{title}
|
|
43
|
+
</h1>
|
|
44
|
+
{description && (
|
|
45
|
+
<p class="mt-3 text-lg text-[var(--bd-text-secondary)] leading-relaxed">
|
|
46
|
+
{description}
|
|
47
|
+
</p>
|
|
48
|
+
)}
|
|
49
|
+
<div class="mt-4 flex items-center gap-3 text-sm text-[var(--bd-text-muted)]">
|
|
50
|
+
{author && <span>{author}</span>}
|
|
51
|
+
{author && date && <span class="text-[var(--bd-border)]">·</span>}
|
|
52
|
+
{date && (
|
|
53
|
+
<time datetime={date.toISOString()}>
|
|
54
|
+
{date.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" })}
|
|
55
|
+
</time>
|
|
56
|
+
)}
|
|
57
|
+
{readingTime && (
|
|
58
|
+
<>
|
|
59
|
+
<span class="text-[var(--bd-border)]">·</span>
|
|
60
|
+
<span>{readingTime}</span>
|
|
61
|
+
</>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
</header>
|
|
65
|
+
|
|
66
|
+
{image && (
|
|
67
|
+
<img
|
|
68
|
+
src={image}
|
|
69
|
+
alt={title}
|
|
70
|
+
class="w-full rounded-lg mb-10 bd-no-zoom"
|
|
71
|
+
loading="eager"
|
|
72
|
+
/>
|
|
73
|
+
)}
|
|
74
|
+
|
|
75
|
+
<div class="prose prose-gray dark:prose-invert max-w-none">
|
|
76
|
+
<slot />
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- Back to blog -->
|
|
80
|
+
<div class="mt-12 pt-8 border-t border-[var(--bd-border)]">
|
|
81
|
+
<a
|
|
82
|
+
href="/blog"
|
|
83
|
+
class="inline-flex items-center gap-1.5 text-sm text-[var(--bd-text-secondary)] hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
|
|
84
|
+
>
|
|
85
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
86
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
|
87
|
+
</svg>
|
|
88
|
+
Back to Blog
|
|
89
|
+
</a>
|
|
90
|
+
</div>
|
|
91
|
+
</article>
|
|
92
|
+
</div>
|
|
93
|
+
</BaseLayout>
|