@barodoc/theme-docs 0.0.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.
- package/LICENSE +21 -0
- package/package.json +68 -0
- package/src/components/CodeCopy.astro +173 -0
- package/src/components/DocHeader.tsx +166 -0
- package/src/components/DocsSidebar.tsx +84 -0
- package/src/components/Header.astro +32 -0
- package/src/components/LanguageSwitcher.astro +77 -0
- package/src/components/MobileNav.astro +61 -0
- package/src/components/MobileNavSheet.tsx +73 -0
- package/src/components/Search.tsx +210 -0
- package/src/components/SearchDialog.tsx +83 -0
- package/src/components/Sidebar.astro +56 -0
- package/src/components/SidebarWrapper.tsx +24 -0
- package/src/components/TableOfContents.astro +98 -0
- package/src/components/ThemeScript.astro +11 -0
- package/src/components/ThemeToggle.tsx +57 -0
- package/src/components/api/ApiEndpoint.astro +36 -0
- package/src/components/api/ApiParam.astro +26 -0
- package/src/components/api/ApiParams.astro +16 -0
- package/src/components/api/ApiResponse.astro +35 -0
- package/src/components/index.ts +30 -0
- package/src/components/mdx/Accordion.tsx +61 -0
- package/src/components/mdx/Badge.tsx +33 -0
- package/src/components/mdx/Callout.astro +79 -0
- package/src/components/mdx/Card.astro +66 -0
- package/src/components/mdx/CardGroup.astro +18 -0
- package/src/components/mdx/CodeGroup.astro +63 -0
- package/src/components/mdx/CodeGroup.tsx +51 -0
- package/src/components/mdx/Columns.tsx +31 -0
- package/src/components/mdx/DocAccordion.tsx +87 -0
- package/src/components/mdx/DocCallout.tsx +65 -0
- package/src/components/mdx/DocCard.tsx +70 -0
- package/src/components/mdx/DocTabs.tsx +48 -0
- package/src/components/mdx/Expandable.tsx +107 -0
- package/src/components/mdx/FileTree.tsx +72 -0
- package/src/components/mdx/Frame.tsx +23 -0
- package/src/components/mdx/Icon.tsx +59 -0
- package/src/components/mdx/Mermaid.tsx +94 -0
- package/src/components/mdx/ParamField.tsx +76 -0
- package/src/components/mdx/ResponseField.tsx +62 -0
- package/src/components/mdx/Step.astro +14 -0
- package/src/components/mdx/Steps.astro +37 -0
- package/src/components/mdx/Steps.tsx +49 -0
- package/src/components/mdx/Tabs.tsx +67 -0
- package/src/components/mdx/Tooltip.tsx +36 -0
- package/src/components/ui/accordion.tsx +54 -0
- package/src/components/ui/alert.tsx +60 -0
- package/src/components/ui/button.tsx +57 -0
- package/src/components/ui/card.tsx +75 -0
- package/src/components/ui/collapsible.tsx +9 -0
- package/src/components/ui/dialog.tsx +119 -0
- package/src/components/ui/index.ts +11 -0
- package/src/components/ui/scroll-area.tsx +45 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +137 -0
- package/src/components/ui/tabs.tsx +52 -0
- package/src/components/ui/tooltip.tsx +29 -0
- package/src/index.ts +74 -0
- package/src/layouts/BaseLayout.astro +28 -0
- package/src/layouts/DocsLayout.astro +121 -0
- package/src/lib/utils.ts +6 -0
- package/src/pages/docs/[...slug].astro +116 -0
- package/src/pages/index.astro +217 -0
- package/src/styles/global.css +342 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import { X } from "lucide-react";
|
|
5
|
+
import { cn } from "../../lib/utils";
|
|
6
|
+
|
|
7
|
+
const Sheet = SheetPrimitive.Root;
|
|
8
|
+
|
|
9
|
+
const SheetTrigger = SheetPrimitive.Trigger;
|
|
10
|
+
|
|
11
|
+
const SheetClose = SheetPrimitive.Close;
|
|
12
|
+
|
|
13
|
+
const SheetPortal = SheetPrimitive.Portal;
|
|
14
|
+
|
|
15
|
+
const SheetOverlay = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
|
17
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
|
18
|
+
>(({ className, ...props }, ref) => (
|
|
19
|
+
<SheetPrimitive.Overlay
|
|
20
|
+
className={cn(
|
|
21
|
+
"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
22
|
+
className
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
ref={ref}
|
|
26
|
+
/>
|
|
27
|
+
));
|
|
28
|
+
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
|
29
|
+
|
|
30
|
+
const sheetVariants = cva(
|
|
31
|
+
"fixed z-50 gap-4 bg-[var(--color-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
|
+
{
|
|
33
|
+
variants: {
|
|
34
|
+
side: {
|
|
35
|
+
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
|
36
|
+
bottom:
|
|
37
|
+
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
|
38
|
+
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
|
39
|
+
right:
|
|
40
|
+
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
defaultVariants: {
|
|
44
|
+
side: "right",
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
interface SheetContentProps
|
|
50
|
+
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
|
51
|
+
VariantProps<typeof sheetVariants> {}
|
|
52
|
+
|
|
53
|
+
const SheetContent = React.forwardRef<
|
|
54
|
+
React.ElementRef<typeof SheetPrimitive.Content>,
|
|
55
|
+
SheetContentProps
|
|
56
|
+
>(({ side = "right", className, children, ...props }, ref) => (
|
|
57
|
+
<SheetPortal>
|
|
58
|
+
<SheetOverlay />
|
|
59
|
+
<SheetPrimitive.Content
|
|
60
|
+
ref={ref}
|
|
61
|
+
className={cn(sheetVariants({ side }), className)}
|
|
62
|
+
{...props}
|
|
63
|
+
>
|
|
64
|
+
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
|
65
|
+
<X className="h-4 w-4" />
|
|
66
|
+
<span className="sr-only">Close</span>
|
|
67
|
+
</SheetPrimitive.Close>
|
|
68
|
+
{children}
|
|
69
|
+
</SheetPrimitive.Content>
|
|
70
|
+
</SheetPortal>
|
|
71
|
+
));
|
|
72
|
+
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
|
73
|
+
|
|
74
|
+
const SheetHeader = ({
|
|
75
|
+
className,
|
|
76
|
+
...props
|
|
77
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
78
|
+
<div
|
|
79
|
+
className={cn(
|
|
80
|
+
"flex flex-col space-y-2 text-center sm:text-left",
|
|
81
|
+
className
|
|
82
|
+
)}
|
|
83
|
+
{...props}
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
SheetHeader.displayName = "SheetHeader";
|
|
87
|
+
|
|
88
|
+
const SheetFooter = ({
|
|
89
|
+
className,
|
|
90
|
+
...props
|
|
91
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
92
|
+
<div
|
|
93
|
+
className={cn(
|
|
94
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
95
|
+
className
|
|
96
|
+
)}
|
|
97
|
+
{...props}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
SheetFooter.displayName = "SheetFooter";
|
|
101
|
+
|
|
102
|
+
const SheetTitle = React.forwardRef<
|
|
103
|
+
React.ElementRef<typeof SheetPrimitive.Title>,
|
|
104
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
|
105
|
+
>(({ className, ...props }, ref) => (
|
|
106
|
+
<SheetPrimitive.Title
|
|
107
|
+
ref={ref}
|
|
108
|
+
className={cn("text-lg font-semibold text-[var(--color-text)]", className)}
|
|
109
|
+
{...props}
|
|
110
|
+
/>
|
|
111
|
+
));
|
|
112
|
+
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
|
113
|
+
|
|
114
|
+
const SheetDescription = React.forwardRef<
|
|
115
|
+
React.ElementRef<typeof SheetPrimitive.Description>,
|
|
116
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
|
117
|
+
>(({ className, ...props }, ref) => (
|
|
118
|
+
<SheetPrimitive.Description
|
|
119
|
+
ref={ref}
|
|
120
|
+
className={cn("text-sm text-[var(--color-text-secondary)]", className)}
|
|
121
|
+
{...props}
|
|
122
|
+
/>
|
|
123
|
+
));
|
|
124
|
+
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
|
125
|
+
|
|
126
|
+
export {
|
|
127
|
+
Sheet,
|
|
128
|
+
SheetPortal,
|
|
129
|
+
SheetOverlay,
|
|
130
|
+
SheetTrigger,
|
|
131
|
+
SheetClose,
|
|
132
|
+
SheetContent,
|
|
133
|
+
SheetHeader,
|
|
134
|
+
SheetFooter,
|
|
135
|
+
SheetTitle,
|
|
136
|
+
SheetDescription,
|
|
137
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
3
|
+
import { cn } from "../../lib/utils";
|
|
4
|
+
|
|
5
|
+
const Tabs = TabsPrimitive.Root;
|
|
6
|
+
|
|
7
|
+
const TabsList = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof TabsPrimitive.List>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
|
10
|
+
>(({ className, ...props }, ref) => (
|
|
11
|
+
<TabsPrimitive.List
|
|
12
|
+
ref={ref}
|
|
13
|
+
className={cn(
|
|
14
|
+
"inline-flex h-10 items-center justify-center rounded-lg bg-[var(--color-bg-secondary)] p-1 text-[var(--color-text-secondary)]",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
));
|
|
20
|
+
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
21
|
+
|
|
22
|
+
const TabsTrigger = React.forwardRef<
|
|
23
|
+
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
|
24
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
|
25
|
+
>(({ className, ...props }, ref) => (
|
|
26
|
+
<TabsPrimitive.Trigger
|
|
27
|
+
ref={ref}
|
|
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(--color-bg)] data-[state=active]:text-[var(--color-text)] data-[state=active]:shadow",
|
|
30
|
+
className
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
));
|
|
35
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
36
|
+
|
|
37
|
+
const TabsContent = React.forwardRef<
|
|
38
|
+
React.ElementRef<typeof TabsPrimitive.Content>,
|
|
39
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
|
40
|
+
>(({ className, ...props }, ref) => (
|
|
41
|
+
<TabsPrimitive.Content
|
|
42
|
+
ref={ref}
|
|
43
|
+
className={cn(
|
|
44
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
45
|
+
className
|
|
46
|
+
)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
));
|
|
50
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
51
|
+
|
|
52
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
3
|
+
import { cn } from "../../lib/utils";
|
|
4
|
+
|
|
5
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
6
|
+
|
|
7
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
8
|
+
|
|
9
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
10
|
+
|
|
11
|
+
const TooltipContent = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
14
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
15
|
+
<TooltipPrimitive.Portal>
|
|
16
|
+
<TooltipPrimitive.Content
|
|
17
|
+
ref={ref}
|
|
18
|
+
sideOffset={sideOffset}
|
|
19
|
+
className={cn(
|
|
20
|
+
"z-50 overflow-hidden rounded-md bg-[var(--color-text)] px-3 py-1.5 text-xs text-[var(--color-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
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
</TooltipPrimitive.Portal>
|
|
26
|
+
));
|
|
27
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
28
|
+
|
|
29
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { AstroIntegration } from "astro";
|
|
2
|
+
import type { ThemeExport } from "@barodoc/core";
|
|
3
|
+
import mdx from "@astrojs/mdx";
|
|
4
|
+
import react from "@astrojs/react";
|
|
5
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
6
|
+
|
|
7
|
+
export interface DocsThemeOptions {
|
|
8
|
+
customCss?: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function createThemeIntegration(options?: DocsThemeOptions): AstroIntegration {
|
|
12
|
+
return {
|
|
13
|
+
name: "@barodoc/theme-docs",
|
|
14
|
+
hooks: {
|
|
15
|
+
"astro:config:setup": async ({ updateConfig, injectRoute, logger }) => {
|
|
16
|
+
logger.info("Setting up Barodoc docs theme...");
|
|
17
|
+
|
|
18
|
+
// Inject routes
|
|
19
|
+
injectRoute({
|
|
20
|
+
pattern: "/",
|
|
21
|
+
entrypoint: "@barodoc/theme-docs/pages/index.astro",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
injectRoute({
|
|
25
|
+
pattern: "/docs/[...slug]",
|
|
26
|
+
entrypoint: "@barodoc/theme-docs/pages/docs/[...slug].astro",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Inject localized routes for non-default locales
|
|
30
|
+
injectRoute({
|
|
31
|
+
pattern: "/[locale]",
|
|
32
|
+
entrypoint: "@barodoc/theme-docs/pages/index.astro",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
injectRoute({
|
|
36
|
+
pattern: "/[locale]/docs/[...slug]",
|
|
37
|
+
entrypoint: "@barodoc/theme-docs/pages/docs/[...slug].astro",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Update Astro config with integrations and Vite plugins
|
|
41
|
+
updateConfig({
|
|
42
|
+
integrations: [mdx(), react()],
|
|
43
|
+
vite: {
|
|
44
|
+
plugins: [tailwindcss()],
|
|
45
|
+
resolve: {
|
|
46
|
+
dedupe: ["react", "react-dom"],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
markdown: {
|
|
50
|
+
shikiConfig: {
|
|
51
|
+
themes: {
|
|
52
|
+
light: "github-light",
|
|
53
|
+
dark: "github-dark",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
logger.info("Docs theme routes injected");
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default function docsTheme(options?: DocsThemeOptions): ThemeExport {
|
|
66
|
+
return {
|
|
67
|
+
name: "@barodoc/theme-docs",
|
|
68
|
+
integration: () => createThemeIntegration(options),
|
|
69
|
+
styles: options?.customCss || [],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Re-export components for easy access
|
|
74
|
+
export * from "./components/index.ts";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
import ThemeScript from "../components/ThemeScript.astro";
|
|
3
|
+
import { SearchDialog } from "../components/SearchDialog";
|
|
4
|
+
import "@barodoc/theme-docs/styles/global.css";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
title: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { title, description = "Documentation" } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<!doctype html>
|
|
15
|
+
<html lang="en">
|
|
16
|
+
<head>
|
|
17
|
+
<meta charset="UTF-8" />
|
|
18
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
19
|
+
<meta name="description" content={description} />
|
|
20
|
+
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
|
21
|
+
<title>{title}</title>
|
|
22
|
+
<ThemeScript />
|
|
23
|
+
</head>
|
|
24
|
+
<body class="min-h-screen bg-[var(--color-bg)] text-[var(--color-text)] antialiased">
|
|
25
|
+
<slot />
|
|
26
|
+
<SearchDialog client:load />
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
import BaseLayout from "./BaseLayout.astro";
|
|
3
|
+
import Header from "../components/Header.astro";
|
|
4
|
+
import Sidebar from "../components/Sidebar.astro";
|
|
5
|
+
import TableOfContents from "../components/TableOfContents.astro";
|
|
6
|
+
import MobileNav from "../components/MobileNav.astro";
|
|
7
|
+
import CodeCopy from "../components/CodeCopy.astro";
|
|
8
|
+
import { defaultLocale } from "virtual:barodoc/i18n";
|
|
9
|
+
import { getLocaleFromPath } from "@barodoc/core";
|
|
10
|
+
|
|
11
|
+
interface PageLink {
|
|
12
|
+
title: string;
|
|
13
|
+
href: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
title: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
headings?: { depth: number; slug: string; text: string }[];
|
|
20
|
+
prevPage?: PageLink | null;
|
|
21
|
+
nextPage?: PageLink | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { title, description, headings = [], prevPage, nextPage } = Astro.props;
|
|
25
|
+
const currentPath = Astro.url.pathname;
|
|
26
|
+
|
|
27
|
+
// Get locale from path
|
|
28
|
+
const i18nConfig = { defaultLocale, locales: [defaultLocale] };
|
|
29
|
+
const currentLocale = getLocaleFromPath(currentPath, i18nConfig);
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
<BaseLayout title={title} description={description}>
|
|
33
|
+
<Header currentLocale={currentLocale} />
|
|
34
|
+
|
|
35
|
+
<!-- Centered container for docs layout -->
|
|
36
|
+
<div class="w-full min-h-[calc(100vh-3.5rem)] flex justify-center">
|
|
37
|
+
<div class="flex w-full max-w-[1120px]">
|
|
38
|
+
<!-- Desktop Sidebar -->
|
|
39
|
+
<aside class="hidden lg:block w-[220px] shrink-0">
|
|
40
|
+
<div class="sticky top-14 h-[calc(100vh-3.5rem)] overflow-y-auto py-6 pr-4">
|
|
41
|
+
<Sidebar currentPath={currentPath} currentLocale={currentLocale} />
|
|
42
|
+
</div>
|
|
43
|
+
</aside>
|
|
44
|
+
|
|
45
|
+
<!-- Main Content -->
|
|
46
|
+
<main class="flex-1 min-w-0 min-w-[650px] max-w-[720px]">
|
|
47
|
+
<div class="px-8 py-8">
|
|
48
|
+
<article class="prose prose-gray dark:prose-invert max-w-none">
|
|
49
|
+
<slot />
|
|
50
|
+
</article>
|
|
51
|
+
|
|
52
|
+
<!-- Page Navigation -->
|
|
53
|
+
{(prevPage || nextPage) && (
|
|
54
|
+
<nav class="mt-8 pt-6 border-t border-[var(--color-border)]" aria-label="Page navigation">
|
|
55
|
+
<div class="grid grid-cols-2 gap-4">
|
|
56
|
+
<div class="col-span-1">
|
|
57
|
+
{prevPage && (
|
|
58
|
+
<a
|
|
59
|
+
href={prevPage.href}
|
|
60
|
+
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"
|
|
61
|
+
>
|
|
62
|
+
<span class="text-xs text-[var(--color-text-muted)] flex items-center gap-1">
|
|
63
|
+
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
64
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
|
65
|
+
</svg>
|
|
66
|
+
Previous
|
|
67
|
+
</span>
|
|
68
|
+
<span class="text-sm font-medium text-[var(--color-text)] group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors">
|
|
69
|
+
{prevPage.title}
|
|
70
|
+
</span>
|
|
71
|
+
</a>
|
|
72
|
+
)}
|
|
73
|
+
</div>
|
|
74
|
+
<div class="col-span-1">
|
|
75
|
+
{nextPage && (
|
|
76
|
+
<a
|
|
77
|
+
href={nextPage.href}
|
|
78
|
+
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"
|
|
79
|
+
>
|
|
80
|
+
<span class="text-xs text-[var(--color-text-muted)] flex items-center gap-1 justify-end">
|
|
81
|
+
Next
|
|
82
|
+
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
83
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
84
|
+
</svg>
|
|
85
|
+
</span>
|
|
86
|
+
<span class="text-sm font-medium text-[var(--color-text)] group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors">
|
|
87
|
+
{nextPage.title}
|
|
88
|
+
</span>
|
|
89
|
+
</a>
|
|
90
|
+
)}
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</nav>
|
|
94
|
+
)}
|
|
95
|
+
|
|
96
|
+
<!-- Page footer -->
|
|
97
|
+
<footer class="mt-6 pt-4 border-t border-[var(--color-border-light)]">
|
|
98
|
+
<p class="text-sm text-[var(--color-text-muted)]">
|
|
99
|
+
Was this page helpful? <a href="#" class="text-primary-600 dark:text-primary-400 hover:underline">Give us feedback</a>
|
|
100
|
+
</p>
|
|
101
|
+
</footer>
|
|
102
|
+
</div>
|
|
103
|
+
</main>
|
|
104
|
+
|
|
105
|
+
<!-- Table of Contents -->
|
|
106
|
+
{headings.length > 0 && (
|
|
107
|
+
<aside class="hidden xl:block w-[180px] shrink-0">
|
|
108
|
+
<div class="sticky top-14 h-[calc(100vh-3.5rem)] overflow-y-auto py-6 pl-6">
|
|
109
|
+
<TableOfContents headings={headings} />
|
|
110
|
+
</div>
|
|
111
|
+
</aside>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<!-- Mobile Navigation -->
|
|
117
|
+
<MobileNav currentPath={currentPath} currentLocale={currentLocale} />
|
|
118
|
+
|
|
119
|
+
<!-- Code copy functionality -->
|
|
120
|
+
<CodeCopy />
|
|
121
|
+
</BaseLayout>
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getCollection } from "astro:content";
|
|
3
|
+
import DocsLayout from "../../layouts/DocsLayout.astro";
|
|
4
|
+
import config from "virtual:barodoc/config";
|
|
5
|
+
import { defaultLocale, locales } from "virtual:barodoc/i18n";
|
|
6
|
+
import { getLocalizedNavGroup } from "@barodoc/core";
|
|
7
|
+
|
|
8
|
+
export async function getStaticPaths() {
|
|
9
|
+
const docs = await getCollection("docs");
|
|
10
|
+
|
|
11
|
+
return docs.map((doc) => {
|
|
12
|
+
// Handle locale in slug
|
|
13
|
+
const slugParts = doc.slug.split("/");
|
|
14
|
+
const hasLocale = locales.includes(slugParts[0]);
|
|
15
|
+
|
|
16
|
+
let locale = defaultLocale;
|
|
17
|
+
let cleanSlug = doc.slug;
|
|
18
|
+
|
|
19
|
+
if (hasLocale) {
|
|
20
|
+
locale = slugParts[0];
|
|
21
|
+
cleanSlug = slugParts.slice(1).join("/");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Build the path
|
|
25
|
+
const path = locale === defaultLocale
|
|
26
|
+
? cleanSlug
|
|
27
|
+
: `${locale}/${cleanSlug}`;
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
params: { slug: path || undefined },
|
|
31
|
+
props: { doc, locale, cleanSlug },
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface Props {
|
|
37
|
+
doc: Awaited<ReturnType<typeof getCollection<"docs">>>[number];
|
|
38
|
+
locale: string;
|
|
39
|
+
cleanSlug: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { doc, locale, cleanSlug } = Astro.props;
|
|
43
|
+
const { Content, headings } = await doc.render();
|
|
44
|
+
|
|
45
|
+
// Find the category (navigation group) for this page
|
|
46
|
+
function findCategory(slug: string): string | null {
|
|
47
|
+
for (const group of config.navigation) {
|
|
48
|
+
if (group.pages.includes(slug)) {
|
|
49
|
+
return getLocalizedNavGroup(group, locale, defaultLocale);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Get all pages in order from navigation
|
|
56
|
+
function getAllPages(): string[] {
|
|
57
|
+
const pages: string[] = [];
|
|
58
|
+
for (const group of config.navigation) {
|
|
59
|
+
pages.push(...group.pages);
|
|
60
|
+
}
|
|
61
|
+
return pages;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Get page title from slug
|
|
65
|
+
function getPageTitle(slug: string): string {
|
|
66
|
+
const parts = slug.split('/');
|
|
67
|
+
const name = parts[parts.length - 1];
|
|
68
|
+
return name
|
|
69
|
+
.split('-')
|
|
70
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
71
|
+
.join(' ');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Get page href
|
|
75
|
+
function getPageHref(slug: string): string {
|
|
76
|
+
if (locale === defaultLocale) {
|
|
77
|
+
return `/docs/${slug}`;
|
|
78
|
+
}
|
|
79
|
+
return `/${locale}/docs/${slug}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Find prev/next pages
|
|
83
|
+
const allPages = getAllPages();
|
|
84
|
+
const currentIndex = allPages.indexOf(cleanSlug);
|
|
85
|
+
|
|
86
|
+
const prevPage = currentIndex > 0
|
|
87
|
+
? { title: getPageTitle(allPages[currentIndex - 1]), href: getPageHref(allPages[currentIndex - 1]) }
|
|
88
|
+
: null;
|
|
89
|
+
|
|
90
|
+
const nextPage = currentIndex < allPages.length - 1
|
|
91
|
+
? { title: getPageTitle(allPages[currentIndex + 1]), href: getPageHref(allPages[currentIndex + 1]) }
|
|
92
|
+
: null;
|
|
93
|
+
|
|
94
|
+
const category = findCategory(cleanSlug);
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
<DocsLayout
|
|
98
|
+
title={doc.data.title}
|
|
99
|
+
description={doc.data.description}
|
|
100
|
+
headings={headings}
|
|
101
|
+
prevPage={prevPage}
|
|
102
|
+
nextPage={nextPage}
|
|
103
|
+
>
|
|
104
|
+
{category && (
|
|
105
|
+
<p class="text-xs font-medium uppercase tracking-wider text-primary-600 dark:text-primary-400 mb-2">
|
|
106
|
+
{category}
|
|
107
|
+
</p>
|
|
108
|
+
)}
|
|
109
|
+
<h1>{doc.data.title}</h1>
|
|
110
|
+
{doc.data.description && (
|
|
111
|
+
<p class="lead text-sm text-[var(--color-text-secondary)] mt-1 mb-4">
|
|
112
|
+
{doc.data.description}
|
|
113
|
+
</p>
|
|
114
|
+
)}
|
|
115
|
+
<Content />
|
|
116
|
+
</DocsLayout>
|