@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,70 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Card, CardHeader, CardTitle, CardDescription } from "../ui/card";
|
|
3
|
+
import { cn } from "../../lib/utils";
|
|
4
|
+
import { ChevronRight } from "lucide-react";
|
|
5
|
+
|
|
6
|
+
interface DocCardProps {
|
|
7
|
+
title: string;
|
|
8
|
+
icon?: string;
|
|
9
|
+
href?: string;
|
|
10
|
+
children?: React.ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function DocCard({ title, icon, href, children }: DocCardProps) {
|
|
14
|
+
const Comp = href ? "a" : "div";
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Card
|
|
18
|
+
className={cn(
|
|
19
|
+
"group relative overflow-hidden transition-all duration-200",
|
|
20
|
+
href && "cursor-pointer hover:border-primary-300 dark:hover:border-primary-600 hover:shadow-lg hover:shadow-primary-500/5"
|
|
21
|
+
)}
|
|
22
|
+
>
|
|
23
|
+
<Comp href={href} className="block">
|
|
24
|
+
<div className="absolute inset-0 bg-gradient-to-br from-primary-50/50 to-transparent dark:from-primary-950/30 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none" />
|
|
25
|
+
<CardHeader className="relative">
|
|
26
|
+
{icon && (
|
|
27
|
+
<div className="flex items-center justify-center w-10 h-10 rounded-lg bg-[var(--color-bg-secondary)] group-hover:bg-primary-50 dark:group-hover:bg-primary-950/50 mb-3 transition-colors">
|
|
28
|
+
<span className="text-xl">{icon}</span>
|
|
29
|
+
</div>
|
|
30
|
+
)}
|
|
31
|
+
<div className="flex items-center gap-2">
|
|
32
|
+
<CardTitle className="group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors">
|
|
33
|
+
{title}
|
|
34
|
+
</CardTitle>
|
|
35
|
+
{href && (
|
|
36
|
+
<ChevronRight className="h-4 w-4 text-[var(--color-text-muted)] opacity-0 -translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200" />
|
|
37
|
+
)}
|
|
38
|
+
</div>
|
|
39
|
+
{children && (
|
|
40
|
+
<CardDescription className="mt-1.5 leading-relaxed">
|
|
41
|
+
{children}
|
|
42
|
+
</CardDescription>
|
|
43
|
+
)}
|
|
44
|
+
</CardHeader>
|
|
45
|
+
</Comp>
|
|
46
|
+
</Card>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface DocCardGroupProps {
|
|
51
|
+
cols?: 1 | 2 | 3 | 4;
|
|
52
|
+
children: React.ReactNode;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function DocCardGroup({ cols = 2, children }: DocCardGroupProps) {
|
|
56
|
+
const gridCols: Record<number, string> = {
|
|
57
|
+
1: "grid-cols-1",
|
|
58
|
+
2: "grid-cols-1 sm:grid-cols-2",
|
|
59
|
+
3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
|
|
60
|
+
4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4",
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className={cn("not-prose grid gap-4 my-6", gridCols[cols])}>
|
|
65
|
+
{children}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default DocCard;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from "../ui/tabs";
|
|
3
|
+
import { cn } from "../../lib/utils";
|
|
4
|
+
|
|
5
|
+
interface TabItem {
|
|
6
|
+
label: string;
|
|
7
|
+
value: string;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface DocTabsProps {
|
|
12
|
+
items: TabItem[];
|
|
13
|
+
defaultValue?: string;
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function DocTabs({ items, defaultValue, className }: DocTabsProps) {
|
|
18
|
+
const defaultTab = defaultValue || items[0]?.value;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Tabs defaultValue={defaultTab} className={cn("not-prose my-6", className)}>
|
|
22
|
+
<TabsList className="w-full justify-start bg-[var(--color-bg-secondary)] rounded-lg p-1">
|
|
23
|
+
{items.map((item) => (
|
|
24
|
+
<TabsTrigger
|
|
25
|
+
key={item.value}
|
|
26
|
+
value={item.value}
|
|
27
|
+
className="data-[state=active]:bg-[var(--color-bg)] data-[state=active]:shadow-sm"
|
|
28
|
+
>
|
|
29
|
+
{item.label}
|
|
30
|
+
</TabsTrigger>
|
|
31
|
+
))}
|
|
32
|
+
</TabsList>
|
|
33
|
+
{items.map((item) => (
|
|
34
|
+
<TabsContent
|
|
35
|
+
key={item.value}
|
|
36
|
+
value={item.value}
|
|
37
|
+
className="mt-4 rounded-lg border border-[var(--color-border)] bg-[var(--color-bg-secondary)] p-4"
|
|
38
|
+
>
|
|
39
|
+
<div className="text-sm">
|
|
40
|
+
{item.children}
|
|
41
|
+
</div>
|
|
42
|
+
</TabsContent>
|
|
43
|
+
))}
|
|
44
|
+
</Tabs>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default DocTabs;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { ChevronDown, Plus, Minus } from "lucide-react";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
|
|
5
|
+
interface ExpandableProps {
|
|
6
|
+
title: string;
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
defaultOpen?: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function Expandable({
|
|
13
|
+
title,
|
|
14
|
+
children,
|
|
15
|
+
defaultOpen = false,
|
|
16
|
+
className,
|
|
17
|
+
}: ExpandableProps) {
|
|
18
|
+
const [isOpen, setIsOpen] = React.useState(defaultOpen);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
className={cn(
|
|
23
|
+
"not-prose border border-[var(--color-border)] rounded-lg overflow-hidden my-4",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
>
|
|
27
|
+
<button
|
|
28
|
+
type="button"
|
|
29
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
30
|
+
className="flex w-full items-center justify-between px-4 py-3 bg-[var(--color-bg-secondary)] hover:bg-[var(--color-bg-tertiary)] transition-colors text-left"
|
|
31
|
+
>
|
|
32
|
+
<span className="text-sm font-medium text-[var(--color-text)]">
|
|
33
|
+
{title}
|
|
34
|
+
</span>
|
|
35
|
+
<ChevronDown
|
|
36
|
+
className={cn(
|
|
37
|
+
"h-4 w-4 text-[var(--color-text-muted)] transition-transform",
|
|
38
|
+
isOpen && "rotate-180"
|
|
39
|
+
)}
|
|
40
|
+
/>
|
|
41
|
+
</button>
|
|
42
|
+
{isOpen && (
|
|
43
|
+
<div className="px-4 py-3 bg-[var(--color-bg)] border-t border-[var(--color-border)] text-sm text-[var(--color-text-secondary)]">
|
|
44
|
+
{children}
|
|
45
|
+
</div>
|
|
46
|
+
)}
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface ExpandableListProps {
|
|
52
|
+
children: React.ReactNode;
|
|
53
|
+
className?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function ExpandableList({ children, className }: ExpandableListProps) {
|
|
57
|
+
return (
|
|
58
|
+
<div className={cn("space-y-2 my-4", className)}>
|
|
59
|
+
{children}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface ExpandableItemProps {
|
|
65
|
+
title: string;
|
|
66
|
+
type?: string;
|
|
67
|
+
children: React.ReactNode;
|
|
68
|
+
defaultOpen?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function ExpandableItem({
|
|
72
|
+
title,
|
|
73
|
+
type,
|
|
74
|
+
children,
|
|
75
|
+
defaultOpen = false,
|
|
76
|
+
}: ExpandableItemProps) {
|
|
77
|
+
const [isOpen, setIsOpen] = React.useState(defaultOpen);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<div className="not-prose border-l-2 border-[var(--color-border)] pl-4">
|
|
81
|
+
<button
|
|
82
|
+
type="button"
|
|
83
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
84
|
+
className="flex items-center gap-2 text-left group"
|
|
85
|
+
>
|
|
86
|
+
{isOpen ? (
|
|
87
|
+
<Minus className="h-3 w-3 text-[var(--color-text-muted)]" />
|
|
88
|
+
) : (
|
|
89
|
+
<Plus className="h-3 w-3 text-[var(--color-text-muted)]" />
|
|
90
|
+
)}
|
|
91
|
+
<code className="font-mono text-sm text-[var(--color-text)] group-hover:text-primary-600 dark:group-hover:text-primary-400">
|
|
92
|
+
{title}
|
|
93
|
+
</code>
|
|
94
|
+
{type && (
|
|
95
|
+
<span className="font-mono text-xs text-[var(--color-text-muted)]">
|
|
96
|
+
{type}
|
|
97
|
+
</span>
|
|
98
|
+
)}
|
|
99
|
+
</button>
|
|
100
|
+
{isOpen && (
|
|
101
|
+
<div className="mt-2 ml-5 text-sm text-[var(--color-text-secondary)]">
|
|
102
|
+
{children}
|
|
103
|
+
</div>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { ChevronRight, File, Folder, FolderOpen } from "lucide-react";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
|
|
5
|
+
interface FileTreeProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function FileTree({ children, className }: FileTreeProps) {
|
|
11
|
+
return (
|
|
12
|
+
<div className={cn("not-prose my-4 rounded-lg border border-[var(--color-border)] bg-[var(--color-bg-secondary)] p-4 font-mono text-sm", className)}>
|
|
13
|
+
<ul className="space-y-1">{children}</ul>
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface FileProps {
|
|
19
|
+
name: string;
|
|
20
|
+
icon?: React.ReactNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function TreeFile({ name, icon }: FileProps) {
|
|
24
|
+
return (
|
|
25
|
+
<li className="flex items-center gap-2 py-0.5 text-[var(--color-text-secondary)]">
|
|
26
|
+
{icon || <File className="h-4 w-4 text-[var(--color-text-muted)]" />}
|
|
27
|
+
<span>{name}</span>
|
|
28
|
+
</li>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface FolderProps {
|
|
33
|
+
name: string;
|
|
34
|
+
children?: React.ReactNode;
|
|
35
|
+
defaultOpen?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function TreeFolder({ name, children, defaultOpen = false }: FolderProps) {
|
|
39
|
+
const [isOpen, setIsOpen] = React.useState(defaultOpen);
|
|
40
|
+
const hasChildren = React.Children.count(children) > 0;
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<li>
|
|
44
|
+
<button
|
|
45
|
+
type="button"
|
|
46
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
47
|
+
className="flex w-full items-center gap-1 py-0.5 text-[var(--color-text)] hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
|
|
48
|
+
>
|
|
49
|
+
{hasChildren && (
|
|
50
|
+
<ChevronRight
|
|
51
|
+
className={cn(
|
|
52
|
+
"h-3 w-3 text-[var(--color-text-muted)] transition-transform",
|
|
53
|
+
isOpen && "rotate-90"
|
|
54
|
+
)}
|
|
55
|
+
/>
|
|
56
|
+
)}
|
|
57
|
+
{!hasChildren && <span className="w-3" />}
|
|
58
|
+
{isOpen ? (
|
|
59
|
+
<FolderOpen className="h-4 w-4 text-primary-500" />
|
|
60
|
+
) : (
|
|
61
|
+
<Folder className="h-4 w-4 text-primary-500" />
|
|
62
|
+
)}
|
|
63
|
+
<span className="font-medium">{name}</span>
|
|
64
|
+
</button>
|
|
65
|
+
{isOpen && hasChildren && (
|
|
66
|
+
<ul className="ml-4 border-l border-[var(--color-border)] pl-3 mt-1 space-y-1">
|
|
67
|
+
{children}
|
|
68
|
+
</ul>
|
|
69
|
+
)}
|
|
70
|
+
</li>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
|
|
4
|
+
interface FrameProps {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
caption?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function Frame({ children, caption, className }: FrameProps) {
|
|
11
|
+
return (
|
|
12
|
+
<figure className={cn("not-prose my-6", className)}>
|
|
13
|
+
<div className="overflow-hidden rounded-lg border border-[var(--color-border)] bg-[var(--color-bg-secondary)]">
|
|
14
|
+
{children}
|
|
15
|
+
</div>
|
|
16
|
+
{caption && (
|
|
17
|
+
<figcaption className="mt-2 text-center text-sm text-[var(--color-text-muted)]">
|
|
18
|
+
{caption}
|
|
19
|
+
</figcaption>
|
|
20
|
+
)}
|
|
21
|
+
</figure>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { icons, type LucideIcon as LucideIconType } from "lucide-react";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
|
|
5
|
+
interface IconProps {
|
|
6
|
+
name: string;
|
|
7
|
+
size?: number | "sm" | "md" | "lg" | "xl";
|
|
8
|
+
className?: string;
|
|
9
|
+
color?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const sizeMap = {
|
|
13
|
+
sm: 16,
|
|
14
|
+
md: 20,
|
|
15
|
+
lg: 24,
|
|
16
|
+
xl: 32,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function Icon({ name, size = "md", className, color }: IconProps) {
|
|
20
|
+
// Convert name to PascalCase (e.g., "chevron-right" -> "ChevronRight")
|
|
21
|
+
const iconName = name
|
|
22
|
+
.split("-")
|
|
23
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
24
|
+
.join("");
|
|
25
|
+
|
|
26
|
+
const LucideIcon = icons[iconName as keyof typeof icons] as LucideIconType | undefined;
|
|
27
|
+
|
|
28
|
+
if (!LucideIcon) {
|
|
29
|
+
console.warn(`Icon "${name}" not found in lucide-react`);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const numericSize = typeof size === "number" ? size : sizeMap[size];
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<LucideIcon
|
|
37
|
+
size={numericSize}
|
|
38
|
+
className={cn("inline-block", className)}
|
|
39
|
+
style={color ? { color } : undefined}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Pre-built icon components for common use cases
|
|
45
|
+
export function CheckIcon({ className }: { className?: string }) {
|
|
46
|
+
return <Icon name="check" className={cn("text-green-500", className)} />;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function XIcon({ className }: { className?: string }) {
|
|
50
|
+
return <Icon name="x" className={cn("text-red-500", className)} />;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function InfoIcon({ className }: { className?: string }) {
|
|
54
|
+
return <Icon name="info" className={cn("text-blue-500", className)} />;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function WarningIcon({ className }: { className?: string }) {
|
|
58
|
+
return <Icon name="alert-triangle" className={cn("text-yellow-500", className)} />;
|
|
59
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
|
|
4
|
+
interface MermaidProps {
|
|
5
|
+
chart: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function Mermaid({ chart, className }: MermaidProps) {
|
|
10
|
+
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
11
|
+
const [svg, setSvg] = React.useState<string>("");
|
|
12
|
+
const [error, setError] = React.useState<string | null>(null);
|
|
13
|
+
|
|
14
|
+
React.useEffect(() => {
|
|
15
|
+
let isMounted = true;
|
|
16
|
+
|
|
17
|
+
async function renderChart() {
|
|
18
|
+
try {
|
|
19
|
+
// Dynamically import mermaid
|
|
20
|
+
const mermaid = (await import("mermaid")).default;
|
|
21
|
+
|
|
22
|
+
mermaid.initialize({
|
|
23
|
+
startOnLoad: false,
|
|
24
|
+
theme: document.documentElement.classList.contains("dark") ? "dark" : "default",
|
|
25
|
+
securityLevel: "loose",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const id = `mermaid-${Math.random().toString(36).substr(2, 9)}`;
|
|
29
|
+
const { svg } = await mermaid.render(id, chart);
|
|
30
|
+
|
|
31
|
+
if (isMounted) {
|
|
32
|
+
setSvg(svg);
|
|
33
|
+
setError(null);
|
|
34
|
+
}
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (isMounted) {
|
|
37
|
+
setError(err instanceof Error ? err.message : "Failed to render diagram");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
renderChart();
|
|
43
|
+
|
|
44
|
+
return () => {
|
|
45
|
+
isMounted = false;
|
|
46
|
+
};
|
|
47
|
+
}, [chart]);
|
|
48
|
+
|
|
49
|
+
// Re-render on theme change
|
|
50
|
+
React.useEffect(() => {
|
|
51
|
+
const observer = new MutationObserver((mutations) => {
|
|
52
|
+
mutations.forEach((mutation) => {
|
|
53
|
+
if (mutation.attributeName === "class") {
|
|
54
|
+
// Theme changed, re-render
|
|
55
|
+
setSvg("");
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
observer.observe(document.documentElement, {
|
|
61
|
+
attributes: true,
|
|
62
|
+
attributeFilter: ["class"],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return () => observer.disconnect();
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
if (error) {
|
|
69
|
+
return (
|
|
70
|
+
<div className="not-prose my-4 rounded-lg border border-red-200 bg-red-50 dark:border-red-900 dark:bg-red-950/30 p-4">
|
|
71
|
+
<p className="text-sm text-red-600 dark:text-red-400">
|
|
72
|
+
Mermaid diagram error: {error}
|
|
73
|
+
</p>
|
|
74
|
+
<pre className="mt-2 text-xs text-red-500 overflow-auto">{chart}</pre>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!svg) {
|
|
80
|
+
return (
|
|
81
|
+
<div className={cn("not-prose my-4 flex items-center justify-center p-8 bg-[var(--color-bg-secondary)] rounded-lg", className)}>
|
|
82
|
+
<div className="text-sm text-[var(--color-text-muted)]">Loading diagram...</div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
ref={containerRef}
|
|
90
|
+
className={cn("not-prose my-4 flex justify-center overflow-x-auto rounded-lg bg-[var(--color-bg)] p-4", className)}
|
|
91
|
+
dangerouslySetInnerHTML={{ __html: svg }}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
|
|
4
|
+
interface ParamFieldProps {
|
|
5
|
+
name: string;
|
|
6
|
+
type?: string;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
default?: string;
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function ParamField({
|
|
14
|
+
name,
|
|
15
|
+
type,
|
|
16
|
+
required,
|
|
17
|
+
default: defaultValue,
|
|
18
|
+
children,
|
|
19
|
+
className,
|
|
20
|
+
}: ParamFieldProps) {
|
|
21
|
+
return (
|
|
22
|
+
<div
|
|
23
|
+
className={cn(
|
|
24
|
+
"not-prose border-b border-[var(--color-border)] py-4 last:border-b-0",
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
>
|
|
28
|
+
<div className="flex flex-wrap items-baseline gap-2">
|
|
29
|
+
<code className="font-mono text-sm font-semibold text-[var(--color-text)]">
|
|
30
|
+
{name}
|
|
31
|
+
</code>
|
|
32
|
+
{type && (
|
|
33
|
+
<span className="font-mono text-xs text-[var(--color-text-muted)]">
|
|
34
|
+
{type}
|
|
35
|
+
</span>
|
|
36
|
+
)}
|
|
37
|
+
{required && (
|
|
38
|
+
<span className="rounded bg-red-100 px-1.5 py-0.5 text-xs font-medium text-red-700 dark:bg-red-900/30 dark:text-red-400">
|
|
39
|
+
required
|
|
40
|
+
</span>
|
|
41
|
+
)}
|
|
42
|
+
{defaultValue && (
|
|
43
|
+
<span className="text-xs text-[var(--color-text-muted)]">
|
|
44
|
+
default: <code className="font-mono">{defaultValue}</code>
|
|
45
|
+
</span>
|
|
46
|
+
)}
|
|
47
|
+
</div>
|
|
48
|
+
{children && (
|
|
49
|
+
<div className="mt-2 text-sm text-[var(--color-text-secondary)] prose prose-sm dark:prose-invert max-w-none">
|
|
50
|
+
{children}
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface ParamFieldGroupProps {
|
|
58
|
+
children: React.ReactNode;
|
|
59
|
+
title?: string;
|
|
60
|
+
className?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function ParamFieldGroup({ children, title, className }: ParamFieldGroupProps) {
|
|
64
|
+
return (
|
|
65
|
+
<div className={cn("my-6", className)}>
|
|
66
|
+
{title && (
|
|
67
|
+
<h4 className="text-sm font-semibold text-[var(--color-text)] mb-4 uppercase tracking-wide">
|
|
68
|
+
{title}
|
|
69
|
+
</h4>
|
|
70
|
+
)}
|
|
71
|
+
<div className="rounded-lg border border-[var(--color-border)] bg-[var(--color-bg)] px-4">
|
|
72
|
+
{children}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
|
|
4
|
+
interface ResponseFieldProps {
|
|
5
|
+
name: string;
|
|
6
|
+
type?: string;
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function ResponseField({
|
|
12
|
+
name,
|
|
13
|
+
type,
|
|
14
|
+
children,
|
|
15
|
+
className,
|
|
16
|
+
}: ResponseFieldProps) {
|
|
17
|
+
return (
|
|
18
|
+
<div
|
|
19
|
+
className={cn(
|
|
20
|
+
"not-prose border-b border-[var(--color-border)] py-4 first:pt-0 last:border-b-0",
|
|
21
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
>
|
|
24
|
+
<div className="flex flex-wrap items-baseline gap-2">
|
|
25
|
+
<code className="font-mono text-sm font-semibold text-[var(--color-text)]">
|
|
26
|
+
{name}
|
|
27
|
+
</code>
|
|
28
|
+
{type && (
|
|
29
|
+
<span className="font-mono text-xs text-[var(--color-text-muted)]">
|
|
30
|
+
{type}
|
|
31
|
+
</span>
|
|
32
|
+
)}
|
|
33
|
+
</div>
|
|
34
|
+
{children && (
|
|
35
|
+
<div className="mt-2 text-sm text-[var(--color-text-secondary)] prose prose-sm dark:prose-invert max-w-none">
|
|
36
|
+
{children}
|
|
37
|
+
</div>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface ResponseFieldGroupProps {
|
|
44
|
+
children: React.ReactNode;
|
|
45
|
+
title?: string;
|
|
46
|
+
className?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function ResponseFieldGroup({ children, title, className }: ResponseFieldGroupProps) {
|
|
50
|
+
return (
|
|
51
|
+
<div className={cn("my-6", className)}>
|
|
52
|
+
{title && (
|
|
53
|
+
<h4 className="text-sm font-semibold text-[var(--color-text)] mb-4 uppercase tracking-wide">
|
|
54
|
+
{title}
|
|
55
|
+
</h4>
|
|
56
|
+
)}
|
|
57
|
+
<div className="rounded-lg border border-[var(--color-border)] bg-[var(--color-bg)] px-4">
|
|
58
|
+
{children}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
title: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { title } = Astro.props;
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<div class="relative">
|
|
10
|
+
<h3 class="font-semibold text-[var(--color-text)] mb-2">{title}</h3>
|
|
11
|
+
<div class="text-sm text-[var(--color-text-secondary)]">
|
|
12
|
+
<slot />
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Steps component for step-by-step guides
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<div class="not-prose my-6 space-y-4 border-l-2 border-[var(--color-border)] pl-6 ml-2">
|
|
6
|
+
<slot />
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<style>
|
|
10
|
+
div :global(> *) {
|
|
11
|
+
position: relative;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
div :global(> *::before) {
|
|
15
|
+
content: counter(step);
|
|
16
|
+
counter-increment: step;
|
|
17
|
+
position: absolute;
|
|
18
|
+
left: -2.25rem;
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
width: 1.5rem;
|
|
23
|
+
height: 1.5rem;
|
|
24
|
+
font-size: 0.75rem;
|
|
25
|
+
font-weight: 600;
|
|
26
|
+
background-color: var(--color-bg);
|
|
27
|
+
border: 2px solid var(--color-border);
|
|
28
|
+
border-radius: 9999px;
|
|
29
|
+
}
|
|
30
|
+
</style>
|
|
31
|
+
|
|
32
|
+
<script>
|
|
33
|
+
// Initialize step counter
|
|
34
|
+
document.querySelectorAll('.not-prose.space-y-4').forEach(container => {
|
|
35
|
+
container.style.counterReset = 'step';
|
|
36
|
+
});
|
|
37
|
+
</script>
|